//+------------------------------------------------------------------+ //| HITPositionManagerTestHarness.mq5 | //| Copyright 2026, nanpin-martin.com | //| https://www.nanpin-martin.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, nanpin-martin.com" #property link "https://nanpin-martin.com/" #property version "1.00" #include #include #include // Strategy Tester harness only. It seeds positions, then calls the production managers. input int input_managed_magic_number = 10001; // 管理対象magic input int input_control_magic_number = 20002; // 非対象確認用magic input ulong input_slippage_points = 10; // 許容偏差(point) input double input_test_lot_size = 0.01; // 検証用ロット input bool input_seed_managed_buy = true; // 管理対象BUYを作成 input bool input_seed_managed_sell = true; // 管理対象SELLを作成 input bool input_seed_control_position = true; // magic違いポジションを作成 input int input_position_close_after_h1_bars = 12; // 保有時間決済(H1本数、0で無効) input bool input_sltp_manager_enabled = true; // SLTP管理を有効化 input bool input_sltp_use_breakeven = true; // 通常BEを有効化 input double input_sltp_breakeven_trigger_pips = 30.0; input double input_sltp_breakeven_buffer_pips = 3.0; input bool input_sltp_use_elapsed_breakeven = false; input double input_sltp_elapsed_breakeven_hours = 4.0; input double input_sltp_elapsed_breakeven_buffer_pips = 3.0; input bool input_sltp_use_active_trailing = false; input double input_sltp_active_breakeven_pips = 30.0; input double input_sltp_active_stop_loss_offset_pips = 5.0; input double input_sltp_active_step_trigger_pips = 10.0; input double input_sltp_active_step_move_pips = 5.0; input bool input_sltp_use_tp_progress_stop = false; input double input_sltp_tp_progress_trigger_percent = 70.0; input double input_sltp_tp_progress_sl_lock_percent = 30.0; input bool input_sltp_use_high_volatility_limit = false; CTrade g_test_trade; CSLTPManager g_sltp_manager; CHITPositionLifecycleManager g_position_lifecycle; bool g_seed_done = false; bool g_sltp_settings_valid = false; int EffectiveCloseAfterSeconds() { int close_bars = input_position_close_after_h1_bars; if(close_bars < 0) close_bars = 0; if(close_bars > 240) close_bars = 240; const int h1_seconds = PeriodSeconds(PERIOD_H1); if(close_bars <= 0 || h1_seconds <= 0) return 0; return close_bars * h1_seconds; } bool IsHedgingAccount() { return (AccountInfoInteger(ACCOUNT_MARGIN_MODE) == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING); } double NormalizedVolume(const double volume) { const double min_volume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); const double max_volume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); const double step_volume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); if(min_volume <= 0.0 || max_volume <= 0.0 || step_volume <= 0.0) return volume; double normalized = volume; if(normalized < min_volume) normalized = min_volume; if(normalized > max_volume) normalized = max_volume; const double steps = MathFloor((normalized - min_volume) / step_volume + 0.5); normalized = min_volume + steps * step_volume; int volume_digits = 0; double step = step_volume; while(step < 1.0 && volume_digits < 8) { step *= 10.0; volume_digits++; } return NormalizeDouble(normalized, volume_digits); } bool ConfigureManagers() { if(!g_position_lifecycle.Init(_Symbol, (ulong)input_managed_magic_number, input_slippage_points, EffectiveCloseAfterSeconds())) return false; g_sltp_manager.SetMagicNumber((ulong)input_managed_magic_number); g_sltp_manager.SetSymbol(_Symbol); g_sltp_manager.SetDeviationInPoints(input_slippage_points); g_sltp_manager.SetBreakevenSettings(input_sltp_use_breakeven, input_sltp_breakeven_trigger_pips, input_sltp_breakeven_buffer_pips); g_sltp_manager.SetElapsedBreakevenSettings(input_sltp_use_elapsed_breakeven, input_sltp_elapsed_breakeven_hours, input_sltp_elapsed_breakeven_buffer_pips); g_sltp_manager.SetActiveTrailingSettings(input_sltp_use_active_trailing, input_sltp_active_breakeven_pips, input_sltp_active_stop_loss_offset_pips, input_sltp_active_step_trigger_pips, input_sltp_active_step_move_pips); g_sltp_manager.SetTpProgressStopSettings(input_sltp_use_tp_progress_stop, input_sltp_tp_progress_trigger_percent, input_sltp_tp_progress_sl_lock_percent); g_sltp_manager.SetHighVolatilityLimitSettings(input_sltp_use_high_volatility_limit); g_sltp_settings_valid = g_sltp_manager.ValidateSettings(); return g_sltp_settings_valid; } bool SeedBuyPosition(const ulong magic, const string comment) { MqlTick tick; if(!SymbolInfoTick(_Symbol, tick)) { Print("Harness tick read failed: err=", GetLastError()); return false; } const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); const double sl = NormalizeDouble(tick.bid - 5000.0 * point, digits); const double tp = NormalizeDouble(tick.bid + 10000.0 * point, digits); g_test_trade.SetExpertMagicNumber(magic); ResetLastError(); if(!g_test_trade.Buy(NormalizedVolume(input_test_lot_size), _Symbol, 0.0, sl, tp, comment)) { Print("Harness BUY seed failed: magic=", magic, " retcode=", g_test_trade.ResultRetcode(), " err=", GetLastError()); return false; } Print("Harness BUY seed succeeded: magic=", magic, " ticket=", g_test_trade.ResultOrder(), " comment=", comment); return true; } bool SeedSellPosition(const ulong magic, const string comment) { MqlTick tick; if(!SymbolInfoTick(_Symbol, tick)) { Print("Harness tick read failed: err=", GetLastError()); return false; } const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS); const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); const double sl = NormalizeDouble(tick.ask + 5000.0 * point, digits); const double tp = NormalizeDouble(tick.ask - 10000.0 * point, digits); g_test_trade.SetExpertMagicNumber(magic); ResetLastError(); if(!g_test_trade.Sell(NormalizedVolume(input_test_lot_size), _Symbol, 0.0, sl, tp, comment)) { Print("Harness SELL seed failed: magic=", magic, " retcode=", g_test_trade.ResultRetcode(), " err=", GetLastError()); return false; } Print("Harness SELL seed succeeded: magic=", magic, " ticket=", g_test_trade.ResultOrder(), " comment=", comment); return true; } void SeedTestPositions() { if(g_seed_done) return; g_test_trade.SetDeviationInPoints((int)input_slippage_points); g_test_trade.SetTypeFillingBySymbol(_Symbol); if(input_seed_managed_buy) SeedBuyPosition((ulong)input_managed_magic_number, "HIT PM TEST managed BUY"); if(input_seed_managed_sell) SeedSellPosition((ulong)input_managed_magic_number, "HIT PM TEST managed SELL"); if(input_seed_control_position) SeedBuyPosition((ulong)input_control_magic_number, "HIT PM TEST control magic"); g_seed_done = true; } void ManageTargetPositions() { g_position_lifecycle.SetCloseAfterSeconds(EffectiveCloseAfterSeconds()); g_position_lifecycle.CloseExpiredPositions(); if(!input_sltp_manager_enabled || !g_sltp_settings_valid) return; g_sltp_manager.ManagePositions(); g_sltp_manager.HighVolatilityLimit(); } int OnInit() { if(!MQLInfoInteger(MQL_TESTER)) { Print("HITPositionManagerTestHarness is for Strategy Tester only."); return INIT_FAILED; } if(!IsHedgingAccount()) { Print("HITPositionManagerTestHarness requires a retail hedging account. account_margin_mode=", AccountInfoInteger(ACCOUNT_MARGIN_MODE)); return INIT_FAILED; } if(!ConfigureManagers()) return INIT_FAILED; return INIT_SUCCEEDED; } void OnTick() { SeedTestPositions(); ManageTargetPositions(); }