Gap Trading Strategy
import pandas as pd, numpy as np
class GapTradingStrategy:
@staticmethod
def detect_gaps(df: pd.DataFrame, min_gap_atr: float = 0.5) -> list[dict]:
atr = (df["high"] - df["low"]).rolling(14).mean()
gaps = []
for i in range(1, len(df)):
gap = df.iloc[i]["open"] - df.iloc[i-1]["close"]
if abs(gap) > min_gap_atr * atr.iloc[i]:
filled = False
if gap > 0: # Gap up
filled = (df.iloc[i:min(i+20, len(df))]["low"].min() <= df.iloc[i-1]["close"])
else: # Gap down
filled = (df.iloc[i:min(i+20, len(df))]["high"].max() >= df.iloc[i-1]["close"])
gaps.append({
"time": df.index[i], "gap_pips": round(gap * 10000, 1),
"direction": "up" if gap > 0 else "down",
"gap_atr": round(abs(gap) / atr.iloc[i], 2),
"filled_within_20_bars": filled,
})
return gaps
@staticmethod
def gap_fill_statistics(df: pd.DataFrame) -> dict:
gaps = GapTradingStrategy.detect_gaps(df)
if not gaps: return {"n_gaps": 0}
fill_rate = sum(1 for g in gaps if g["filled_within_20_bars"]) / len(gaps)
up_gaps = [g for g in gaps if g["direction"] == "up"]
down_gaps = [g for g in gaps if g["direction"] == "down"]
return {
"n_gaps": len(gaps),
"fill_rate_pct": round(fill_rate * 100, 1),
"up_gap_fill_rate": round(sum(1 for g in up_gaps if g["filled_within_20_bars"]) / max(len(up_gaps), 1) * 100, 1),
"down_gap_fill_rate": round(sum(1 for g in down_gaps if g["filled_within_20_bars"]) / max(len(down_gaps), 1) * 100, 1),
"avg_gap_size_pips": round(np.mean([abs(g["gap_pips"]) for g in gaps]), 1),
"strategy": "FADE THE GAP" if fill_rate > 0.65 else "GAP AND GO" if fill_rate < 0.40 else "MIXED — use confirmation",
"note": f"Gaps fill {fill_rate*100:.0f}% of the time within 20 bars on this pair",
}
@staticmethod
def sunday_gap_trade(friday_close: float, sunday_open: float, atr: float) -> dict:
gap = sunday_open - friday_close
return {
"strategy": "sunday_gap_fade",
"gap_pips": round(gap * 10000, 1),
"direction": "SELL (fade gap up)" if gap > 0 else "BUY (fade gap down)",
"entry": round(sunday_open, 5),
"target": round(friday_close, 5),
"stop": round(sunday_open + (gap * 0.5 if gap > 0 else gap * 0.5), 5),
"note": "Sunday gaps fill ~70% of the time. Use small size due to wide spreads.",
}
1---2name: gap-trading-strategy3description: Gap trading — opening gaps, gap fill probability, gap-and-go, fade the gap. Use for "gap trading", "opening gap", "gap fill", "gap and go", "fade the gap", "Sunday gap", "weekend gap", "gap statistics", "gap probability", or any gap-based trading. Works with session-profiler.4---56# Gap Trading Strategy78```python9import pandas as pd, numpy as np1011class GapTradingStrategy:1213 @staticmethod14 def detect_gaps(df: pd.DataFrame, min_gap_atr: float = 0.5) -> list[dict]:15 atr = (df["high"] - df["low"]).rolling(14).mean()16 gaps = []17 for i in range(1, len(df)):18 gap = df.iloc[i]["open"] - df.iloc[i-1]["close"]19 if abs(gap) > min_gap_atr * atr.iloc[i]:20 filled = False21 if gap > 0: # Gap up22 filled = (df.iloc[i:min(i+20, len(df))]["low"].min() <= df.iloc[i-1]["close"])23 else: # Gap down24 filled = (df.iloc[i:min(i+20, len(df))]["high"].max() >= df.iloc[i-1]["close"])25 gaps.append({26 "time": df.index[i], "gap_pips": round(gap * 10000, 1),27 "direction": "up" if gap > 0 else "down",28 "gap_atr": round(abs(gap) / atr.iloc[i], 2),29 "filled_within_20_bars": filled,30 })31 return gaps3233 @staticmethod34 def gap_fill_statistics(df: pd.DataFrame) -> dict:35 gaps = GapTradingStrategy.detect_gaps(df)36 if not gaps: return {"n_gaps": 0}37 fill_rate = sum(1 for g in gaps if g["filled_within_20_bars"]) / len(gaps)38 up_gaps = [g for g in gaps if g["direction"] == "up"]39 down_gaps = [g for g in gaps if g["direction"] == "down"]40 return {41 "n_gaps": len(gaps),42 "fill_rate_pct": round(fill_rate * 100, 1),43 "up_gap_fill_rate": round(sum(1 for g in up_gaps if g["filled_within_20_bars"]) / max(len(up_gaps), 1) * 100, 1),44 "down_gap_fill_rate": round(sum(1 for g in down_gaps if g["filled_within_20_bars"]) / max(len(down_gaps), 1) * 100, 1),45 "avg_gap_size_pips": round(np.mean([abs(g["gap_pips"]) for g in gaps]), 1),46 "strategy": "FADE THE GAP" if fill_rate > 0.65 else "GAP AND GO" if fill_rate < 0.40 else "MIXED — use confirmation",47 "note": f"Gaps fill {fill_rate*100:.0f}% of the time within 20 bars on this pair",48 }4950 @staticmethod51 def sunday_gap_trade(friday_close: float, sunday_open: float, atr: float) -> dict:52 gap = sunday_open - friday_close53 return {54 "strategy": "sunday_gap_fade",55 "gap_pips": round(gap * 10000, 1),56 "direction": "SELL (fade gap up)" if gap > 0 else "BUY (fade gap down)",57 "entry": round(sunday_open, 5),58 "target": round(friday_close, 5),59 "stop": round(sunday_open + (gap * 0.5 if gap > 0 else gap * 0.5), 5),60 "note": "Sunday gaps fill ~70% of the time. Use small size due to wide spreads.",61 }62```