Files
EA_with_Python/Experts/MyProject/HITPositionManagerTestHarness.mq5
T
2026-06-07 22:06:30 +09:00

244 lines
8.8 KiB
Plaintext

//+------------------------------------------------------------------+
//| 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 <Trade/Trade.mqh>
#include <MyLib/Trading/SLTPManager.mqh>
#include <MyLib/Trading/HITPositionLifecycleManager.mqh>
// 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();
}