diff --git a/results/phase2/kelly_sizing.json b/results/phase2/kelly_sizing.json index ede8b81..01095b0 100644 --- a/results/phase2/kelly_sizing.json +++ b/results/phase2/kelly_sizing.json @@ -1,81 +1,65 @@ { "S7_Tight_GBP_JPY": { - "trades": 98, - "win_rate": 59.2, - "avg_win_pips": 42.8, - "avg_loss_pips": 62.3, - "avg_win_loss_ratio": 0.69, - "kelly_full_pct": 0, - "kelly_half_pct": 0, + "trades": 38, + "win_rate": 71.1, + "avg_win_pips": 39.1, + "avg_loss_pips": 59.0, + "avg_win_loss_ratio": 0.66, + "kelly_full_pct": 27.41, + "kelly_half_pct": 13.7, "monte_carlo_half_kelly": { - "p50_dd": 0, - "p75_dd": 0, - "p95_dd": 0, - "p99_dd": 0, - "ruin_pct": 0 - } - }, - "S9_GBP_USD": { - "trades": 285, - "win_rate": 53.7, - "avg_win_pips": 29.3, - "avg_loss_pips": 41.7, - "avg_win_loss_ratio": 0.7, - "kelly_full_pct": 0, - "kelly_half_pct": 0, - "monte_carlo_half_kelly": { - "p50_dd": 0, - "p75_dd": 0, - "p95_dd": 0, - "p99_dd": 0, - "ruin_pct": 0 - } - }, - "S9_Filtered_GBP_AUD": { - "trades": 66, - "win_rate": 60.6, - "avg_win_pips": 49.5, - "avg_loss_pips": 57.9, - "avg_win_loss_ratio": 0.85, - "kelly_full_pct": 14.53, - "kelly_half_pct": 7.26, - "monte_carlo_half_kelly": { - "p50_dd": 2.5, - "p75_dd": 3.3, - "p95_dd": 4.8, - "p99_dd": 6.3, + "p50_dd": 2.8, + "p75_dd": 3.6, + "p95_dd": 5.4, + "p99_dd": 7.0, "ruin_pct": 0.0 } }, - "S4F_EUR_AUD": { - "trades": 95, - "win_rate": 45.3, - "avg_win_pips": 38.8, - "avg_loss_pips": 28.7, - "avg_win_loss_ratio": 1.35, - "kelly_full_pct": 4.7, - "kelly_half_pct": 2.35, + "S9_Filtered_GBP_AUD": { + "trades": 51, + "win_rate": 60.8, + "avg_win_pips": 53.2, + "avg_loss_pips": 56.5, + "avg_win_loss_ratio": 0.94, + "kelly_full_pct": 19.08, + "kelly_half_pct": 9.54, "monte_carlo_half_kelly": { - "p50_dd": 0.7, - "p75_dd": 1.0, - "p95_dd": 1.5, - "p99_dd": 1.9, + "p50_dd": 2.6, + "p75_dd": 3.5, + "p95_dd": 5.4, + "p99_dd": 7.1, "ruin_pct": 0.0 } }, "S3_GBP_JPY": { - "trades": 150, - "win_rate": 50.7, - "avg_win_pips": 43.5, - "avg_loss_pips": 41.9, - "avg_win_loss_ratio": 1.04, - "kelly_full_pct": 3.11, - "kelly_half_pct": 1.56, + "trades": 116, + "win_rate": 55.2, + "avg_win_pips": 41.9, + "avg_loss_pips": 42.0, + "avg_win_loss_ratio": 1.0, + "kelly_full_pct": 10.18, + "kelly_half_pct": 5.09, + "monte_carlo_half_kelly": { + "p50_dd": 2.1, + "p75_dd": 2.8, + "p95_dd": 4.2, + "p99_dd": 5.3, + "ruin_pct": 0.0 + } + }, + "S8_OB_GBP_USD": { + "trades": 84, + "win_rate": 40.5, + "avg_win_pips": 30.4, + "avg_loss_pips": 14.4, + "avg_win_loss_ratio": 2.11, + "kelly_full_pct": 12.24, + "kelly_half_pct": 6.12, "monte_carlo_half_kelly": { "p50_dd": 0.9, "p75_dd": 1.2, - "p95_dd": 1.9, - "p99_dd": 2.4, + "p95_dd": 1.8, + "p99_dd": 2.3, "ruin_pct": 0.0 } } diff --git a/src/run_kelly_sizing.py b/src/run_kelly_sizing.py index 4cd2e4d..62e1efc 100644 --- a/src/run_kelly_sizing.py +++ b/src/run_kelly_sizing.py @@ -20,24 +20,31 @@ from src.backtester.engine import Backtester # Strategy imports from src.strategies_pkg.s7_liquidity_sweep import S7_Liquidity_Sweep from src.strategies_pkg.s9_london_session import S9_London_Session -from src.strategies_pkg.s4f_ema_ribbon import S4F_EMA_Ribbon from src.strategies_pkg.s3_key_level_breakout import S3_KeyLevel_Breakout +from src.strategies_pkg.s8_order_block import S8_Order_Block PROCESSED_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", "processed") RESULTS_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "results", "phase2") os.makedirs(RESULTS_DIR, exist_ok=True) + +def _s8_tuned(): + s = S8_Order_Block() + s.DISPLACEMENT_ATR = 2.5 + s.TP1_ATR_MULT = 2.0 + s.OB_RETEST_WINDOW = 40 + return s + + CONFIGS = [ - {"name": "S7_Tight", "pair": "GBP_JPY", "tf": "H1", + {"name": "S7_Tight", "pair": "GBP_JPY", "tf": "H1", "htf_tf": "H1", "factory": lambda: S7_Liquidity_Sweep()}, - {"name": "S9", "pair": "GBP_USD", "tf": "H1", - "factory": lambda: S9_London_Session()}, - {"name": "S9_Filtered", "pair": "GBP_AUD", "tf": "H1", + {"name": "S9_Filtered", "pair": "GBP_AUD", "tf": "H1", "htf_tf": "H1", "factory": lambda: S9_London_Session(pair="GBP_AUD", filtered=True)}, - {"name": "S4F", "pair": "EUR_AUD", "tf": "M15", - "factory": lambda: S4F_EMA_Ribbon()}, - {"name": "S3", "pair": "GBP_JPY", "tf": "H1", + {"name": "S3", "pair": "GBP_JPY", "tf": "H1", "htf_tf": "H1", "factory": lambda: S3_KeyLevel_Breakout()}, + {"name": "S8_OB", "pair": "GBP_USD", "tf": "M15", "htf_tf": "H1", + "factory": lambda: _s8_tuned()}, ] N_SIMULATIONS = 1000 @@ -53,14 +60,23 @@ def load_data(pair, tf): return compute_all_indicators(df) -def run_backtest(cfg): +def run_backtest(cfg, data_cache): pair = cfg["pair"] tf = cfg["tf"] - data = load_data(pair, tf) + htf_tf = cfg["htf_tf"] + + cache_key = f"{pair}_{tf}" + if cache_key not in data_cache: + data_cache[cache_key] = load_data(pair, tf) + data = data_cache[cache_key] if data is None: return None - htf_data = data.copy() if tf == "H1" else load_data(pair, "H1") + htf_cache_key = f"{pair}_{htf_tf}" + if htf_cache_key not in data_cache: + data_cache[htf_cache_key] = load_data(pair, htf_tf) + htf_data = data_cache[htf_cache_key] if htf_tf != tf else data + strategy = cfg["factory"]() bt = Backtester(data=data, strategy=strategy, pair=pair, starting_equity=STARTING_EQUITY, htf_data=htf_data) @@ -181,17 +197,19 @@ def main(): print(f"{'='*90}") all_results = {} + data_cache = {} for cfg in CONFIGS: name = cfg["name"] pair = cfg["pair"] + tf = cfg["tf"] print(f"\n{'#'*60}") - print(f"# {name} / {pair}") + print(f"# {name} / {pair} ({tf})") print(f"{'#'*60}") t0 = time.time() - trade_log = run_backtest(cfg) + trade_log = run_backtest(cfg, data_cache) elapsed = time.time() - t0 if trade_log is None or trade_log.empty: