Strengthen Python timeout recovery and candidate freshness

This commit is contained in:
Hiroaki86
2026-05-30 08:59:40 +09:00
parent 265d378e07
commit 321431d8ec
17 changed files with 454 additions and 48 deletions
+67 -1
View File
@@ -2,6 +2,66 @@
#define HIT_TRADE_MANAGER_MQH
//+------------------------------------------------------------------+
//| 注文ロットがブローカー制約を満たすか判定する関数
//+------------------------------------------------------------------+
/**
* @brief 通常注文と分割注文の最終送信前に volume min/max/step を検証します。
*/
bool IsOrderVolumeAllowed(const double volume, const string context)
{
double min_volume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max_volume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step_volume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(volume <= 0.0 || min_volume <= 0.0 || max_volume <= 0.0 || step_volume <= 0.0)
{
Print("[Order Skip] invalid volume setting. context=", context,
" volume=", volume, " min=", min_volume,
" max=", max_volume, " step=", step_volume);
return false;
}
if(volume < min_volume || volume > max_volume)
{
Print("[Order Skip] volume out of range. context=", context,
" volume=", volume, " min=", min_volume, " max=", max_volume);
return false;
}
double steps = (volume - min_volume) / step_volume;
double nearest = MathRound(steps);
if(MathAbs(steps - nearest) > 0.000001)
{
Print("[Order Skip] volume does not match broker step. context=", context,
" volume=", volume, " min=", min_volume, " step=", step_volume);
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| ブローカーのロットstepから正規化桁数を推定する関数
//+------------------------------------------------------------------+
int VolumeDigits()
{
int volume_digits = 2;
double step_volume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
if(step_volume > 0.0)
{
volume_digits = 0;
double step = step_volume;
while(step < 1.0 && volume_digits < 8)
{
step *= 10.0;
volume_digits++;
}
}
return volume_digits;
}
//+------------------------------------------------------------------+
//| エントリー注文を送信する関数 (1:buy-stop, 2:buy-limit, 3:sell-stop, 4:sell-limit)
//+------------------------------------------------------------------+
@@ -23,7 +83,7 @@ bool SendOrder(int orderType, double price, double tp, double sl, double volume,
request.magic = magic_number;
request.symbol = _Symbol;
request.volume = volume;
request.volume = NormalizeDouble(volume, VolumeDigits());
request.deviation = slippage;
request.type_filling = GetOrderFillingPolicy(_Symbol);
request.price = NormalizeDouble(price, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS));
@@ -64,6 +124,12 @@ bool SendOrder(int orderType, double price, double tp, double sl, double volume,
return false;
}
string volume_context = orderTypeStr;
if(comment_suffix != "")
volume_context += " " + comment_suffix;
if(!IsOrderVolumeAllowed(volume, volume_context))
return false;
// 注文送信
if(!OrderSend(request, result))
{
+15 -11
View File
@@ -335,8 +335,9 @@ public:
continue;
double candidate_sl = 0.0;
const double evaluation_price = (type == POSITION_TYPE_BUY) ? tick.bid : tick.ask;
if (!CalcHighVolatilitySL(type,
tick.bid,
evaluation_price,
open_m1,
open_m3,
open_m5,
@@ -381,7 +382,10 @@ private:
/// @details GOLD/XAUUSD は 0.1、2/3桁は0.01、4/5桁は0.0001を1 pipとして扱う。
double PipSize()
{
if (m_symbol == "XAUUSD" || m_symbol == "GOLD")
string normalized_symbol = m_symbol;
StringToUpper(normalized_symbol);
if (StringFind(normalized_symbol, "XAUUSD") >= 0 ||
StringFind(normalized_symbol, "GOLD") >= 0)
return 0.1;
const int digits = (int)SymbolInfoInteger(m_symbol, SYMBOL_DIGITS);
@@ -585,7 +589,7 @@ private:
/// @brief 高ボラティリティ判定に基づくSL候補を計算する。
/// @return いずれかの時間足でしきい値を超え、SL候補を出力した場合は true。
bool CalcHighVolatilitySL(const ENUM_POSITION_TYPE type,
const double current_bid,
const double current_price,
const double open_m1,
const double open_m3,
const double open_m5,
@@ -599,11 +603,11 @@ private:
double candidate_sl = 0.0;
const double rate_ratio = 0.9;
EvaluateHighVolatilityOpen(type, current_bid, open_m1, 100.0, rate_ratio, pip_size, digits, has_candidate, candidate_sl);
EvaluateHighVolatilityOpen(type, current_bid, open_m3, 150.0, rate_ratio, pip_size, digits, has_candidate, candidate_sl);
EvaluateHighVolatilityOpen(type, current_bid, open_m5, 200.0, rate_ratio, pip_size, digits, has_candidate, candidate_sl);
EvaluateHighVolatilityOpen(type, current_bid, open_m10, 300.0, rate_ratio, pip_size, digits, has_candidate, candidate_sl);
EvaluateHighVolatilityOpen(type, current_bid, open_m15, 400.0, rate_ratio, pip_size, digits, has_candidate, candidate_sl);
EvaluateHighVolatilityOpen(type, current_price, open_m1, 100.0, rate_ratio, pip_size, digits, has_candidate, candidate_sl);
EvaluateHighVolatilityOpen(type, current_price, open_m3, 150.0, rate_ratio, pip_size, digits, has_candidate, candidate_sl);
EvaluateHighVolatilityOpen(type, current_price, open_m5, 200.0, rate_ratio, pip_size, digits, has_candidate, candidate_sl);
EvaluateHighVolatilityOpen(type, current_price, open_m10, 300.0, rate_ratio, pip_size, digits, has_candidate, candidate_sl);
EvaluateHighVolatilityOpen(type, current_price, open_m15, 400.0, rate_ratio, pip_size, digits, has_candidate, candidate_sl);
if (!has_candidate)
return false;
@@ -614,7 +618,7 @@ private:
/// @brief 1本の時間足始値から急変幅を評価し、有効なSL候補なら最良候補へ反映する。
void EvaluateHighVolatilityOpen(const ENUM_POSITION_TYPE type,
const double current_bid,
const double current_price,
const double open_price,
const double limit_pips,
const double rate_ratio,
@@ -631,12 +635,12 @@ private:
if (type == POSITION_TYPE_BUY)
{
movement_pips = (current_bid - open_price) / pip_size;
movement_pips = (current_price - open_price) / pip_size;
sl_value = open_price + limit_pips * rate_ratio * pip_size;
}
else if (type == POSITION_TYPE_SELL)
{
movement_pips = (open_price - current_bid) / pip_size;
movement_pips = (open_price - current_price) / pip_size;
sl_value = open_price - limit_pips * rate_ratio * pip_size;
}
else