The Fundamental Trading Concept This indicator is built on one of the most profound observations in technical analysis: markets don't move in straight lines, but rather in a series of swings or waves. W.D. Gann recognized that these swings create a rhythm in the market, and by understanding this rhythm, traders can anticipate future price movements. The Core Philosophy The indicator is looking for what I call "exhaustion patterns" - specific market structures that suggest a trend is running out of steam and may be ready to reverse. Think of it like watching ocean waves: before a big wave crashes and reverses, there are subtle signs in the water's movement that experienced surfers can read. This indicator is trying to read those same subtle signs in price action. Understanding Market Structure Through Pivots What is a Pivot? A pivot is a turning point in the market - a place where price stopped going in one direction and started going in another. Imagine you're hiking up a mountain: A Pivot High is like reaching a peak where you have to start going down A Pivot Low is like reaching a valley where you have to start going up The indicator identifies these using a simple 3-bar pattern: Pivot High: Yesterday's high is higher than both today's high AND the day before yesterday's high Pivot Low: Yesterday's low is lower than both today's low AND the day before yesterday's low The Genius of the Trading Logic For Buy Signals - The Detailed Process Let me walk you through exactly what happens when the indicator looks for a buy signal: New Pivot High Forms: The market makes a pivot high (a local peak) Historical Context Check: The indicator asks, "Is this pivot high lower than the absolute highest high the market has ever made?" This filters out pivots at all-time highs, where buying might be dangerous. Looking for the Previous Higher High: The indicator then looks backward in time for a previous pivot high that was HIGHER than the current one. This is crucial - it's looking for evidence that the market is making LOWER HIGHS, a classic sign of a downtrend. Time Filter: It only considers previous highs that are at least 'GannDays' (default 4) bars ago. This ensures we're looking at meaningful swings, not just minor fluctuations. Finding the Lowest Point: Between that previous higher high and now, the indicator finds the LOWEST pivot low. This represents the maximum pessimism or selling pressure in that period. Final Validation: If the time from that lowest low to the current pivot high is at least 'GannDays', it's saying: "The market fell from a higher high, made a significant low, and has now rallied back up for at least 4 days to make this current high. This could be the start of a new uptrend." Why This Works - The Psychology What's happening here is profound. The pattern identifies: Failed Continuation: The market couldn't make a higher high (bearish) Significant Selling: A meaningful low was established (maximum fear) Recovery Rally: Price has now rallied for several days off that low Potential Reversal: This could be early buyers stepping in, sensing value For Sell Signals - The Mirror Image The sell signal logic is the exact opposite: New Pivot Low Forms: But it's not the absolute lowest low ever Finds Previous Lower Low: Looks for evidence of HIGHER LOWS (uptrend characteristic) Identifies Highest High: Finds the peak of optimism between the previous low and now Time Validation: Ensures enough time has passed for this to be meaningful The psychology here is identifying when an uptrend might be exhausting - higher lows (bullish) but now failing to continue, with a significant high (maximum optimism) followed by several days of decline. The Brilliant Use of Time Gann was obsessed with time, and this indicator honors that. The 'GannDays' parameter (default 4) serves multiple purposes: Filters Noise: Ensures we're looking at meaningful swings, not random fluctuations Respects Market Rhythm: Many markets have natural 3-5 day rhythms Allows Patterns to Develop: Gives the market time to "prove" its intention Signal Management and Discipline Signal Expiration (SigExpir = 50) This is brilliant risk management. A signal doesn't last forever - if the market doesn't trigger your entry within 50 bars, the signal expires. This prevents you from acting on stale information. Entry Mechanism Buy Signal: Places a stop order 1 tick ABOVE the pivot high Sell Signal: Places a stop order 1 tick BELOW the pivot low This ensures you only enter if the market actually breaks through these levels, confirming the reversal. The Array Storage System The indicator stores up to 1500 historical pivots. Why? Pattern Recognition: Needs historical context to identify patterns Adaptive Analysis: Can look far back for significant levels Market Memory: Respects that old pivots can still be relevant Real-World Trading Application Imagine you're trading the S&P 500: Market has been falling for weeks A pivot low forms at 4,200 (the fear point) Market rallies for 5 days to 4,250 Pivot high forms at 4,250 Indicator looks back, sees previous high was 4,300 Calculates: "We fell from 4,300 to 4,200, now rallied to 4,250" Signal generated: Buy stop at 4,251 If the market continues up and breaks 4,251, you're in. If it fails and falls back, you're protected. Why This Approach is Powerful Objective Rules: No subjective interpretation needed Risk Definition: You know exactly where to enter and can place stops Patience: Waits for high-probability setups Time-Tested: Based on Gann's century-old principles Adaptable: Works across different markets and timeframes The Hidden Wisdom The deepest insight here is that the indicator is NOT trying to catch exact tops and bottoms. Instead, it's waiting for the market to show its hand - to prove that a reversal might be underway - before suggesting action. This is the difference between prediction and reaction, and it's why this approach has survived for decades. The indicator essentially says: "Don't try to catch a falling knife. Wait for the knife to hit the ground, bounce, and show it might be safe to pick up." python code Code: import numpy as np import pandas as pd from typing import Tuple, List, Optional import matplotlib.pyplot as plt from dataclasses import dataclass from datetime import datetime @dataclass class PivotPoint: """Class to store pivot point information""" bar: int price: float pivot_type: str # 'high' or 'low' class GannPivotIndicator: """ GANN PIVOT INDICATOR Python implementation of the Gann Pivot Trading System This indicator identifies pivot highs and lows, then looks for specific patterns based on Gann's principles to generate buy and sell signals. """ def __init__(self, gann_days: int = 4, sig_expir: int = 50, max_array_size: int = 1500): """ Initialize the Gann Pivot Indicator Parameters: ----------- gann_days : int Minimum number of days required between pivots for a valid signal sig_expir : int Signal expiration - how many bars a signal remains valid max_array_size : int Maximum number of pivots to store in memory """ self.gann_days = gann_days self.sig_expir = sig_expir self.max_array_size = max_array_size # Initialize variables self.market_high = -np.inf self.market_low = np.inf # Pivot storage self.pivot_highs: List[PivotPoint] = [] self.pivot_lows: List[PivotPoint] = [] # Signal tracking self.temp_buy_price = 0 self.temp_buy_bar = 0 self.temp_buy_on = False self.temp_sell_price = 0 self.temp_sell_bar = 0 self.temp_sell_on = False # Track current bar self.current_bar = 0 # Store signals for plotting self.buy_signals = [] self.sell_signals = [] def _add_pivot_high(self, bar: int, price: float): """Add a pivot high to the list with bounds checking""" pivot = PivotPoint(bar=bar, price=price, pivot_type='high') if len(self.pivot_highs) >= self.max_array_size: # Remove oldest pivot to make room self.pivot_highs.pop(0) self.pivot_highs.append(pivot) def _add_pivot_low(self, bar: int, price: float): """Add a pivot low to the list with bounds checking""" pivot = PivotPoint(bar=bar, price=price, pivot_type='low') if len(self.pivot_lows) >= self.max_array_size: # Remove oldest pivot to make room self.pivot_lows.pop(0) self.pivot_lows.append(pivot) def _identify_pivots(self, high: pd.Series, low: pd.Series, idx: int): """ Identify pivot highs and lows using 3-bar patterns Pivot High: High[1] > High[0] and High[1] > High[2] Pivot Low: Low[1] < Low[0] and Low[1] < Low[2] """ if idx >= 2: # Need at least 3 bars # Check for Pivot High if high.iloc[idx] < high.iloc[idx-1] and high.iloc[idx-1] > high.iloc[idx-2]: self._add_pivot_high(self.current_bar - 1, high.iloc[idx-1]) # Check for Pivot Low if low.iloc[idx] > low.iloc[idx-1] and low.iloc[idx-1] < low.iloc[idx-2]: self._add_pivot_low(self.current_bar - 1, low.iloc[idx-1]) def _check_buy_setup(self): """ Check for buy setup conditions Logic: 1. Find a new pivot high that's lower than market's all-time high 2. Look back for a previous higher pivot high (at least gann_days ago) 3. Find the lowest pivot low between them 4. If conditions met, set buy signal """ if len(self.pivot_highs) < 2 or len(self.pivot_lows) < 1: return latest_pivot_high = self.pivot_highs[-1] # Check if this is a new pivot high and not at market high if (latest_pivot_high.bar == self.current_bar - 1 and latest_pivot_high.price < self.market_high): # Find previous higher pivot high prev_higher_bar = None for i in range(len(self.pivot_highs) - 2, -1, -1): if self.pivot_highs.price > latest_pivot_high.price: prev_higher_bar = self.pivot_highs.bar break if prev_higher_bar is None: return # Check if enough bars between pivots bar_count = latest_pivot_high.bar - prev_higher_bar if bar_count < self.gann_days: return # Find lowest pivot low between the two pivot highs lowest_low = None lowest_low_bar = None for pivot_low in self.pivot_lows: if pivot_low.bar > prev_higher_bar and pivot_low.bar < latest_pivot_high.bar: if lowest_low is None or pivot_low.price < lowest_low: lowest_low = pivot_low.price lowest_low_bar = pivot_low.bar if lowest_low_bar is None: return # Check if enough days from low to current pivot high low_to_high_days = latest_pivot_high.bar - lowest_low_bar if (low_to_high_days >= self.gann_days and latest_pivot_high.bar > self.temp_buy_bar): self.temp_buy_price = latest_pivot_high.price self.temp_buy_bar = latest_pivot_high.bar self.temp_buy_on = True def _check_sell_setup(self): """ Check for sell setup conditions Logic: 1. Find a new pivot low that's higher than market's all-time low 2. Look back for a previous lower pivot low (at least gann_days ago) 3. Find the highest pivot high between them 4. If conditions met, set sell signal """ if len(self.pivot_lows) < 2 or len(self.pivot_highs) < 1: return latest_pivot_low = self.pivot_lows[-1] # Check if this is a new pivot low and not at market low if (latest_pivot_low.bar == self.current_bar - 1 and latest_pivot_low.price > self.market_low): # Find previous lower pivot low prev_lower_bar = None bar_count = 0 for i in range(len(self.pivot_lows) - 2, -1, -1): if self.pivot_lows.price < latest_pivot_low.price: prev_lower_bar = self.pivot_lows.bar bar_count = latest_pivot_low.bar - prev_lower_bar break if prev_lower_bar is None or bar_count < self.gann_days: return # Find highest pivot high between the two pivot lows highest_high = None highest_high_bar = None for pivot_high in self.pivot_highs: if pivot_high.bar > prev_lower_bar and pivot_high.bar < latest_pivot_low.bar: if highest_high is None or pivot_high.price > highest_high: highest_high = pivot_high.price highest_high_bar = pivot_high.bar if highest_high_bar is None: return # Check if enough days from high to current pivot low high_to_low_days = latest_pivot_low.bar - highest_high_bar if (high_to_low_days >= self.gann_days and latest_pivot_low.bar > self.temp_sell_bar): self.temp_sell_price = latest_pivot_low.price self.temp_sell_bar = latest_pivot_low.bar self.temp_sell_on = True def process_bar(self, high: float, low: float, high_series: pd.Series, low_series: pd.Series, idx: int) -> Tuple[Optional[float], Optional[float]]: """ Process a single bar of data Returns: -------- Tuple of (buy_signal_price, sell_signal_price) or (None, None) if no signals """ self.current_bar = idx + 1 # CurrentBar in EasyLanguage is 1-based # Update market highs and lows if high > self.market_high: self.market_high = high if low < self.market_low: self.market_low = low # Identify pivots self._identify_pivots(high_series, low_series, idx) # Check for new setups self._check_buy_setup() self._check_sell_setup() buy_signal = None sell_signal = None # Check if buy signal is active and valid if self.temp_buy_on: if (high < self.temp_buy_price and self.current_bar - self.temp_buy_bar <= self.sig_expir): buy_signal = self.temp_buy_price + 0.01 # Adding 1 tick else: self.temp_buy_on = False # Check if sell signal is active and valid if self.temp_sell_on: if (low > self.temp_sell_price and self.current_bar - self.temp_sell_bar <= self.sig_expir): sell_signal = self.temp_sell_price - 0.01 # Subtracting 1 tick else: self.temp_sell_on = False return buy_signal, sell_signal def analyze(self, data: pd.DataFrame) -> pd.DataFrame: """ Analyze a DataFrame with OHLC data Parameters: ----------- data : pd.DataFrame DataFrame with columns: 'high', 'low' (and optionally 'open', 'close') Returns: -------- pd.DataFrame with additional columns: 'buy_signal', 'sell_signal' """ # Reset state self.__init__(self.gann_days, self.sig_expir, self.max_array_size) # Prepare result dataframe result = data.copy() result['buy_signal'] = np.nan result['sell_signal'] = np.nan # Process each bar for i in range(len(data)): buy_signal, sell_signal = self.process_bar( data['high'].iloc, data['low'].iloc, data['high'], data['low'], i ) if buy_signal is not None: result.loc[result.index, 'buy_signal'] = buy_signal self.buy_signals.append((result.index, buy_signal)) if sell_signal is not None: result.loc[result.index, 'sell_signal'] = sell_signal self.sell_signals.append((result.index, sell_signal)) return result def plot_signals(self, data: pd.DataFrame, result: pd.DataFrame, figsize: Tuple[int, int] = (15, 8)): """ Plot the price data with buy and sell signals """ fig, ax = plt.subplots(figsize=figsize) # Plot candlestick chart (simplified) for idx in range(len(data)): high = data['high'].iloc[idx] low = data['low'].iloc[idx] # Plot high-low bars ax.plot([idx, idx], [low, high], 'k-', linewidth=0.5) # Add open/close if available if 'open' in data.columns and 'close' in data.columns: open_price = data['open'].iloc[idx] close_price = data['close'].iloc[idx] color = 'g' if close_price > open_price else 'r' ax.plot([idx-0.3, idx+0.3], [open_price, open_price], color, linewidth=1) ax.plot([idx-0.3, idx+0.3], [close_price, close_price], color, linewidth=1) # Plot buy signals buy_x = [] buy_y = [] for idx, signal in enumerate(result['buy_signal']): if not pd.isna(signal): buy_x.append(idx) buy_y.append(signal) ax.scatter(buy_x, buy_y, color='green', marker='^', s=100, label='Buy Signal', zorder=5) # Plot sell signals sell_x = [] sell_y = [] for idx, signal in enumerate(result['sell_signal']): if not pd.isna(signal): sell_x.append(idx) sell_y.append(signal) ax.scatter(sell_x, sell_y, color='red', marker='v', s=100, label='Sell Signal', zorder=5) # Mark pivot points for pivot in self.pivot_highs[-20:]: # Show last 20 pivot highs if pivot.bar < len(data): ax.plot(pivot.bar-1, pivot.price, 'b.', markersize=8) for pivot in self.pivot_lows[-20:]: # Show last 20 pivot lows if pivot.bar < len(data): ax.plot(pivot.bar-1, pivot.price, 'r.', markersize=8) ax.set_xlabel('Bar Number') ax.set_ylabel('Price') ax.set_title(f'Gann Pivot Indicator (GannDays={self.gann_days}, SigExpir={self.sig_expir})') ax.legend() ax.grid(True, alpha=0.3) plt.tight_layout() plt.show() def get_pivot_statistics(self) -> dict: """ Get statistics about the pivots and signals generated """ return { 'total_pivot_highs': len(self.pivot_highs), 'total_pivot_lows': len(self.pivot_lows), 'total_buy_signals': len(self.buy_signals), 'total_sell_signals': len(self.sell_signals), 'market_high': self.market_high, 'market_low': self.market_low } # Example usage if __name__ == "__main__": # Create sample data np.random.seed(42) dates = pd.date_range('2023-01-01', periods=500, freq='D') # Generate synthetic price data trend = np.cumsum(np.random.randn(500) * 0.5) noise = np.random.randn(500) * 2 base_price = 100 + trend + noise data = pd.DataFrame({ 'date': dates, 'open': base_price + np.random.randn(500) * 0.5, 'high': base_price + np.abs(np.random.randn(500)) * 1.5, 'low': base_price - np.abs(np.random.randn(500)) * 1.5, 'close': base_price + np.random.randn(500) * 0.3 }) # Ensure high is highest and low is lowest data['high'] = data[['open', 'high', 'close']].max(axis=1) data['low'] = data[['open', 'low', 'close']].min(axis=1) # Initialize and run the indicator indicator = GannPivotIndicator(gann_days=4, sig_expir=50) result = indicator.analyze(data) # Plot results indicator.plot_signals(data, result) # Print statistics stats = indicator.get_pivot_statistics() print("\nGann Pivot Indicator Statistics:") for key, value in stats.items(): print(f"{key}: {value}") # Show signals print("\nBuy Signals:") for date, price in indicator.buy_signals[:5]: # Show first 5 print(f" {date}: ${price:.2f}") print("\nSell Signals:") for date, price in indicator.sell_signals[:5]: # Show first 5 print(f" {date}: ${price:.2f}") W.D. Gann's Swing Trading Methods Historical Background W.D. Gann originally developed a 3-day swing chart method documented in his June 1933 course "The Mechanical Method And Trend Indicator For Trading Grains." However, in early 1955 prior to his death, Gann made handwritten alterations noting: "Use two-day charts and rules better than three day," with his signature. Core Principles of Gann Swing Charts 1. Three-Day Swing Chart Rules (Original Method) The trend line has to move in 3 up days consecutively, to qualify for an uptrend. Gann Rules for Swing Trading - Bramesh's Technical Analysis +2 The basic rules are: Upswing: A correction in an uptrend, leading to a swing low, needs at least 3 consecutive days of lower lows Gann Swing Trading | Trade2Win Downswing: Three consecutive days of higher highs are needed to form a swing high in a downtrend According to W.D. Gann, 3 consecutive down candles must occur before the line "swings" to the bottom of the 3rd down candle 2. Two-Day Swing Chart Rules (Revised Method) The swing direction can change to up only if the market makes two consecutive higher highs, and the swing direction can change to down only if the market makes two consecutive lower lows. This method is more responsive to market changes. 3. One-Day Swing Charts While less common, 1-day swing charts follow similar principles but react to every new high or low, making them extremely sensitive to price movements. Key Swing Trading Rules Bar Types and Classifications Up Day: This occurs when the asset's price reaches a higher high and a higher low than the previous day Down Day: A down day is when the asset's price forms a lower high and a lower low compared to the prior day Inside Day: This is when the asset's price shows a lower high and a higher low than the previous day Outside Day: An outside day occurs when the asset's price breaks the range of the previous day by forming both a higher high and a lower low Gann Swing Charts | FXTM Trading Applications Trend Identification Uptrend: Trend Change from Down to Up. Prices must take out the nearest peak, and the trend was previously down Downtrend: Trend Change from Up to Down. Prices must take out the nearest valley and the trend was previously up Support and Resistance Support: Support Is the Valley of the Previous Clearly Defined Swing Resistance: Resistance Is the Peak of the Previous Clearly Defined Swing Modified Modern Approach Some traders have modified the traditional Gann 3 period swing chart by relaxing the requirement for there to be 3 "consecutive" days in order for a Gann swing to be formed, using instead "3 lower lows (without there being a new higher high in-between)." Practical Trading Rules According to Gann Rules for Swing Trading Gann Rules for Swing Trading - Bramesh's Technical Analysis: If a high price of any week is achieved on Friday, then expect higher prices in the next week Gann Rules for Swing Trading - Bramesh's Technical Analysis If the low price of the week is achieved on Friday, it indicates lower price next week Gann Rules for Swing Trading - Bramesh's Technical Analysis In a highly up trending market, weekly lows are generally achieved on Tuesday Gann Rules for Swing Trading - Bramesh's Technical Analysis If a price crosses the high of the last 3 days at least by 0.5% of Stock Value, it is a buy signal on the fourth day Gann Rules for Swing Trading - Bramesh's Technical Analysis If the low of the last 3 days is broken by 0.5% of Stock Value then it is a sell signal on the fourth day Gann Rules for Swing Trading - Bramesh's Technical Analysis Key Benefits The main goal of using Gann's charts is to remove the "noise" from the price charts, which is not only unnecessary but also obscures the interpretation of the price action FXTMChartalert The choice between 1, 2, or 3-day swing charts depends on your trading style: 1-day: Most sensitive, suitable for very short-term trading 2-day: Gann's preferred method later in life, good balance 3-day: Original method, filters out more noise, better for position trading
No backtest ? Might try on TradingView tomorrow. Likely not going to be profitable because... We can beat a race but we can't beat the races. Chart reading is the most hazardous.
INDICATOR Code: //@version=5 indicator("Gann Pivot Indicator", overlay=true, max_bars_back=1500) // ===================================================================== // GANN PIVOT INDICATOR // // Pine Script Translation for TradingView // // Use in conjunction with the Gann Pivot Trading System // ===================================================================== // Input Parameters gannDays = input.int(4, "Gann Days", minval=1, tooltip="Minimum number of days required between pivots for a valid signal") sigExpir = input.int(50, "Signal Expiration", minval=1, tooltip="How many bars a signal remains valid") showPivots = input.bool(true, "Show Pivot Points", tooltip="Display pivot highs and lows on chart") showBuyZone = input.bool(true, "Show Buy Signal Zone", tooltip="Highlight buy signal zones") showSellZone = input.bool(true, "Show Sell Signal Zone", tooltip="Highlight sell signal zones") // Colors buyColor = input.color(color.green, "Buy Signal Color") sellColor = input.color(color.red, "Sell Signal Color") pivotHighColor = input.color(color.blue, "Pivot High Color") pivotLowColor = input.color(color.orange, "Pivot Low Color") // Variables for tracking pivots var float marketHigh = -999999.0 var float marketLow = 999999.0 // Arrays to store pivot data (Pine Script handles array bounds automatically) var pivotHighBars = array.new_int(1500) var pivotHighPrices = array.new_float(1500) var pivotLowBars = array.new_int(1500) var pivotLowPrices = array.new_float(1500) // Pivot counters var int phiCount = 0 var int ploCount = 0 // Signal variables var float tempBuyPrice = 0.0 var int tempBuyBar = 0 var bool tempBuyOn = false var float tempSellPrice = 0.0 var int tempSellBar = 0 var bool tempSellOn = false // Track market extremes if high > marketHigh marketHigh := high if low < marketLow marketLow := low // Function to add pivot high with array management addPivotHigh(bar, price) => if phiCount < 1500 array.set(pivotHighBars, phiCount, bar) array.set(pivotHighPrices, phiCount, price) phiCount := phiCount + 1 else // Shift array elements for i = 0 to 1498 array.set(pivotHighBars, i, array.get(pivotHighBars, i + 1)) array.set(pivotHighPrices, i, array.get(pivotHighPrices, i + 1)) array.set(pivotHighBars, 1499, bar) array.set(pivotHighPrices, 1499, price) // Function to add pivot low with array management addPivotLow(bar, price) => if ploCount < 1500 array.set(pivotLowBars, ploCount, bar) array.set(pivotLowPrices, ploCount, price) ploCount := ploCount + 1 else // Shift array elements for i = 0 to 1498 array.set(pivotLowBars, i, array.get(pivotLowBars, i + 1)) array.set(pivotLowPrices, i, array.get(pivotLowPrices, i + 1)) array.set(pivotLowBars, 1499, bar) array.set(pivotLowPrices, 1499, price) // Identify Pivot Highs (3-bar pattern) isPivotHigh = high < high[1] and high[1] > high[2] if isPivotHigh addPivotHigh(bar_index - 1, high[1]) if showPivots label.new(bar_index - 1, high[1], "PH", color=pivotHighColor, style=label.style_circle, size=size.tiny) // Identify Pivot Lows (3-bar pattern) isPivotLow = low > low[1] and low[1] < low[2] if isPivotLow addPivotLow(bar_index - 1, low[1]) if showPivots label.new(bar_index - 1, low[1], "PL", color=pivotLowColor, style=label.style_circle, size=size.tiny) // Check for Buy Setup checkBuySetup() => if phiCount > 1 and ploCount > 0 latestPHBar = array.get(pivotHighBars, phiCount - 1) latestPHPrice = array.get(pivotHighPrices, phiCount - 1) // Check if this is a recent pivot high and not at market high if latestPHBar == bar_index - 1 and latestPHPrice < marketHigh prevHigherBar = -1 // Find previous higher pivot high for i = phiCount - 2 to 0 if array.get(pivotHighPrices, i) > latestPHPrice prevHigherBar := array.get(pivotHighBars, i) break if prevHigherBar != -1 barCount = latestPHBar - prevHigherBar if barCount >= gannDays // Find lowest pivot low between the two highs lowestPLo = 999999.0 lowestPLoBar = -1 for i = 0 to ploCount - 1 ploBar = array.get(pivotLowBars, i) if ploBar > prevHigherBar and ploBar < latestPHBar ploPrice = array.get(pivotLowPrices, i) if ploPrice < lowestPLo lowestPLo := ploPrice lowestPLoBar := ploBar if lowestPLoBar != -1 lo2HiDays = latestPHBar - lowestPLoBar if lo2HiDays >= gannDays and latestPHBar > tempBuyBar tempBuyPrice := latestPHPrice tempBuyBar := latestPHBar tempBuyOn := true // Check for Sell Setup checkSellSetup() => if ploCount > 1 and phiCount > 0 latestPLBar = array.get(pivotLowBars, ploCount - 1) latestPLPrice = array.get(pivotLowPrices, ploCount - 1) // Check if this is a recent pivot low and not at market low if latestPLBar == bar_index - 1 and latestPLPrice > marketLow prevLowerBar = -1 barCount = 0 // Find previous lower pivot low for i = ploCount - 2 to 0 if array.get(pivotLowPrices, i) < latestPLPrice prevLowerBar := array.get(pivotLowBars, i) barCount := latestPLBar - prevLowerBar break if prevLowerBar != -1 and barCount >= gannDays // Find highest pivot high between the two lows highestPHi = -999999.0 highestPHiBar = -1 for i = 0 to phiCount - 1 phiBar = array.get(pivotHighBars, i) if phiBar > prevLowerBar and phiBar < latestPLBar phiPrice = array.get(pivotHighPrices, i) if phiPrice > highestPHi highestPHi := phiPrice highestPHiBar := phiBar if highestPHiBar != -1 hi2LoDays = latestPLBar - highestPHiBar if hi2LoDays >= gannDays and latestPLBar > tempSellBar tempSellPrice := latestPLPrice tempSellBar := latestPLBar tempSellOn := true // Run setup checks checkBuySetup() checkSellSetup() // Handle Buy Signals buySignalPrice = float(na) if tempBuyOn if high < tempBuyPrice and bar_index - tempBuyBar <= sigExpir buySignalPrice := tempBuyPrice + syminfo.mintick else tempBuyOn := false // Handle Sell Signals sellSignalPrice = float(na) if tempSellOn if low > tempSellPrice and bar_index - tempSellBar <= sigExpir sellSignalPrice := tempSellPrice - syminfo.mintick else tempSellOn := false // Plot buy and sell signals plotshape(not na(buySignalPrice), title="Buy Signal", location=location.absolute, style=shape.triangleup, size=size.small, color=buyColor, text="BUY") plotshape(not na(sellSignalPrice), title="Sell Signal", location=location.absolute, style=shape.triangledown, size=size.small, color=sellColor, text="SELL") // Draw signal zones (optional visual enhancement) if showBuyZone and not na(buySignalPrice) box.new(bar_index - 1, buySignalPrice * 0.999, bar_index + 5, buySignalPrice * 1.001, border_color=buyColor, bgcolor=color.new(buyColor, 90)) if showSellZone and not na(sellSignalPrice) box.new(bar_index - 1, sellSignalPrice * 0.999, bar_index + 5, sellSignalPrice * 1.001, border_color=sellColor, bgcolor=color.new(sellColor, 90)) // Plot signal levels as lines plot(tempBuyOn ? tempBuyPrice + syminfo.mintick : na, title="Buy Level", color=buyColor, style=plot.style_linebr, linewidth=2) plot(tempSellOn ? tempSellPrice - syminfo.mintick : na, title="Sell Level", color=sellColor, style=plot.style_linebr, linewidth=2) // Add alerts alertcondition(not na(buySignalPrice), title="Gann Buy Signal", message="Gann Pivot Buy Signal at {{close}}") alertcondition(not na(sellSignalPrice), title="Gann Sell Signal", message="Gann Pivot Sell Signal at {{close}}") // Display information panel var table infoTable = table.new(position.top_right, 2, 5, bgcolor=color.new(color.black, 80)) if barstate.islast table.cell(infoTable, 0, 0, "Gann Days:", text_color=color.white, text_size=size.small) table.cell(infoTable, 1, 0, str.tostring(gannDays), text_color=color.white, text_size=size.small) table.cell(infoTable, 0, 1, "Signal Expiry:", text_color=color.white, text_size=size.small) table.cell(infoTable, 1, 1, str.tostring(sigExpir), text_color=color.white, text_size=size.small) table.cell(infoTable, 0, 2, "Pivot Highs:", text_color=color.white, text_size=size.small) table.cell(infoTable, 1, 2, str.tostring(phiCount), text_color=color.white, text_size=size.small) table.cell(infoTable, 0, 3, "Pivot Lows:", text_color=color.white, text_size=size.small) table.cell(infoTable, 1, 3, str.tostring(ploCount), text_color=color.white, text_size=size.small) table.cell(infoTable, 0, 4, "Active:", text_color=color.white, text_size=size.small) status = tempBuyOn ? "BUY" : tempSellOn ? "SELL" : "NONE" statusColor = tempBuyOn ? buyColor : tempSellOn ? sellColor : color.gray table.cell(infoTable, 1, 4, status, text_color=statusColor, text_size=size.small) SYSTEM Code: //@version=5 strategy("Gann Pivot Trading System", overlay=true, initial_capital=100000, default_qty_type=strategy.percent_of_equity, default_qty_value=10, commission_type=strategy.commission.percent, commission_value=0.05, max_bars_back=1500) // ===================================================================== // GANN PIVOT TRADING SYSTEM // Based on C.T.C.R.'s GANN PIVOT INDICATOR by Roger D. Rines // Complete Trading System with Risk Management // ===================================================================== // Strategy Inputs group1 = "Gann Settings" gannDays = input.int(4, "Gann Days", minval=1, group=group1, tooltip="Minimum days between pivots for valid signal") sigExpir = input.int(50, "Signal Expiration", minval=1, group=group1, tooltip="How many bars a signal remains valid") group2 = "Position Sizing" useFixedSize = input.bool(true, "Use Fixed Position Size", group=group2) fixedContracts = input.int(1, "Fixed Contracts", minval=1, group=group2) riskPercent = input.float(1.0, "Risk % Per Trade", minval=0.1, maxval=10, step=0.1, group=group2) group3 = "Risk Management" useStopLoss = input.bool(true, "Use Stop Loss", group=group3) stopLossType = input.string("Fixed Points", "Stop Loss Type", options=["Fixed Points", "ATR", "Pivot Based"], group=group3) stopLossPoints = input.float(2.0, "Stop Loss Points", minval=0.1, group=group3) stopLossATR = input.float(2.0, "Stop Loss ATR Multiplier", minval=0.1, group=group3) useProfitTarget = input.bool(true, "Use Profit Target", group=group3) profitTargetType = input.string("Fixed Points", "Profit Target Type", options=["Fixed Points", "ATR", "Risk Multiple"], group=group3) profitTargetPoints = input.float(4.0, "Profit Target Points", minval=0.1, group=group3) profitTargetATR = input.float(3.0, "Profit Target ATR Multiplier", minval=0.1, group=group3) profitTargetMultiple = input.float(2.0, "Risk/Reward Ratio", minval=0.5, maxval=10, step=0.5, group=group3) useTrailingStop = input.bool(false, "Use Trailing Stop", group=group3) trailingType = input.string("Fixed Points", "Trailing Stop Type", options=["Fixed Points", "ATR", "Pivot Based"], group=group3) trailingPoints = input.float(2.0, "Trailing Stop Points", minval=0.1, group=group3) trailingATR = input.float(1.5, "Trailing Stop ATR Multiplier", minval=0.1, group=group3) group4 = "Time Filters" useTimeFilter = input.bool(false, "Use Time Filter", group=group4) startHour = input.int(9, "Start Hour", minval=0, maxval=23, group=group4) startMinute = input.int(30, "Start Minute", minval=0, maxval=59, group=group4) endHour = input.int(15, "End Hour", minval=0, maxval=23, group=group4) endMinute = input.int(0, "End Minute", minval=0, maxval=59, group=group4) exitHour = input.int(15, "Exit Hour", minval=0, maxval=23, group=group4) exitMinute = input.int(45, "Exit Minute", minval=0, maxval=59, group=group4) group5 = "Additional Filters" useMAFilter = input.bool(false, "Use MA Filter", group=group5) maLength = input.int(50, "MA Length", minval=1, group=group5) minVolume = input.int(100000, "Minimum Volume", minval=0, group=group5) maxDailyTrades = input.int(5, "Max Daily Trades", minval=1, group=group5) group6 = "Visual Settings" showPivots = input.bool(true, "Show Pivot Points", group=group6) showSignals = input.bool(true, "Show Entry Signals", group=group6) showLevels = input.bool(true, "Show Stop/Target Levels", group=group6) showDashboard = input.bool(true, "Show Info Dashboard", group=group6) // Colors buyColor = input.color(color.green, "Buy Signal Color", group=group6) sellColor = input.color(color.red, "Sell Signal Color", group=group6) pivotHighColor = input.color(color.blue, "Pivot High Color", group=group6) pivotLowColor = input.color(color.orange, "Pivot Low Color", group=group6) // Variables var float marketHigh = -999999.0 var float marketLow = 999999.0 // Arrays for pivot storage var pivotHighBars = array.new_int(1500) var pivotHighPrices = array.new_float(1500) var pivotLowBars = array.new_int(1500) var pivotLowPrices = array.new_float(1500) // Pivot counters var int phiCount = 0 var int ploCount = 0 // Signal variables var float tempBuyPrice = 0.0 var int tempBuyBar = 0 var bool tempBuyOn = false var float tempSellPrice = 0.0 var int tempSellBar = 0 var bool tempSellOn = false // Trading variables var int tradesToday = 0 var float entryPrice = 0.0 var float stopPrice = 0.0 var float targetPrice = 0.0 var float trailingStopPrice = 0.0 var bool isTrailing = false // Performance tracking var int consecutiveWins = 0 var int consecutiveLosses = 0 var float maxDrawdown = 0.0 var float equity = 100000.0 var float peakEquity = 100000.0 // Calculate indicators atrValue = ta.atr(14) maValue = useMAFilter ? ta.sma(close, maLength) : close // Time functions isWithinTime() => if useTimeFilter currentTime = hour * 100 + minute startTime = startHour * 100 + startMinute endTime = endHour * 100 + endMinute currentTime >= startTime and currentTime <= endTime else true shouldExitTime() => if useTimeFilter currentTime = hour * 100 + minute exitTime = exitHour * 100 + exitMinute currentTime >= exitTime else false // Reset daily counters if dayofmonth != dayofmonth[1] tradesToday := 0 // Track market extremes if high > marketHigh marketHigh := high if low < marketLow marketLow := low // Function to add pivot high addPivotHigh(bar, price) => if phiCount < 1500 array.set(pivotHighBars, phiCount, bar) array.set(pivotHighPrices, phiCount, price) phiCount := phiCount + 1 else for i = 0 to 1498 array.set(pivotHighBars, i, array.get(pivotHighBars, i + 1)) array.set(pivotHighPrices, i, array.get(pivotHighPrices, i + 1)) array.set(pivotHighBars, 1499, bar) array.set(pivotHighPrices, 1499, price) // Function to add pivot low addPivotLow(bar, price) => if ploCount < 1500 array.set(pivotLowBars, ploCount, bar) array.set(pivotLowPrices, ploCount, price) ploCount := ploCount + 1 else for i = 0 to 1498 array.set(pivotLowBars, i, array.get(pivotLowBars, i + 1)) array.set(pivotLowPrices, i, array.get(pivotLowPrices, i + 1)) array.set(pivotLowBars, 1499, bar) array.set(pivotLowPrices, 1499, price) // Identify Pivot Highs isPivotHigh = high < high[1] and high[1] > high[2] if isPivotHigh addPivotHigh(bar_index - 1, high[1]) if showPivots label.new(bar_index - 1, high[1], "PH", color=pivotHighColor, style=label.style_circle, size=size.tiny) // Identify Pivot Lows isPivotLow = low > low[1] and low[1] < low[2] if isPivotLow addPivotLow(bar_index - 1, low[1]) if showPivots label.new(bar_index - 1, low[1], "PL", color=pivotLowColor, style=label.style_circle, size=size.tiny) // Check for Buy Setup checkBuySetup() => if phiCount > 1 and ploCount > 0 latestPHBar = array.get(pivotHighBars, phiCount - 1) latestPHPrice = array.get(pivotHighPrices, phiCount - 1) if latestPHBar == bar_index - 1 and latestPHPrice < marketHigh prevHigherBar = -1 for i = phiCount - 2 to 0 if array.get(pivotHighPrices, i) > latestPHPrice prevHigherBar := array.get(pivotHighBars, i) break if prevHigherBar != -1 barCount = latestPHBar - prevHigherBar if barCount >= gannDays lowestPLo = 999999.0 lowestPLoBar = -1 for i = 0 to ploCount - 1 ploBar = array.get(pivotLowBars, i) if ploBar > prevHigherBar and ploBar < latestPHBar ploPrice = array.get(pivotLowPrices, i) if ploPrice < lowestPLo lowestPLo := ploPrice lowestPLoBar := ploBar if lowestPLoBar != -1 lo2HiDays = latestPHBar - lowestPLoBar if lo2HiDays >= gannDays and latestPHBar > tempBuyBar tempBuyPrice := latestPHPrice tempBuyBar := latestPHBar tempBuyOn := true // Check for Sell Setup checkSellSetup() => if ploCount > 1 and phiCount > 0 latestPLBar = array.get(pivotLowBars, ploCount - 1) latestPLPrice = array.get(pivotLowPrices, ploCount - 1) if latestPLBar == bar_index - 1 and latestPLPrice > marketLow prevLowerBar = -1 barCount = 0 for i = ploCount - 2 to 0 if array.get(pivotLowPrices, i) < latestPLPrice prevLowerBar := array.get(pivotLowBars, i) barCount := latestPLBar - prevLowerBar break if prevLowerBar != -1 and barCount >= gannDays highestPHi = -999999.0 highestPHiBar = -1 for i = 0 to phiCount - 1 phiBar = array.get(pivotHighBars, i) if phiBar > prevLowerBar and phiBar < latestPLBar phiPrice = array.get(pivotHighPrices, i) if phiPrice > highestPHi highestPHi := phiPrice highestPHiBar := phiBar if highestPHiBar != -1 hi2LoDays = latestPLBar - highestPHiBar if hi2LoDays >= gannDays and latestPLBar > tempSellBar tempSellPrice := latestPLPrice tempSellBar := latestPLBar tempSellOn := true // Run setup checks checkBuySetup() checkSellSetup() // Position sizing calculation calculatePositionSize() => if useFixedSize fixedContracts else riskAmount = strategy.equity * (riskPercent / 100) stopDistance = stopLossType == "Fixed Points" ? stopLossPoints : stopLossType == "ATR" ? atrValue * stopLossATR : close * 0.02 // Default 2% for pivot based contracts = math.floor(riskAmount / (stopDistance * syminfo.pointvalue)) math.max(1, contracts) // Entry Conditions canTrade = isWithinTime() and tradesToday < maxDailyTrades and volume >= minVolume // Long Entry longCondition = strategy.position_size == 0 and canTrade and tempBuyOn and high < tempBuyPrice and bar_index - tempBuyBar <= sigExpir and (not useMAFilter or close > maValue) if longCondition posSize = calculatePositionSize() strategy.entry("Long", strategy.long, qty=posSize, stop=tempBuyPrice + syminfo.mintick) tempBuyOn := false tradesToday += 1 // Short Entry shortCondition = strategy.position_size == 0 and canTrade and tempSellOn and low > tempSellPrice and bar_index - tempSellBar <= sigExpir and (not useMAFilter or close < maValue) if shortCondition posSize = calculatePositionSize() strategy.entry("Short", strategy.short, qty=posSize, stop=tempSellPrice - syminfo.mintick) tempSellOn := false tradesToday += 1 // Position Management if strategy.position_size != 0 isLong = strategy.position_size > 0 isShort = strategy.position_size < 0 // Initialize on entry if strategy.position_size != strategy.position_size[1] entryPrice := strategy.position_avg_price isTrailing := false // Calculate stop loss if useStopLoss if stopLossType == "Fixed Points" stopPrice := isLong ? entryPrice - stopLossPoints : entryPrice + stopLossPoints else if stopLossType == "ATR" stopPrice := isLong ? entryPrice - (atrValue * stopLossATR) : entryPrice + (atrValue * stopLossATR) else if stopLossType == "Pivot Based" if isLong and ploCount > 0 stopPrice := array.get(pivotLowPrices, ploCount - 1) - syminfo.mintick else if isShort and phiCount > 0 stopPrice := array.get(pivotHighPrices, phiCount - 1) + syminfo.mintick // Calculate profit target if useProfitTarget riskAmount = math.abs(entryPrice - stopPrice) if profitTargetType == "Fixed Points" targetPrice := isLong ? entryPrice + profitTargetPoints : entryPrice - profitTargetPoints else if profitTargetType == "ATR" targetPrice := isLong ? entryPrice + (atrValue * profitTargetATR) : entryPrice - (atrValue * profitTargetATR) else if profitTargetType == "Risk Multiple" targetPrice := isLong ? entryPrice + (riskAmount * profitTargetMultiple) : entryPrice - (riskAmount * profitTargetMultiple) // Trailing Stop Logic if useTrailingStop newTrailStop = 0.0 if trailingType == "Fixed Points" newTrailStop := isLong ? high - trailingPoints : low + trailingPoints else if trailingType == "ATR" newTrailStop := isLong ? high - (atrValue * trailingATR) : low + (atrValue * trailingATR) else if trailingType == "Pivot Based" if isLong and ploCount > 0 newTrailStop := array.get(pivotLowPrices, ploCount - 1) - syminfo.mintick else if isShort and phiCount > 0 newTrailStop := array.get(pivotHighPrices, phiCount - 1) + syminfo.mintick if isLong and newTrailStop > stopPrice stopPrice := newTrailStop isTrailing := true else if isShort and newTrailStop < stopPrice stopPrice := newTrailStop isTrailing := true // Execute stops and targets if useStopLoss strategy.exit("Exit", stop=stopPrice, limit=useProfitTarget ? targetPrice : na) else if useProfitTarget strategy.exit("Exit", limit=targetPrice) // Time-based exit if shouldExitTime() and strategy.position_size != 0 strategy.close_all("EOD Exit") // Plotting // Signal levels plot(showSignals and tempBuyOn ? tempBuyPrice + syminfo.mintick : na, "Buy Level", buyColor, 2, plot.style_linebr) plot(showSignals and tempSellOn ? tempSellPrice - syminfo.mintick : na, "Sell Level", sellColor, 2, plot.style_linebr) // Stop and target levels plot(showLevels and strategy.position_size != 0 ? stopPrice : na, "Stop Level", color.red, 1, plot.style_linebr) plot(showLevels and strategy.position_size != 0 and useProfitTarget ? targetPrice : na, "Target Level", color.green, 1, plot.style_linebr) // Entry signals plotshape(showSignals and longCondition, "Long Signal", shape.triangleup, location.belowbar, buyColor, size=size.small) plotshape(showSignals and shortCondition, "Short Signal", shape.triangledown, location.abovebar, sellColor, size=size.small) // Dashboard if showDashboard var table dashboard = table.new(position.top_right, 2, 10, bgcolor=color.new(color.black, 80)) if barstate.islast // Headers table.cell(dashboard, 0, 0, "Metric", text_color=color.white, bgcolor=color.new(color.blue, 50)) table.cell(dashboard, 1, 0, "Value", text_color=color.white, bgcolor=color.new(color.blue, 50)) // Settings table.cell(dashboard, 0, 1, "Gann Days", text_color=color.white) table.cell(dashboard, 1, 1, str.tostring(gannDays), text_color=color.white) table.cell(dashboard, 0, 2, "Signal Expiry", text_color=color.white) table.cell(dashboard, 1, 2, str.tostring(sigExpir), text_color=color.white) // Pivot counts table.cell(dashboard, 0, 3, "Pivot Highs", text_color=color.white) table.cell(dashboard, 1, 3, str.tostring(phiCount), text_color=color.white) table.cell(dashboard, 0, 4, "Pivot Lows", text_color=color.white) table.cell(dashboard, 1, 4, str.tostring(ploCount), text_color=color.white) // Trading status table.cell(dashboard, 0, 5, "Position", text_color=color.white) posText = strategy.position_size > 0 ? "LONG" : strategy.position_size < 0 ? "SHORT" : "FLAT" posColor = strategy.position_size > 0 ? buyColor : strategy.position_size < 0 ? sellColor : color.gray table.cell(dashboard, 1, 5, posText, text_color=posColor) // Signals table.cell(dashboard, 0, 6, "Active Signal", text_color=color.white) sigText = tempBuyOn ? "BUY" : tempSellOn ? "SELL" : "NONE" sigColor = tempBuyOn ? buyColor : tempSellOn ? sellColor : color.gray table.cell(dashboard, 1, 6, sigText, text_color=sigColor) // Daily trades table.cell(dashboard, 0, 7, "Trades Today", text_color=color.white) table.cell(dashboard, 1, 7, str.tostring(tradesToday) + "/" + str.tostring(maxDailyTrades), text_color=color.white) // Performance table.cell(dashboard, 0, 8, "Net Profit", text_color=color.white) netProfit = strategy.netprofit profitColor = netProfit >= 0 ? color.green : color.red table.cell(dashboard, 1, 8, "$" + str.tostring(netProfit, "#,###.##"), text_color=profitColor) // Win rate table.cell(dashboard, 0, 9, "Win Rate", text_color=color.white) winRate = strategy.wintrades / math.max(strategy.closedtrades, 1) * 100 table.cell(dashboard, 1, 9, str.tostring(winRate, "#.#") + "%", text_color=color.white) // Alerts alertcondition(longCondition, "Long Entry", "Gann Pivot Long Signal at {{close}}") alertcondition(shortCondition, "Short Entry", "Gann Pivot Short Signal at {{close}}") alertcondition(strategy.position_size != 0 and useStopLoss and ((strategy.position_size > 0 and low <= stopPrice) or (strategy.position_size < 0 and high >= stopPrice)), "Stop Hit", "Stop Loss Hit at {{close}}") alertcondition(strategy.position_size != 0 and useProfitTarget and ((strategy.position_size > 0 and high >= targetPrice) or (strategy.position_size < 0 and low <= targetPrice)), "Target Hit", "Profit Target Hit at {{close}}") Key Features of the TradingView Version: 1. Visual Enhancements Pivot points marked with small circles (PH/PL) Buy/sell signals shown as triangles with text Optional signal zones (colored boxes) Signal levels drawn as lines 2. User-Friendly Inputs Adjustable Gann Days and Signal Expiration Toggle pivot display on/off Customizable colors for all elements Show/hide signal zones 3. Information Panel Real-time display of indicator statistics Shows current active signals Pivot count tracking 4. Alerts Built-in alert conditions for buy/sell signals Can be connected to TradingView's alert system 5. Pine Script Optimizations Uses Pine Script v5 features Efficient array handling Proper NA value handling Clean, maintainable code structure How to Use: Add to TradingView: Copy the code and paste into Pine Editor Save and Add to Chart: Click "Add to Chart" Configure Settings: Adjust parameters in the indicator settings Set Alerts: Right-click on chart → "Add Alert" → Choose Gann conditions Interpret Signals: Green triangles = Buy signals Red triangles = Sell signals Blue dots = Pivot highs Orange dots = Pivot lows The indicator faithfully reproduces the original logic while taking advantage of TradingView's visual capabilities for a better trading experience.
A CODE block would help with readability and compilability (doesn't python need indentation?). https://www.elitetrader.com/et/help/bb-codes
yep it's a bitch alright .... nothing works as good as watching cnn and msnbc i guess for your financial advice. maybe some suze orman or motley fools?
The Gann indicator is based on historical price and time data. Price moves the Gann indicator, not the other way around. Risk control in more important than the indicator.