mirror of
https://github.com/silencesdg/mt5_python_ea_suite.git
synced 2026-08-02 13:47:43 +00:00
feat: 一票制并发锁 + MT5硬止损兜底
- 一票制: _pending_long/_pending_short 计数器防同一周期内多信号穿透 - 硬止损: MT5下单时附带sl/tp,倍率1.5x(止损)/1.3x(止盈),比EA软止损更宽 - config.py: 新增 hard_sl_multiplier/hard_tp_multiplier - 全部 send_order 链路(abc/remote/live/server/dryrun/backtest) 支持 sl/tp 参数 - 对冲模块: 彻底移除 - 日志: 去重+30轮摘要
This commit is contained in:
+48
-8
@@ -32,10 +32,12 @@ from config import (
|
||||
SYMBOL, TIMEFRAME, OPTIMIZER_COUNT, OPTIMIZER_START_DATE, OPTIMIZER_END_DATE,
|
||||
USE_DATE_RANGE, INITIAL_CAPITAL, SIGNAL_THRESHOLDS, DEFAULT_WEIGHTS,
|
||||
RISK_CONFIG, GENETIC_OPTIMIZER_CONFIG,
|
||||
MARKET_STATE_CONFIG, TREND_INDICATOR_WEIGHTS, TREND_THRESHOLDS, CONFIDENCE_THRESHOLDS
|
||||
MARKET_STATE_CONFIG, TREND_INDICATOR_WEIGHTS, TREND_THRESHOLDS, CONFIDENCE_THRESHOLDS,
|
||||
DATA_PROVIDER_MODE, REMOTE_SERVER_HOST, REMOTE_SERVER_PORT
|
||||
)
|
||||
from utils.constants import PERIOD_H1
|
||||
from core.utils import get_rates, initialize, shutdown
|
||||
from core.data.remote import RemoteDataProvider
|
||||
from logger import logger
|
||||
|
||||
# 导入策略模块确保 StrategyRegistry 已注册
|
||||
@@ -46,6 +48,18 @@ _multi_tf: MultiTimeframeDataStore | None = None
|
||||
_cached_signals: pd.DataFrame | None = None
|
||||
_registry: StrategyRegistry | None = None
|
||||
|
||||
# MT5 结构化数组 dtype(用于远程 API JSON → numpy 转换)
|
||||
_MT5_RATES_DTYPE = np.dtype([
|
||||
('time', 'i8'),
|
||||
('open', 'f8'),
|
||||
('high', 'f8'),
|
||||
('low', 'f8'),
|
||||
('close', 'f8'),
|
||||
('tick_volume', 'i8'),
|
||||
('spread', 'i4'),
|
||||
('real_volume', 'i8'),
|
||||
])
|
||||
|
||||
|
||||
def init_worker():
|
||||
"""多进程worker初始化:抑制日志噪音"""
|
||||
@@ -397,15 +411,41 @@ def evaluate_fitness(individual, multi_tf: MultiTimeframeDataStore,
|
||||
return (total_pnl,)
|
||||
|
||||
|
||||
def _json_to_mt5_rates(rates_data):
|
||||
"""将远程API JSON rates 转换为 MT5 兼容的 numpy 结构化数组"""
|
||||
if not rates_data:
|
||||
return None
|
||||
records = []
|
||||
for r in rates_data:
|
||||
records.append((
|
||||
r['time'], r['open'], r['high'], r['low'], r['close'],
|
||||
r.get('tick_volume', 0), r.get('spread', 0), r.get('real_volume', 0),
|
||||
))
|
||||
return np.array(records, dtype=_MT5_RATES_DTYPE)
|
||||
|
||||
|
||||
def load_historical_data():
|
||||
"""一次性加载M1数据并返回 MultiTimeframeDataStore"""
|
||||
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 DATA_PROVIDER_MODE == "remote":
|
||||
provider = RemoteDataProvider(host=REMOTE_SERVER_HOST, port=REMOTE_SERVER_PORT)
|
||||
if not provider.initialize():
|
||||
raise RuntimeError("远程MT5 API初始化失败,请检查 Windows MT5 是否运行")
|
||||
|
||||
rates_json = provider.get_historical_data(SYMBOL, TIMEFRAME, OPTIMIZER_COUNT)
|
||||
provider.shutdown()
|
||||
|
||||
if not rates_json:
|
||||
raise RuntimeError("远程获取历史数据失败")
|
||||
|
||||
rates = _json_to_mt5_rates(rates_json)
|
||||
else:
|
||||
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 or len(rates) == 0:
|
||||
raise RuntimeError("获取历史数据失败")
|
||||
|
||||
Reference in New Issue
Block a user