mirror of
https://github.com/silencesdg/mt5_python_ea_suite.git
synced 2026-07-29 03:37:43 +00:00
686 lines
31 KiB
Python
686 lines
31 KiB
Python
"""
|
|
机器学习优化器 (基于随机探索 + 模型预测)
|
|
替代遗传算法,使用三阶段优化策略:
|
|
1. 随机探索阶段
|
|
2. 模型训练阶段
|
|
3. 预测筛选阶段
|
|
"""
|
|
import random
|
|
import numpy as np
|
|
import pandas as pd
|
|
import sys
|
|
import os
|
|
from datetime import datetime
|
|
from tqdm import tqdm
|
|
from sklearn.ensemble import RandomForestRegressor
|
|
from sklearn.model_selection import train_test_split
|
|
from sklearn.metrics import mean_squared_error, r2_score
|
|
import json
|
|
import csv
|
|
import multiprocessing
|
|
from joblib import Parallel, delayed
|
|
|
|
# 固定随机种子,确保可复现
|
|
SEED = 42
|
|
random.seed(SEED)
|
|
np.random.seed(SEED)
|
|
|
|
# Add project root to path
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
# Core components for backtesting
|
|
from core.data_providers import BacktestDataProvider
|
|
from core.risk import RiskController
|
|
from execution.dynamic_weights import DynamicWeightManager
|
|
from config import (
|
|
SYMBOL, TIMEFRAME, OPTIMIZER_COUNT, OPTIMIZER_START_DATE, OPTIMIZER_END_DATE,
|
|
USE_DATE_RANGE, BACKTEST_CONFIG, INITIAL_CAPITAL, SIGNAL_THRESHOLDS, DEFAULT_WEIGHTS,
|
|
RISK_CONFIG, GENETIC_OPTIMIZER_CONFIG, MARKET_STATE_CONFIG, TREND_INDICATOR_WEIGHTS,
|
|
TREND_THRESHOLDS, CONFIDENCE_THRESHOLDS
|
|
)
|
|
from core.utils import get_rates, initialize, shutdown
|
|
|
|
# Import all strategy classes
|
|
from strategies.ma_cross import MACrossStrategy
|
|
from strategies.rsi import RSIStrategy
|
|
from strategies.bollinger import BollingerStrategy
|
|
from strategies.mean_reversion import MeanReversionStrategy
|
|
from strategies.momentum_breakout import MomentumBreakoutStrategy
|
|
from strategies.macd import MACDStrategy
|
|
from strategies.kdj import KDJStrategy
|
|
from strategies.turtle import TurtleStrategy
|
|
from strategies.daily_breakout import DailyBreakoutStrategy
|
|
from strategies.wave_theory import WaveTheoryStrategy
|
|
|
|
# --- Global Data (Loaded once) ---
|
|
df_historical_data = None
|
|
|
|
# --- Parameter Definition (Same as GA optimizer) ---
|
|
# Define the search space for strategy parameters and weights
|
|
PARAMETER_DEFINITIONS = [
|
|
# MACrossStrategy parameters
|
|
{'name': 'ma_cross_short_window', 'type': 'int', 'min': 3, 'max': 15, 'strategy': 'MACrossStrategy'},
|
|
{'name': 'ma_cross_long_window', 'type': 'int', 'min': 10, 'max': 60, 'strategy': 'MACrossStrategy'},
|
|
|
|
# RSIStrategy parameters
|
|
{'name': 'rsi_period', 'type': 'int', 'min': 7, 'max': 21, 'strategy': 'RSIStrategy'},
|
|
{'name': 'rsi_overbought', 'type': 'int', 'min': 65, 'max': 80, 'strategy': 'RSIStrategy'},
|
|
{'name': 'rsi_oversold', 'type': 'int', 'min': 20, 'max': 35, 'strategy': 'RSIStrategy'},
|
|
|
|
# BollingerStrategy parameters
|
|
{'name': 'bollinger_period', 'type': 'int', 'min': 10, 'max': 50, 'strategy': 'BollingerStrategy'},
|
|
{'name': 'bollinger_std_dev', 'type': 'float', 'min': 1.5, 'max': 3.0, 'strategy': 'BollingerStrategy'},
|
|
|
|
# MACDStrategy parameters
|
|
{'name': 'macd_fast_ema', 'type': 'int', 'min': 8, 'max': 20, 'strategy': 'MACDStrategy'},
|
|
{'name': 'macd_slow_ema', 'type': 'int', 'min': 20, 'max': 35, 'strategy': 'MACDStrategy'},
|
|
{'name': 'macd_signal_period', 'type': 'int', 'min': 5, 'max': 15, 'strategy': 'MACDStrategy'},
|
|
|
|
# MeanReversionStrategy parameters
|
|
{'name': 'mean_reversion_period', 'type': 'int', 'min': 10, 'max': 50, 'strategy': 'MeanReversionStrategy'},
|
|
{'name': 'mean_reversion_std_dev','type': 'float', 'min': 1.5, 'max': 3.0, 'strategy': 'MeanReversionStrategy'},
|
|
|
|
# MomentumBreakoutStrategy parameters
|
|
{'name': 'momentum_breakout_period', 'type': 'int', 'min': 10, 'max': 50, 'strategy': 'MomentumBreakoutStrategy'},
|
|
|
|
# KDJStrategy parameters
|
|
{'name': 'kdj_period', 'type': 'int', 'min': 5, 'max': 21, 'strategy': 'KDJStrategy'},
|
|
|
|
# TurtleStrategy parameters
|
|
{'name': 'turtle_period', 'type': 'int', 'min': 10, 'max': 50, 'strategy': 'TurtleStrategy'},
|
|
|
|
# WaveTheoryStrategy parameters
|
|
{'name': 'wave_ema_short', 'type': 'int', 'min': 3, 'max': 10, 'strategy': 'WaveTheoryStrategy'},
|
|
{'name': 'wave_ema_medium', 'type': 'int', 'min': 8, 'max': 20, 'strategy': 'WaveTheoryStrategy'},
|
|
{'name': 'wave_ema_long', 'type': 'int', 'min': 20, 'max': 50, 'strategy': 'WaveTheoryStrategy'},
|
|
{'name': 'wave_period', 'type': 'int', 'min': 10, 'max': 40, 'strategy': 'WaveTheoryStrategy'},
|
|
{'name': 'wave_range_period', 'type': 'int', 'min': 10, 'max': 40, 'strategy': 'WaveTheoryStrategy'},
|
|
{'name': 'wave_adx_period', 'type': 'int', 'min': 10, 'max': 25, 'strategy': 'WaveTheoryStrategy'},
|
|
{'name': 'wave_momentum_period', 'type': 'int', 'min': 7, 'max': 21, 'strategy': 'WaveTheoryStrategy'},
|
|
{'name': 'wave_range_threshold', 'type': 'float', 'min': 0.002, 'max': 0.01, 'strategy': 'WaveTheoryStrategy'},
|
|
{'name': 'wave_adx_threshold', 'type': 'int', 'min': 15, 'max': 35, 'strategy': 'WaveTheoryStrategy'},
|
|
|
|
# DailyBreakoutStrategy parameters
|
|
{'name': 'daily_breakout_bars_count', 'type': 'int', 'min': 720, 'max': 2880, 'strategy': 'DailyBreakoutStrategy'},
|
|
|
|
# Signal Thresholds
|
|
{'name': 'buy_threshold', 'type': 'float', 'min': 0.5, 'max': 3.0, 'strategy': 'signal'},
|
|
{'name': 'sell_threshold', 'type': 'float', 'min': -3.0, 'max': -0.5, 'strategy': 'signal'},
|
|
|
|
# Risk Management parameters
|
|
{'name': 'stop_loss_pct', 'type': 'float', 'min': -0.02, 'max': -0.005, 'strategy': 'risk'},
|
|
{'name': 'profit_retracement_pct','type': 'float', 'min': 0.05, 'max': 0.20, 'strategy': 'risk'},
|
|
{'name': 'min_profit_for_trailing','type': 'float', 'min': 0.005, 'max': 0.02, 'strategy': 'risk'},
|
|
{'name': 'take_profit_pct', 'type': 'float', 'min': 0.10, 'max': 0.50, 'strategy': 'risk'},
|
|
{'name': 'max_holding_minutes', 'type': 'int', 'min': 30, 'max': 180, 'strategy': 'risk'},
|
|
{'name': 'min_profit_for_time_exit','type': 'float', 'min': 0.002, 'max': 0.01, 'strategy': 'risk'},
|
|
|
|
# Strategy Weights (for all strategies)
|
|
{'name': 'weight_MACrossStrategy', 'type': 'float', 'min': 0.0, 'max': 2.0, 'strategy': 'weight'},
|
|
{'name': 'weight_RSIStrategy', 'type': 'float', 'min': 0.0, 'max': 2.0, 'strategy': 'weight'},
|
|
{'name': 'weight_BollingerStrategy', 'type': 'float', 'min': 0.0, 'max': 2.0, 'strategy': 'weight'},
|
|
{'name': 'weight_MeanReversionStrategy', 'type': 'float', 'min': 0.0, 'max': 2.0, 'strategy': 'weight'},
|
|
{'name': 'weight_MomentumBreakoutStrategy', 'type': 'float', 'min': 0.0, 'max': 2.0, 'strategy': 'weight'},
|
|
{'name': 'weight_MACDStrategy', 'type': 'float', 'min': 0.0, 'max': 2.0, 'strategy': 'weight'},
|
|
{'name': 'weight_KDJStrategy', 'type': 'float', 'min': 0.0, 'max': 2.0, 'strategy': 'weight'},
|
|
{'name': 'weight_TurtleStrategy', 'type': 'float', 'min': 0.0, 'max': 2.0, 'strategy': 'weight'},
|
|
{'name': 'weight_DailyBreakoutStrategy', 'type': 'float', 'min': 0.0, 'max': 2.0, 'strategy': 'weight'},
|
|
{'name': 'weight_WaveTheoryStrategy', 'type': 'float', 'min': 0.0, 'max': 2.0, 'strategy': 'weight'},
|
|
|
|
# Market State Analysis parameters
|
|
{'name': 'market_trend_period', 'type': 'int', 'min': 20, 'max': 100, 'strategy': 'market_state'},
|
|
{'name': 'market_retracement_tolerance', 'type': 'float', 'min': 0.1, 'max': 0.5, 'strategy': 'market_state'},
|
|
{'name': 'market_volume_period', 'type': 'int', 'min': 10, 'max': 50, 'strategy': 'market_state'},
|
|
{'name': 'market_volume_ma_period', 'type': 'int', 'min': 5, 'max': 30, 'strategy': 'market_state'},
|
|
|
|
# Trend Indicator Weights
|
|
{'name': 'trend_price_breakout_weight', 'type': 'float', 'min': 0.1, 'max': 0.5, 'strategy': 'trend_weights'},
|
|
{'name': 'trend_volume_confirmation_weight', 'type': 'float', 'min': 0.1, 'max': 0.5, 'strategy': 'trend_weights'},
|
|
{'name': 'trend_momentum_oscillator_weight', 'type': 'float', 'min': 0.1, 'max': 0.5, 'strategy': 'trend_weights'},
|
|
{'name': 'trend_moving_average_weight', 'type': 'float', 'min': 0.1, 'max': 0.5, 'strategy': 'trend_weights'},
|
|
|
|
# Trend Thresholds
|
|
{'name': 'trend_strong_threshold', 'type': 'float', 'min': 0.4, 'max': 0.8, 'strategy': 'trend_thresholds'},
|
|
{'name': 'trend_weak_threshold', 'type': 'float', 'min': 0.2, 'max': 0.5, 'strategy': 'trend_thresholds'},
|
|
{'name': 'trend_volume_spike', 'type': 'float', 'min': 1.0, 'max': 3.0, 'strategy': 'trend_thresholds'},
|
|
{'name': 'trend_oversold', 'type': 'int', 'min': 20, 'max': 40, 'strategy': 'trend_thresholds'},
|
|
{'name': 'trend_overbought', 'type': 'int', 'min': 60, 'max': 80, 'strategy': 'trend_thresholds'},
|
|
|
|
# Confidence Thresholds
|
|
{'name': 'confidence_high', 'type': 'float', 'min': 0.5, 'max': 0.9, 'strategy': 'confidence'},
|
|
{'name': 'confidence_medium', 'type': 'float', 'min': 0.3, 'max': 0.7, 'strategy': 'confidence'},
|
|
]
|
|
|
|
# Map config keys to strategy class names (for instantiation)
|
|
strategy_config_key_to_class = {
|
|
"ma_cross": MACrossStrategy,
|
|
"rsi": RSIStrategy,
|
|
"bollinger": BollingerStrategy,
|
|
"mean_reversion": MeanReversionStrategy,
|
|
"momentum_breakout": MomentumBreakoutStrategy,
|
|
"macd": MACDStrategy,
|
|
"kdj": KDJStrategy,
|
|
"turtle": TurtleStrategy,
|
|
"daily_breakout": DailyBreakoutStrategy,
|
|
"wave_theory": WaveTheoryStrategy
|
|
}
|
|
|
|
def load_historical_data():
|
|
"""加载历史数据(与原优化器相同)"""
|
|
global df_historical_data
|
|
if df_historical_data is not None:
|
|
return df_historical_data
|
|
|
|
initialize()
|
|
rates = (
|
|
get_rates(SYMBOL, TIMEFRAME, OPTIMIZER_COUNT, OPTIMIZER_START_DATE, OPTIMIZER_END_DATE)
|
|
if USE_DATE_RANGE else
|
|
get_rates(SYMBOL, TIMEFRAME, OPTIMIZER_COUNT)
|
|
)
|
|
shutdown()
|
|
|
|
if rates is None:
|
|
print("获取历史数据失败,退出。")
|
|
sys.exit(1)
|
|
|
|
df = pd.DataFrame(rates)
|
|
df.set_index(pd.to_datetime(df['time'], unit='s'), inplace=True)
|
|
|
|
if len(df) > OPTIMIZER_COUNT:
|
|
df = df.iloc[-OPTIMIZER_COUNT:]
|
|
|
|
df_historical_data = df
|
|
return df_historical_data
|
|
|
|
def generate_random_parameters():
|
|
"""生成随机参数组合"""
|
|
params = []
|
|
for param_def in PARAMETER_DEFINITIONS:
|
|
if param_def['type'] == 'int':
|
|
value = random.randint(param_def['min'], param_def['max'])
|
|
else: # float
|
|
value = random.uniform(param_def['min'], param_def['max'])
|
|
params.append(value)
|
|
return params
|
|
|
|
def evaluate_parameters(params_array, df_data):
|
|
"""评估参数组合的适应度(与原优化器相同)"""
|
|
parsed_params = {}
|
|
for idx, param_def in enumerate(PARAMETER_DEFINITIONS):
|
|
val = params_array[idx]
|
|
parsed_params[param_def['name']] = int(round(val)) if param_def['type'] == 'int' else val
|
|
|
|
# 构建策略参数字典(与原优化器相同)
|
|
strategy_params_dict = {}
|
|
for param_def in PARAMETER_DEFINITIONS:
|
|
strategy_name = param_def.get('strategy')
|
|
if strategy_name and strategy_name not in ['weight', 'signal', 'risk', 'market_state', 'trend_weights', 'trend_thresholds', 'confidence']:
|
|
if strategy_name not in strategy_params_dict:
|
|
strategy_params_dict[strategy_name] = {}
|
|
|
|
param_name = param_def['name']
|
|
# 参数名映射(与原优化器相同)
|
|
if param_name == 'ma_cross_short_window':
|
|
param_name = 'short_window'
|
|
elif param_name == 'ma_cross_long_window':
|
|
param_name = 'long_window'
|
|
elif param_name.endswith('_period') and 'ma_cross' not in param_name:
|
|
param_name = 'period'
|
|
elif param_name == 'bollinger_std_dev':
|
|
param_name = 'std_dev'
|
|
elif param_name == 'mean_reversion_std_dev':
|
|
param_name = 'std_dev'
|
|
elif param_name.startswith('macd_'):
|
|
param_name = param_name[5:] # 移除 "macd_" 前缀
|
|
elif param_name.startswith('wave_'):
|
|
param_name = param_name[5:] # 移除 "wave_" 前缀
|
|
if param_name == "period":
|
|
param_name = "wave_period"
|
|
elif param_name == 'daily_breakout_bars_count':
|
|
param_name = 'bars_count'
|
|
|
|
strategy_params_dict[strategy_name][param_name] = parsed_params[param_def['name']]
|
|
|
|
# 策略实例化
|
|
strategies_for_backtest_instance = {}
|
|
for config_key, strategy_class in strategy_config_key_to_class.items():
|
|
strategy_class_name = strategy_class.__name__
|
|
strat_params = strategy_params_dict.get(strategy_class_name, {})
|
|
strategies_for_backtest_instance[config_key] = strategy_class(None, SYMBOL, TIMEFRAME, **strat_params)
|
|
|
|
# 获取策略权重
|
|
suggested_weights = {
|
|
p['name'].replace('weight_', ''): parsed_params[p['name']]
|
|
for p in PARAMETER_DEFINITIONS if p.get('strategy') == 'weight'
|
|
}
|
|
|
|
# 获取信号阈值
|
|
signal_thresholds = {
|
|
'buy_threshold': parsed_params.get('buy_threshold', SIGNAL_THRESHOLDS.get('buy_threshold', 1.0)),
|
|
'sell_threshold': parsed_params.get('sell_threshold', SIGNAL_THRESHOLDS.get('sell_threshold', -1.0))
|
|
}
|
|
|
|
# 获取风险管理参数
|
|
risk_params = {
|
|
'stop_loss_pct': parsed_params.get('stop_loss_pct', RISK_CONFIG.get('stop_loss_pct', -0.01)),
|
|
'profit_retracement_pct': parsed_params.get('profit_retracement_pct', RISK_CONFIG.get('profit_retracement_pct', 0.10)),
|
|
'min_profit_for_trailing': parsed_params.get('min_profit_for_trailing', RISK_CONFIG.get('min_profit_for_trailing', 0.01)),
|
|
'take_profit_pct': parsed_params.get('take_profit_pct', RISK_CONFIG.get('take_profit_pct', 0.20)),
|
|
'max_holding_minutes': parsed_params.get('max_holding_minutes', RISK_CONFIG.get('max_holding_minutes', 60)),
|
|
'min_profit_for_time_exit': parsed_params.get('min_profit_for_time_exit', RISK_CONFIG.get('min_profit_for_time_exit', 0.005))
|
|
}
|
|
|
|
# 获取市场状态分析参数
|
|
market_state_params = {
|
|
'trend_period': parsed_params.get('market_trend_period', MARKET_STATE_CONFIG.get('trend_period', 50)),
|
|
'retracement_tolerance': parsed_params.get('market_retracement_tolerance', MARKET_STATE_CONFIG.get('retracement_tolerance', 0.30)),
|
|
'volume_period': parsed_params.get('market_volume_period', MARKET_STATE_CONFIG.get('volume_period', 20)),
|
|
'volume_ma_period': parsed_params.get('market_volume_ma_period', MARKET_STATE_CONFIG.get('volume_ma_period', 10))
|
|
}
|
|
|
|
# 获取趋势指标权重
|
|
trend_weights = {
|
|
'price_breakout': parsed_params.get('trend_price_breakout_weight', TREND_INDICATOR_WEIGHTS.get('price_breakout', 0.35)),
|
|
'volume_confirmation': parsed_params.get('trend_volume_confirmation_weight', TREND_INDICATOR_WEIGHTS.get('volume_confirmation', 0.25)),
|
|
'momentum oscillator': parsed_params.get('trend_momentum_oscillator_weight', TREND_INDICATOR_WEIGHTS.get('momentum oscillator', 0.20)),
|
|
'moving_average': parsed_params.get('trend_moving_average_weight', TREND_INDICATOR_WEIGHTS.get('moving_average', 0.20))
|
|
}
|
|
|
|
# 获取趋势阈值
|
|
trend_thresholds = {
|
|
'strong_trend': parsed_params.get('trend_strong_threshold', TREND_THRESHOLDS.get('strong_trend', 0.6)),
|
|
'weak_trend': parsed_params.get('trend_weak_threshold', TREND_THRESHOLDS.get('weak_trend', 0.3)),
|
|
'volume_spike': parsed_params.get('trend_volume_spike', TREND_THRESHOLDS.get('volume_spike', 1.5)),
|
|
'oversold': parsed_params.get('trend_oversold', TREND_THRESHOLDS.get('oversold', 30)),
|
|
'overbought': parsed_params.get('trend_overbought', TREND_THRESHOLDS.get('overbought', 70))
|
|
}
|
|
|
|
# 获取置信度阈值
|
|
confidence_thresholds = {
|
|
'high_confidence': parsed_params.get('confidence_high', CONFIDENCE_THRESHOLDS.get('high_confidence', 0.7)),
|
|
'medium_confidence': parsed_params.get('confidence_medium', CONFIDENCE_THRESHOLDS.get('medium_confidence', 0.4))
|
|
}
|
|
|
|
# 回测执行(与原优化器相同)
|
|
df = df_data.copy()
|
|
data_provider = BacktestDataProvider(df, initial_equity=INITIAL_CAPITAL)
|
|
|
|
risk_controller = RiskController(data_provider, trade_direction=BACKTEST_CONFIG['trade_direction'])
|
|
|
|
for key, value in risk_params.items():
|
|
if hasattr(risk_controller, key):
|
|
setattr(risk_controller, key, value)
|
|
|
|
dynamic_weight_manager = DynamicWeightManager(
|
|
data_provider,
|
|
market_state_params=market_state_params,
|
|
trend_weights=trend_weights,
|
|
trend_thresholds=trend_thresholds,
|
|
confidence_thresholds=confidence_thresholds
|
|
)
|
|
|
|
all_signals_df = pd.DataFrame(index=df.index)
|
|
for config_key, strat_instance in strategies_for_backtest_instance.items():
|
|
if hasattr(strat_instance, 'run_backtest') and callable(getattr(strat_instance, 'run_backtest')):
|
|
all_signals_df[strat_instance.name] = strat_instance.run_backtest(df.copy())
|
|
|
|
if all_signals_df.empty:
|
|
return 0.0
|
|
|
|
strategies_with_weights = dynamic_weight_manager.get_current_strategies_and_weights()
|
|
|
|
weights_arr = []
|
|
for name, sig in all_signals_df.items():
|
|
w = 1.0
|
|
for strat_instance, weight in strategies_with_weights:
|
|
if strat_instance.name == name:
|
|
w = weight
|
|
break
|
|
if w == 1.0:
|
|
w = suggested_weights.get(name, 1.0)
|
|
weights_arr.append(sig.values * w)
|
|
|
|
combined = np.sum(np.column_stack(weights_arr), axis=1)
|
|
buy_th, sell_th = signal_thresholds['buy_threshold'], signal_thresholds['sell_threshold']
|
|
final_signals = np.where(combined > buy_th, 1, np.where(combined < sell_th, -1, 0))
|
|
|
|
data_provider.current_index = 0
|
|
for i in range(len(df)):
|
|
current_price = data_provider.get_current_price(SYMBOL)
|
|
if not current_price:
|
|
continue
|
|
|
|
current_signal = final_signals[i]
|
|
direction = None
|
|
if current_signal == 1:
|
|
direction = "buy"
|
|
elif current_signal == -1:
|
|
direction = "sell"
|
|
|
|
if direction:
|
|
risk_controller.process_trading_signal(direction, current_price, abs(current_signal))
|
|
|
|
risk_controller.monitor_positions(current_price)
|
|
data_provider.tick()
|
|
|
|
total_profit_loss = risk_controller.position_manager.get_trade_summary().get('total_profit_loss', 0)
|
|
return total_profit_loss
|
|
|
|
def phase1_exploration(df_data, exploration_size=100):
|
|
"""第一阶段:随机探索"""
|
|
print(f"=== 第一阶段:随机探索 ===")
|
|
print(f"生成 {exploration_size} 组随机参数进行探索...")
|
|
|
|
exploration_data = []
|
|
|
|
# 使用多进程并行评估
|
|
num_cores = multiprocessing.cpu_count()
|
|
print(f"使用 {num_cores} 个核心进行并行评估...")
|
|
|
|
# 生成所有随机参数
|
|
all_params = [generate_random_parameters() for _ in range(exploration_size)]
|
|
|
|
# 并行评估
|
|
results = Parallel(n_jobs=num_cores)(
|
|
delayed(evaluate_parameters)(params, df_data)
|
|
for params in tqdm(all_params, desc="随机探索评估")
|
|
)
|
|
|
|
# 收集结果
|
|
for params, profit in zip(all_params, results):
|
|
exploration_data.append({
|
|
'params': params.copy(),
|
|
'profit': profit
|
|
})
|
|
|
|
# 按盈利排序
|
|
exploration_data.sort(key=lambda x: x['profit'], reverse=True)
|
|
|
|
print(f"随机探索完成,最佳盈利: ${exploration_data[0]['profit']:.2f}")
|
|
print(f"最差盈利: ${exploration_data[-1]['profit']:.2f}")
|
|
print(f"平均盈利: ${np.mean([d['profit'] for d in exploration_data]):.2f}")
|
|
|
|
return exploration_data
|
|
|
|
def phase2_model_training(exploration_data):
|
|
"""第二阶段:模型训练"""
|
|
print(f"\n=== 第二阶段:模型训练 ===")
|
|
|
|
# 准备训练数据
|
|
X = np.array([d['params'] for d in exploration_data])
|
|
y = np.array([d['profit'] for d in exploration_data])
|
|
|
|
# 分割训练集和测试集
|
|
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=SEED)
|
|
|
|
print(f"训练数据: {len(X_train)} 组, 测试数据: {len(X_test)} 组")
|
|
|
|
# 训练随机森林模型
|
|
model = RandomForestRegressor(
|
|
n_estimators=100,
|
|
max_depth=10,
|
|
min_samples_split=5,
|
|
min_samples_leaf=2,
|
|
random_state=SEED,
|
|
n_jobs=-1
|
|
)
|
|
|
|
model.fit(X_train, y_train)
|
|
|
|
# 评估模型性能
|
|
y_pred_train = model.predict(X_train)
|
|
y_pred_test = model.predict(X_test)
|
|
|
|
train_r2 = r2_score(y_train, y_pred_train)
|
|
test_r2 = r2_score(y_test, y_pred_test)
|
|
train_mse = mean_squared_error(y_train, y_pred_train)
|
|
test_mse = mean_squared_error(y_test, y_pred_test)
|
|
|
|
print(f"模型训练完成:")
|
|
print(f"训练集 R²: {train_r2:.4f}, MSE: {train_mse:.4f}")
|
|
print(f"测试集 R²: {test_r2:.4f}, MSE: {test_mse:.4f}")
|
|
|
|
# 特征重要性分析
|
|
feature_importance = model.feature_importances_
|
|
top_features = np.argsort(feature_importance)[-10:][::-1] # 最重要的10个特征
|
|
|
|
print(f"\n最重要的10个参数:")
|
|
for idx in top_features:
|
|
param_def = PARAMETER_DEFINITIONS[idx]
|
|
print(f" {param_def['name']}: {feature_importance[idx]:.4f}")
|
|
|
|
return model
|
|
|
|
def phase3_prediction_and_validation(model, df_data, exploration_data, prediction_size=1000, top_n=20):
|
|
"""第三阶段:预测筛选和验证"""
|
|
print(f"\n=== 第三阶段:预测筛选 ===")
|
|
print(f"生成 {prediction_size} 组参数进行预测,筛选前 {top_n} 组进行验证...")
|
|
|
|
# 生成大量随机参数进行预测
|
|
prediction_params = [generate_random_parameters() for _ in range(prediction_size)]
|
|
|
|
# 使用模型预测盈利
|
|
predicted_profits = model.predict(prediction_params)
|
|
|
|
# 按预测盈利排序,选择前top_n组
|
|
top_indices = np.argsort(predicted_profits)[-top_n:][::-1]
|
|
|
|
print(f"预测筛选完成,预测最高盈利: ${predicted_profits[top_indices[0]]:.2f}")
|
|
|
|
# 对筛选出的参数进行实际验证
|
|
validation_results = []
|
|
|
|
print(f"开始验证前 {top_n} 组参数...")
|
|
for i, idx in enumerate(tqdm(top_indices, desc="验证参数")):
|
|
params = prediction_params[idx]
|
|
actual_profit = evaluate_parameters(params, df_data)
|
|
|
|
validation_results.append({
|
|
'params': params.copy(),
|
|
'predicted_profit': predicted_profits[idx],
|
|
'actual_profit': actual_profit,
|
|
'prediction_error': abs(predicted_profits[idx] - actual_profit)
|
|
})
|
|
|
|
if (i + 1) % 5 == 0:
|
|
print(f"已验证 {i + 1}/{top_n} 组,当前最佳实际盈利: ${max([r['actual_profit'] for r in validation_results[:i+1]]):.2f}")
|
|
|
|
# 按实际盈利排序
|
|
validation_results.sort(key=lambda x: x['actual_profit'], reverse=True)
|
|
|
|
best_validation = validation_results[0]
|
|
print(f"\n验证完成:")
|
|
print(f"最佳实际盈利: ${best_validation['actual_profit']:.2f}")
|
|
print(f"预测盈利: ${best_validation['predicted_profit']:.2f}")
|
|
print(f"预测误差: ${best_validation['prediction_error']:.2f}")
|
|
|
|
return validation_results
|
|
|
|
def run_ml_optimizer():
|
|
"""运行机器学习优化器"""
|
|
try:
|
|
# ML优化器配置
|
|
EXPLORATION_SIZE = 100 # 第一阶段随机探索数量
|
|
PREDICTION_SIZE = 1000 # 第二阶段预测数量
|
|
VALIDATION_TOP_N = 20 # 第三阶段验证数量
|
|
|
|
print("=" * 60)
|
|
print("机器学习优化器启动")
|
|
print("=" * 60)
|
|
print(f"第一阶段: 随机探索 {EXPLORATION_SIZE} 组参数")
|
|
print(f"第二阶段: 训练预测模型")
|
|
print(f"第三阶段: 预测筛选 {PREDICTION_SIZE} 组参数,验证前 {VALIDATION_TOP_N} 组")
|
|
|
|
# 加载历史数据
|
|
df_historical_data = load_historical_data()
|
|
print(f"历史数据加载完成,数据量: {len(df_historical_data)} 条")
|
|
|
|
# 第一阶段:随机探索
|
|
exploration_data = phase1_exploration(df_historical_data, EXPLORATION_SIZE)
|
|
|
|
# 第二阶段:模型训练
|
|
model = phase2_model_training(exploration_data)
|
|
|
|
# 第三阶段:预测筛选和验证
|
|
validation_results = phase3_prediction_and_validation(
|
|
model, df_historical_data, exploration_data, PREDICTION_SIZE, VALIDATION_TOP_N
|
|
)
|
|
|
|
# 最终结果
|
|
best_result = validation_results[0]
|
|
best_params_array = best_result['params']
|
|
best_profit = best_result['actual_profit']
|
|
|
|
# 解析最佳参数
|
|
parsed_best_params = {}
|
|
for idx, param_def in enumerate(PARAMETER_DEFINITIONS):
|
|
param_value = best_params_array[idx]
|
|
if param_def['type'] == 'int':
|
|
parsed_best_params[param_def['name']] = int(round(param_value))
|
|
else:
|
|
parsed_best_params[param_def['name']] = param_value
|
|
|
|
# 输出结果
|
|
print("\n" + "=" * 60)
|
|
print("机器学习优化完成")
|
|
print("=" * 60)
|
|
print(f"最佳盈利: ${best_profit:.2f}")
|
|
print(f"预测盈利: ${best_result['predicted_profit']:.2f}")
|
|
print(f"预测误差: ${best_result['prediction_error']:.2f}")
|
|
|
|
# 显示最佳参数
|
|
print("\n=== 最佳参数组合 ===")
|
|
print("\n策略参数:")
|
|
for param_def in PARAMETER_DEFINITIONS:
|
|
if param_def.get('strategy') not in ['weight', 'signal', 'risk', 'market_state', 'trend_weights', 'trend_thresholds', 'confidence']:
|
|
print(f" {param_def['name']}: {parsed_best_params[param_def['name']]}")
|
|
|
|
print("\n策略权重:")
|
|
weight_sum = 0
|
|
for param_def in PARAMETER_DEFINITIONS:
|
|
if param_def.get('strategy') == 'weight':
|
|
strategy_name = param_def['name'].replace('weight_', '')
|
|
weight_value = parsed_best_params[param_def['name']]
|
|
weight_sum += weight_value
|
|
print(f" {strategy_name}: {weight_value:.3f}")
|
|
print(f"\n权重总和: {weight_sum:.3f}")
|
|
|
|
# 保存结果(与原优化器格式相同)
|
|
save_ml_optimization_results(parsed_best_params, best_profit, exploration_data, validation_results)
|
|
|
|
return parsed_best_params, best_profit
|
|
|
|
except Exception as e:
|
|
print(f"机器学习优化器运行出错: {e}")
|
|
import traceback
|
|
print(traceback.format_exc())
|
|
sys.exit(1)
|
|
|
|
def save_ml_optimization_results(best_params, best_fitness, exploration_data, validation_results):
|
|
"""保存机器学习优化结果"""
|
|
try:
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
|
|
# 创建结果数据结构
|
|
results = {
|
|
'optimization_info': {
|
|
'timestamp': timestamp,
|
|
'method': 'machine_learning',
|
|
'best_fitness': float(best_fitness),
|
|
'exploration_size': len(exploration_data),
|
|
'prediction_size': 1000,
|
|
'validation_top_n': len(validation_results),
|
|
'random_seed': SEED
|
|
},
|
|
'best_parameters': best_params,
|
|
'exploration_results': exploration_data[:50], # 保存前50个探索结果
|
|
'validation_results': validation_results
|
|
}
|
|
|
|
# 保存为JSON文件
|
|
json_filename = f"ml_optimization_results_{timestamp}.json"
|
|
with open(json_filename, 'w', encoding='utf-8') as f:
|
|
json.dump(results, f, ensure_ascii=False, indent=2)
|
|
print(f"\n机器学习优化结果已保存到: {json_filename}")
|
|
|
|
# 保存为CSV文件(只保存最佳参数)
|
|
csv_filename = f"ml_optimization_best_params_{timestamp}.csv"
|
|
with open(csv_filename, 'w', newline='', encoding='utf-8') as f:
|
|
writer = csv.writer(f)
|
|
writer.writerow(['Parameter', 'Value', 'Type'])
|
|
|
|
for param_def in PARAMETER_DEFINITIONS:
|
|
param_name = param_def['name']
|
|
param_value = best_params[param_name]
|
|
param_type = param_def['type']
|
|
strategy_type = param_def.get('strategy', 'unknown')
|
|
writer.writerow([param_name, param_value, f"{param_type}_{strategy_type}"])
|
|
|
|
print(f"最佳参数已保存到: {csv_filename}")
|
|
|
|
# 生成可读的文本报告
|
|
txt_filename = f"ml_optimization_report_{timestamp}.txt"
|
|
with open(txt_filename, 'w', encoding='utf-8') as f:
|
|
f.write("=" * 80 + "\n")
|
|
f.write("机器学习优化结果报告\n")
|
|
f.write("=" * 80 + "\n")
|
|
f.write(f"优化时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write(f"优化方法: 随机探索 + 模型预测 + 验证筛选\n")
|
|
f.write(f"最佳适应度 (总盈亏): ${best_fitness:.2f}\n")
|
|
f.write(f"探索样本数: {len(exploration_data)}\n")
|
|
f.write(f"预测样本数: 1000\n")
|
|
f.write(f"验证样本数: {len(validation_results)}\n")
|
|
f.write(f"随机种子: {SEED}\n")
|
|
f.write("\n")
|
|
|
|
f.write("优化阶段说明:\n")
|
|
f.write("-" * 40 + "\n")
|
|
f.write("1. 随机探索: 生成100组随机参数进行实际回测\n")
|
|
f.write("2. 模型训练: 使用随机森林模型拟合参数与盈利关系\n")
|
|
f.write("3. 预测筛选: 预测1000组参数,选择前20组进行验证\n")
|
|
f.write("\n")
|
|
|
|
f.write("最佳参数组合:\n")
|
|
f.write("-" * 40 + "\n")
|
|
|
|
# 分组显示参数
|
|
f.write("\n【策略参数】\n")
|
|
for param_def in PARAMETER_DEFINITIONS:
|
|
if param_def.get('strategy') not in ['weight', 'signal', 'risk', 'market_state', 'trend_weights', 'trend_thresholds', 'confidence']:
|
|
param_name = param_def['name']
|
|
param_value = best_params[param_name]
|
|
f.write(f" {param_name}: {param_value}\n")
|
|
|
|
f.write("\n【策略权重】\n")
|
|
weight_sum = 0
|
|
for param_def in PARAMETER_DEFINITIONS:
|
|
if param_def.get('strategy') == 'weight':
|
|
param_name = param_def['name']
|
|
param_value = best_params[param_name]
|
|
strategy_name = param_name.replace('weight_', '')
|
|
weight_sum += param_value
|
|
f.write(f" {strategy_name}: {param_value:.3f}\n")
|
|
f.write(f" 权重总和: {weight_sum:.3f}\n")
|
|
|
|
f.write("\n" + "=" * 80 + "\n")
|
|
f.write("验证结果统计\n")
|
|
f.write("=" * 80 + "\n")
|
|
|
|
validation_profits = [r['actual_profit'] for r in validation_results]
|
|
prediction_errors = [r['prediction_error'] for r in validation_results]
|
|
|
|
f.write(f"验证平均盈利: ${np.mean(validation_profits):.2f}\n")
|
|
f.write(f"验证最佳盈利: ${np.max(validation_profits):.2f}\n")
|
|
f.write(f"验证最差盈利: ${np.min(validation_profits):.2f}\n")
|
|
f.write(f"平均预测误差: ${np.mean(prediction_errors):.2f}\n")
|
|
f.write(f"最大预测误差: ${np.max(prediction_errors):.2f}\n")
|
|
|
|
print(f"详细报告已保存到: {txt_filename}")
|
|
|
|
except Exception as e:
|
|
print(f"保存机器学习优化结果时出错: {e}")
|
|
import traceback
|
|
print(traceback.format_exc())
|
|
|
|
if __name__ == "__main__":
|
|
run_ml_optimizer() |