Files
zhutoutoutousan 5b44e14211 Update
2026-01-05 05:37:33 +01:00

213 lines
8.6 KiB
Python

"""
Martingale Strategy Simulation
Analyzes the statistical properties and risk of martingale strategies
"""
import numpy as np
import matplotlib
matplotlib.use('Agg') # Use non-interactive backend
import matplotlib.pyplot as plt
from scipy import stats
import pandas as pd
class MartingaleSimulator:
def __init__(self, initial_balance=10000, base_lot=0.01, win_prob=0.5,
win_amount=10, loss_amount=10, max_losses=10):
self.initial_balance = initial_balance
self.base_lot = base_lot
self.win_prob = win_prob
self.win_amount = win_amount
self.loss_amount = loss_amount
self.max_losses = max_losses
def calculate_position_size(self, consecutive_losses):
"""Calculate position size after n consecutive losses"""
return self.base_lot * (2 ** consecutive_losses)
def calculate_required_capital(self, consecutive_losses):
"""Calculate total capital needed after n losses"""
return self.base_lot * (2 ** (consecutive_losses + 1) - 1)
def simulate_trade_sequence(self, num_trades=1000):
"""Simulate a sequence of trades"""
balance = self.initial_balance
consecutive_losses = 0
trades = []
ruin = False
for i in range(num_trades):
if balance <= 0:
ruin = True
break
# Calculate position size
position_size = self.calculate_position_size(consecutive_losses)
required_capital = self.calculate_required_capital(consecutive_losses)
# Check if we have enough capital
if required_capital > balance:
ruin = True
break
# Simulate trade outcome
is_win = np.random.random() < self.win_prob
if is_win:
# Win: recover all previous losses
profit = position_size * self.win_amount
balance += profit
consecutive_losses = 0
outcome = 'Win'
else:
# Loss: add to consecutive losses
loss = position_size * self.loss_amount
balance -= loss
consecutive_losses += 1
outcome = 'Loss'
trades.append({
'trade': i + 1,
'balance': balance,
'position_size': position_size,
'consecutive_losses': consecutive_losses,
'outcome': outcome,
'profit': profit if is_win else -loss
})
return pd.DataFrame(trades), ruin
def monte_carlo_analysis(self, num_simulations=1000, num_trades=100):
"""Run Monte Carlo simulation"""
results = []
ruin_count = 0
for sim in range(num_simulations):
trades_df, ruin = self.simulate_trade_sequence(num_trades)
if ruin:
ruin_count += 1
final_balance = 0
else:
final_balance = trades_df['balance'].iloc[-1]
results.append({
'simulation': sim,
'final_balance': final_balance,
'ruin': ruin,
'total_trades': len(trades_df),
'max_consecutive_losses': trades_df['consecutive_losses'].max() if len(trades_df) > 0 else 0
})
return pd.DataFrame(results), ruin_count / num_simulations
def plot_simulation_results(self, num_simulations=100):
"""Plot simulation results"""
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
# Run simulations
results_df, ruin_prob = self.monte_carlo_analysis(num_simulations)
# Plot 1: Final Balance Distribution
axes[0, 0].hist(results_df['final_balance'], bins=50, edgecolor='black')
axes[0, 0].axvline(self.initial_balance, color='red', linestyle='--',
label=f'Initial Balance: ${self.initial_balance:,.0f}')
axes[0, 0].set_xlabel('Final Balance ($)')
axes[0, 0].set_ylabel('Frequency')
axes[0, 0].set_title(f'Final Balance Distribution\nRuin Probability: {ruin_prob:.2%}')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)
# Plot 2: Ruin Probability vs Consecutive Losses
max_losses_range = range(1, self.max_losses + 1)
ruin_probs = []
for n in max_losses_range:
required = self.calculate_required_capital(n)
ruin_probs.append(1.0 if required > self.initial_balance else 0.0)
axes[0, 1].plot(max_losses_range, ruin_probs, 'ro-', linewidth=2, markersize=8)
axes[0, 1].set_xlabel('Consecutive Losses')
axes[0, 1].set_ylabel('Ruin Probability')
axes[0, 1].set_title('Ruin Probability vs Consecutive Losses')
axes[0, 1].grid(True, alpha=0.3)
axes[0, 1].set_ylim([-0.1, 1.1])
# Plot 3: Position Size Growth
losses_range = range(0, self.max_losses + 1)
position_sizes = [self.calculate_position_size(n) for n in losses_range]
required_capital = [self.calculate_required_capital(n) for n in losses_range]
ax3_twin = axes[1, 0].twinx()
line1 = axes[1, 0].plot(losses_range, position_sizes, 'b-o',
label='Position Size', linewidth=2)
line2 = ax3_twin.plot(losses_range, required_capital, 'r-s',
label='Required Capital', linewidth=2)
axes[1, 0].set_xlabel('Consecutive Losses')
axes[1, 0].set_ylabel('Position Size (Lots)', color='b')
ax3_twin.set_ylabel('Required Capital ($)', color='r')
axes[1, 0].set_title('Position Size and Capital Requirements')
axes[1, 0].grid(True, alpha=0.3)
# Combine legends
lines = line1 + line2
labels = [l.get_label() for l in lines]
axes[1, 0].legend(lines, labels, loc='upper left')
# Plot 4: Sample Trade Sequence
sample_trades, _ = self.simulate_trade_sequence(50)
axes[1, 1].plot(sample_trades['trade'], sample_trades['balance'],
'g-', linewidth=2, label='Balance')
axes[1, 1].axhline(self.initial_balance, color='red', linestyle='--',
label='Initial Balance')
axes[1, 1].set_xlabel('Trade Number')
axes[1, 1].set_ylabel('Balance ($)')
axes[1, 1].set_title('Sample Trade Sequence (50 trades)')
axes[1, 1].legend()
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
return fig
if __name__ == "__main__":
# Create simulator
simulator = MartingaleSimulator(
initial_balance=10000,
base_lot=0.01,
win_prob=0.5,
win_amount=10,
loss_amount=10,
max_losses=10
)
# Run analysis
print("Running Martingale Simulation...")
results_df, ruin_prob = simulator.monte_carlo_analysis(num_simulations=1000, num_trades=100)
print(f"\n=== Martingale Strategy Analysis ===")
print(f"Initial Balance: ${simulator.initial_balance:,.2f}")
print(f"Win Probability: {simulator.win_prob:.1%}")
print(f"\nMonte Carlo Results (1000 simulations):")
print(f"Ruin Probability: {ruin_prob:.2%}")
print(f"Mean Final Balance: ${results_df['final_balance'].mean():,.2f}")
print(f"Median Final Balance: ${results_df['final_balance'].median():,.2f}")
print(f"Std Dev Final Balance: ${results_df['final_balance'].std():,.2f}")
print(f"Max Final Balance: ${results_df['final_balance'].max():,.2f}")
print(f"Min Final Balance: ${results_df['final_balance'].min():,.2f}")
# Calculate statistics
profitable_sims = (results_df['final_balance'] > simulator.initial_balance).sum()
print(f"\nProfitable Simulations: {profitable_sims}/{len(results_df)} ({profitable_sims/len(results_df):.1%})")
print(f"Average Max Consecutive Losses: {results_df['max_consecutive_losses'].mean():.2f}")
# Generate plots
import os
# Get the script directory and construct path to figures
script_dir = os.path.dirname(os.path.abspath(__file__))
figures_dir = os.path.join(script_dir, '..', 'figures')
figures_path = os.path.abspath(figures_dir)
os.makedirs(figures_path, exist_ok=True)
fig = simulator.plot_simulation_results(num_simulations=100)
output_path = os.path.join(figures_path, 'martingale_analysis.png')
plt.savefig(output_path, dpi=300, bbox_inches='tight')
print(f"\nFigure saved to {output_path}")
plt.close()