#!/usr/bin/env python3 """ Pyramiding Optimizer v2 - Fixed equity allocation Tests different pyramiding levels (1-5) with the ML v11.2 Long strategy Key fix: Each pyramid entry uses FULL position sizing (like TradingView does), not divided by max pyramid level. """ # All 44 trades with entry/exit and P&L % # Trades are already grouped in sequences in the CSV - a new sequence starts when # the previous one closes (exit) and a new entry happens # The backtest had pyramiding=2, meaning max 2 concurrent positions # But looking at the actual trades, many sequences had 4-5 entries # This suggests TradingView's pyramiding worked differently # Let's look at this from a PURE position sizing perspective: # With pyramiding=N, each signal opens a position worth X% of initial equity # Multiple positions can be open simultaneously # From the CSV, I'll extract ALL 44 trades and simulate different pyramid limits ALL_TRADES = [ # Each tuple: (trade_num, entry_price, exit_price, pnl_percent) (1, 127.26, 123.69, -2.80), (2, 139.45, 141.85, 1.72), (3, 139.77, 141.85, 1.49), (4, 144.09, 140.05, -2.80), (5, 130.99, 133.09, 1.60), (6, 133.64, 135.78, 1.60), (7, 133.90, 135.67, 1.32), (8, 133.15, 135.67, 1.89), (9, 139.24, 141.41, 1.56), (10, 139.12, 141.41, 1.65), (11, 131.41, 133.79, 1.81), (12, 131.95, 133.79, 1.39), (13, 132.82, 129.07, -2.82), (14, 132.76, 129.07, -2.78), (15, 130.96, 133.06, 1.60), (16, 125.93, 127.95, 1.60), (17, 126.71, 128.74, 1.60), (18, 128.70, 130.24, 1.20), (19, 127.67, 130.24, 2.01), (20, 123.58, 125.56, 1.60), (21, 126.22, 128.11, 1.50), (22, 125.96, 128.11, 1.71), (23, 126.06, 128.21, 1.71), (24, 126.32, 128.21, 1.50), (25, 126.10, 121.98, -3.27), (26, 124.89, 121.98, -2.33), (27, 122.07, 124.04, 1.61), (28, 122.09, 124.04, 1.60), (29, 123.03, 125.00, 1.60), (30, 123.34, 125.69, 1.91), (31, 124.08, 125.69, 1.30), (32, 123.82, 125.81, 1.61), (33, 124.72, 126.72, 1.60), (34, 124.90, 126.91, 1.61), (35, 124.92, 126.91, 1.59), (36, 128.81, 130.88, 1.61), (37, 131.33, 133.34, 1.53), (38, 131.14, 133.34, 1.68), (39, 134.23, 136.57, 1.74), (40, 134.60, 136.57, 1.46), (41, 138.24, 140.46, 1.61), (42, 138.89, 141.12, 1.61), (43, 140.00, 136.08, -2.80), (44, 135.09, 137.26, 1.61), ] def simulate_sequential(trades, max_pyramid, starting_capital=1400, leverage=10, commission_rate=0.0005): """ Simulate trading with pyramiding where each trade processes sequentially. With compounding - each trade uses current equity. For simplicity, we process trades in order: - With pyramid=1: Only every Nth trade executes (based on groups) - With pyramid=N: N trades from each group execute """ equity = starting_capital peak_equity = starting_capital max_drawdown_pct = 0 max_drawdown_usd = 0 total_trades = 0 wins = 0 losses = 0 total_commission = 0 # First let's identify trade groups (sequences that close together) # Looking at the exit prices, trades in same sequence have same exit groups = [] current_group = [] last_exit = None for trade_num, entry, exit_p, pnl in trades: if last_exit is not None and exit_p != last_exit: if current_group: groups.append(current_group) current_group = [] current_group.append((trade_num, entry, exit_p, pnl)) last_exit = exit_p if current_group: groups.append(current_group) # Process each group with pyramid limit for group in groups: # Limit to max_pyramid trades trades_to_execute = group[:max_pyramid] group_pnl = 0 group_commission = 0 for trade_num, entry, exit_p, pnl_pct in trades_to_execute: # Each pyramid entry gets full equity / pyramid_count for this group position_equity = equity / len(trades_to_execute) notional = position_equity * leverage gross_pnl = notional * (pnl_pct / 100) commission = notional * commission_rate * 2 net_pnl = gross_pnl - commission group_pnl += net_pnl group_commission += commission total_trades += 1 if net_pnl > 0: wins += 1 else: losses += 1 total_commission += group_commission equity += group_pnl # Track drawdown if equity > peak_equity: peak_equity = equity current_dd_usd = peak_equity - equity current_dd_pct = (current_dd_usd / peak_equity) * 100 if peak_equity > 0 else 0 if current_dd_pct > max_drawdown_pct: max_drawdown_pct = current_dd_pct max_drawdown_usd = current_dd_usd total_profit = equity - starting_capital roi = (total_profit / starting_capital) * 100 win_rate = (wins / total_trades) * 100 if total_trades > 0 else 0 risk_reward = roi / max_drawdown_pct if max_drawdown_pct > 0 else float('inf') return { 'max_pyramid': max_pyramid, 'final_equity': equity, 'total_profit': total_profit, 'roi': roi, 'total_trades': total_trades, 'wins': wins, 'losses': losses, 'win_rate': win_rate, 'max_drawdown_pct': max_drawdown_pct, 'max_drawdown_usd': max_drawdown_usd, 'total_commission': total_commission, 'risk_reward': risk_reward, 'groups': len(groups) } def simulate_full_compounding(trades, max_pyramid, starting_capital=1400, leverage=10, commission_rate=0.0005): """ More realistic simulation: Each pyramid entry uses current equity, and all entries compound individually. """ equity = starting_capital peak_equity = starting_capital max_drawdown_pct = 0 max_drawdown_usd = 0 total_trades = 0 wins = 0 losses = 0 total_commission = 0 # Group trades by exit price (same exit = same close time) groups = [] current_group = [] last_exit = None for trade_num, entry, exit_p, pnl in trades: if last_exit is not None and exit_p != last_exit: if current_group: groups.append(current_group) current_group = [] current_group.append((trade_num, entry, exit_p, pnl)) last_exit = exit_p if current_group: groups.append(current_group) for group in groups: # Limit to max_pyramid trades_to_execute = group[:max_pyramid] n_trades = len(trades_to_execute) # Calculate combined P&L for the group # Each trade uses (equity / n_trades) as base group_pnl = 0 for trade_num, entry, exit_p, pnl_pct in trades_to_execute: position_base = equity / n_trades notional = position_base * leverage gross_pnl = notional * (pnl_pct / 100) commission = notional * commission_rate * 2 net_pnl = gross_pnl - commission group_pnl += net_pnl total_commission += commission total_trades += 1 if pnl_pct > 0: wins += 1 else: losses += 1 equity += group_pnl if equity > peak_equity: peak_equity = equity dd_usd = peak_equity - equity dd_pct = (dd_usd / peak_equity) * 100 if peak_equity > 0 else 0 if dd_pct > max_drawdown_pct: max_drawdown_pct = dd_pct max_drawdown_usd = dd_usd total_profit = equity - starting_capital roi = (total_profit / starting_capital) * 100 win_rate = (wins / total_trades) * 100 if total_trades > 0 else 0 risk_reward = roi / max_drawdown_pct if max_drawdown_pct > 0 else float('inf') return { 'max_pyramid': max_pyramid, 'final_equity': equity, 'total_profit': total_profit, 'roi': roi, 'total_trades': total_trades, 'wins': wins, 'losses': losses, 'win_rate': win_rate, 'max_drawdown_pct': max_drawdown_pct, 'max_drawdown_usd': max_drawdown_usd, 'total_commission': total_commission, 'risk_reward': risk_reward } def main(): print("=" * 90) print("šŸŽÆ PYRAMIDING OPTIMIZER v2 - ML v11.2 LONG STRATEGY") print("=" * 90) print(f"Starting Capital: $1,400 | Leverage: 10x | Commission: 0.05%/trade") print("=" * 90) # Test different pyramid levels print("\n" + "=" * 90) print("šŸ“Š PYRAMIDING COMPARISON (Split Position Sizing)") print("=" * 90) print("Position sizing: Equity split equally among pyramid entries") print("-" * 90) print(f"{'Pyramid':<10} {'Final $':<15} {'Profit':<15} {'ROI':<12} {'Trades':<10} {'Win%':<10} {'Max DD%':<12} {'R/R':<10}") print("-" * 90) results = [] for p in range(1, 6): r = simulate_full_compounding(ALL_TRADES, p) results.append(r) print(f"{p:<10} ${r['final_equity']:>12,.2f} ${r['total_profit']:>12,.2f} {r['roi']:>10.1f}% {r['total_trades']:>8} {r['win_rate']:>8.1f}% {r['max_drawdown_pct']:>10.1f}% {r['risk_reward']:>8.2f}") print("-" * 90) # Analysis print("\n" + "=" * 90) print("šŸ” DETAILED ANALYSIS") print("=" * 90) # Find best scenarios best_roi_idx = max(range(len(results)), key=lambda i: results[i]['roi']) best_rr_idx = max(range(len(results)), key=lambda i: results[i]['risk_reward']) lowest_dd_idx = min(range(len(results)), key=lambda i: results[i]['max_drawdown_pct']) print(f"\nšŸ’° HIGHEST PROFIT: Pyramid = {results[best_roi_idx]['max_pyramid']}") r = results[best_roi_idx] print(f" ${r['total_profit']:,.2f} profit ({r['roi']:.1f}% ROI)") print(f" Max Drawdown: {r['max_drawdown_pct']:.1f}%") print(f"\nšŸ“ˆ BEST RISK/REWARD: Pyramid = {results[best_rr_idx]['max_pyramid']}") r = results[best_rr_idx] print(f" Risk/Reward: {r['risk_reward']:.2f}") print(f" Profit: ${r['total_profit']:,.2f} ({r['roi']:.1f}%)") print(f"\nšŸ›”ļø LOWEST DRAWDOWN: Pyramid = {results[lowest_dd_idx]['max_pyramid']}") r = results[lowest_dd_idx] print(f" Max Drawdown: {r['max_drawdown_pct']:.1f}% (${r['max_drawdown_usd']:,.2f})") print(f" Profit: ${r['total_profit']:,.2f}") # Marginal analysis print("\n" + "=" * 90) print("šŸ“‰ MARGINAL RETURNS (Each Additional Pyramid Level)") print("=" * 90) for i in range(1, len(results)): prev = results[i-1] curr = results[i] profit_change = curr['total_profit'] - prev['total_profit'] profit_change_pct = (profit_change / prev['total_profit'] * 100) if prev['total_profit'] > 0 else float('inf') dd_change = curr['max_drawdown_pct'] - prev['max_drawdown_pct'] if profit_change > 0: emoji = "āœ…" if dd_change < 5 else "āš ļø" else: emoji = "āŒ" print(f"{emoji} Pyramid {prev['max_pyramid']}→{curr['max_pyramid']}: " f"Profit {'+'if profit_change>0 else ''}{profit_change_pct:.0f}% (${profit_change:+,.0f}) | " f"DD {'+'if dd_change>0 else ''}{dd_change:.1f}%") # SWEET SPOT RECOMMENDATION print("\n" + "=" * 90) print("šŸ’Ž SWEET SPOT RECOMMENDATION") print("=" * 90) # Calculate efficiency score: profit gain per unit of drawdown increase efficiencies = [] for i in range(len(results)): r = results[i] efficiency = r['roi'] / r['max_drawdown_pct'] if r['max_drawdown_pct'] > 0 else 0 efficiencies.append((i, efficiency, r['roi'], r['max_drawdown_pct'])) # Best efficiency with decent profit best_eff_idx = max(range(len(efficiencies)), key=lambda i: efficiencies[i][1] if results[i]['roi'] > 50 else 0) print(f"\nšŸ† RECOMMENDED: PYRAMIDING = {results[best_eff_idx]['max_pyramid']}") r = results[best_eff_idx] print(f"\n With $1,400 starting capital and 10x leverage:") print(f" ─────────────────────────────────────────────") print(f" Final Equity: ${r['final_equity']:>12,.2f}") print(f" Total Profit: ${r['total_profit']:>12,.2f} ({r['roi']:.1f}% ROI)") print(f" Total Trades: {r['total_trades']}") print(f" Win Rate: {r['win_rate']:.1f}%") print(f" Max Drawdown: {r['max_drawdown_pct']:.1f}% (${r['max_drawdown_usd']:,.2f})") print(f" Risk/Reward: {r['risk_reward']:.2f}") print(f" Commission: ${r['total_commission']:,.2f}") # Show all options summary print("\n" + "=" * 90) print("šŸ“‹ QUICK DECISION MATRIX") print("=" * 90) print(""" ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¬ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā” │ Risk Level │ Pyramid │ Expected │ Max Pain You'll Feel │ ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤""") risk_labels = ["Conservative", "Moderate", "Aggressive", "Very Aggressive", "YOLO"] for i, r in enumerate(results): label = risk_labels[i] if i < len(risk_labels) else "???" max_pain = r['max_drawdown_pct'] expected = r['roi'] print(f" │ {label:<11} │ {r['max_pyramid']:<12} │ +{expected:>6.0f}% ROI │ -{max_pain:>4.1f}% drawdown (${r['max_drawdown_usd']:>7,.0f}) │") print(""" ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”“ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜ """) print("\nāš ļø REALITY CHECK:") print(" • Backtests are OPTIMISTIC - real trading has slippage, emotions, missed entries") print(" • Higher pyramiding = higher variance = bigger swings both ways") print(" • The 'max drawdown' is the worst point - you WILL experience it") print(" • Consider: Can you stomach losing ${:,.0f} before recovering?".format(results[best_eff_idx]['max_drawdown_usd'])) if __name__ == "__main__": main()