backtest-terminal / backend /backtest_bridge.py
algorembrant's picture
Upload 16 files
5cbffcd verified
Raw
History Blame Contribute Delete
6.92 kB
import MetaTrader5 as mt5
import json
import os
from datetime import datetime
import pandas as pd
class BacktestBridge:
"""Bridge to run MQL5 Expert Advisors and extract backtest results"""
def __init__(self):
self.mt5_path = None
def init_mt5(self):
"""Initialize MT5 connection"""
if not mt5.initialize():
print(f"MT5 initialization failed: {mt5.last_error()}")
return False
# Get MT5 terminal path
terminal_info = mt5.terminal_info()
self.mt5_path = terminal_info.path
print(f"MT5 Path: {self.mt5_path}")
return True
def compile_ea(self, mq5_file_path):
"""
Compile MQL5 Expert Advisor
Note: This requires MetaEditor CLI or manual compilation
"""
# Check if file exists
if not os.path.exists(mq5_file_path):
return {'success': False, 'error': 'MQ5 file not found'}
# For now, assume EA is already compiled
# You need to manually compile in MetaEditor or use MetaEditor CLI
ex5_path = mq5_file_path.replace('.mq5', '.ex5')
if not os.path.exists(ex5_path):
return {
'success': False,
'error': 'EX5 file not found. Please compile in MetaEditor first.'
}
return {'success': True, 'ex5_path': ex5_path}
def get_history_deals(self, from_date, to_date):
"""Get historical deals from MT5"""
deals = mt5.history_deals_get(from_date, to_date)
if deals is None:
return []
deals_list = []
for deal in deals:
deals_list.append({
'ticket': deal.ticket,
'order': deal.order,
'time': deal.time,
'type': 'BUY' if deal.type == 0 else 'SELL',
'entry': 'IN' if deal.entry == 0 else 'OUT',
'symbol': deal.symbol,
'volume': deal.volume,
'price': deal.price,
'profit': deal.profit,
'commission': deal.commission,
'swap': deal.swap,
'comment': deal.comment
})
return deals_list
def get_history_orders(self, from_date, to_date):
"""Get historical orders from MT5"""
orders = mt5.history_orders_get(from_date, to_date)
if orders is None:
return []
orders_list = []
for order in orders:
orders_list.append({
'ticket': order.ticket,
'time_setup': order.time_setup,
'time_done': order.time_done,
'type': order.type,
'state': order.state,
'symbol': order.symbol,
'volume_initial': order.volume_initial,
'volume_current': order.volume_current,
'price_open': order.price_open,
'price_current': order.price_current,
'sl': order.sl,
'tp': order.tp,
'comment': order.comment
})
return orders_list
def analyze_backtest_results(self, from_date, to_date):
"""Analyze backtest results and calculate statistics"""
deals = self.get_history_deals(from_date, to_date)
if not deals:
return {'error': 'No deals found in the specified period'}
# Convert to DataFrame for easier analysis
df = pd.DataFrame(deals)
# Calculate statistics
total_trades = len(df[df['entry'] == 'OUT'])
winning_trades = len(df[(df['entry'] == 'OUT') & (df['profit'] > 0)])
losing_trades = len(df[(df['entry'] == 'OUT') & (df['profit'] < 0)])
total_profit = df[df['entry'] == 'OUT']['profit'].sum()
total_commission = df[df['entry'] == 'OUT']['commission'].sum()
total_swap = df[df['entry'] == 'OUT']['swap'].sum()
net_profit = total_profit + total_commission + total_swap
win_rate = (winning_trades / total_trades * 100) if total_trades > 0 else 0
# Calculate max drawdown
df_out = df[df['entry'] == 'OUT'].copy()
df_out['cumulative_profit'] = df_out['profit'].cumsum()
df_out['peak'] = df_out['cumulative_profit'].cummax()
df_out['drawdown'] = df_out['peak'] - df_out['cumulative_profit']
max_drawdown = df_out['drawdown'].max()
# Profit factor
gross_profit = df[(df['entry'] == 'OUT') & (df['profit'] > 0)]['profit'].sum()
gross_loss = abs(df[(df['entry'] == 'OUT') & (df['profit'] < 0)]['profit'].sum())
profit_factor = (gross_profit / gross_loss) if gross_loss > 0 else 0
return {
'total_trades': total_trades,
'winning_trades': winning_trades,
'losing_trades': losing_trades,
'win_rate': round(win_rate, 2),
'total_profit': round(total_profit, 2),
'total_commission': round(total_commission, 2),
'total_swap': round(total_swap, 2),
'net_profit': round(net_profit, 2),
'max_drawdown': round(max_drawdown, 2),
'profit_factor': round(profit_factor, 2),
'gross_profit': round(gross_profit, 2),
'gross_loss': round(gross_loss, 2),
'deals': deals
}
def export_backtest_report(self, from_date, to_date, output_file='backtest_report.json'):
"""Export backtest results to JSON file"""
results = self.analyze_backtest_results(from_date, to_date)
with open(output_file, 'w') as f:
json.dump(results, f, indent=2, default=str)
print(f"Backtest report exported to {output_file}")
return results
# Example usage
if __name__ == "__main__":
bridge = BacktestBridge()
if bridge.init_mt5():
# Define backtest period
from_date = datetime(2024, 1, 1)
to_date = datetime.now()
# Analyze results
results = bridge.analyze_backtest_results(from_date, to_date)
print("\n=== BACKTEST RESULTS ===")
print(f"Total Trades: {results.get('total_trades', 0)}")
print(f"Win Rate: {results.get('win_rate', 0)}%")
print(f"Net Profit: ${results.get('net_profit', 0)}")
print(f"Max Drawdown: ${results.get('max_drawdown', 0)}")
print(f"Profit Factor: {results.get('profit_factor', 0)}")
# Export to JSON
bridge.export_backtest_report(from_date, to_date)
mt5.shutdown()