Strengthen Python timeout recovery and candidate freshness
This commit is contained in:
@@ -242,6 +242,84 @@ bool UpdateExternalProcessStatus(ExternalProcessState &process, const string lab
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 外部プロセスツリーを強制終了する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief タイムアウトしたcmd/bat配下のPythonをプロセスツリーごと終了します。
|
||||
*
|
||||
* @param label ログ表示用ラベル。
|
||||
* @param process 終了対象の外部プロセス状態。
|
||||
* @return taskkill が正常終了した場合はtrue。
|
||||
*/
|
||||
bool TerminateExternalProcessTree(const string label, ExternalProcessState &process)
|
||||
{
|
||||
if(process.process_id == 0)
|
||||
{
|
||||
Print("[", label, "] cannot terminate process tree: pid is missing.");
|
||||
return false;
|
||||
}
|
||||
|
||||
STARTUPINFO_W startup_info = {};
|
||||
PROCESS_INFORMATION process_info = {};
|
||||
startup_info.cb = (uint)sizeof(startup_info);
|
||||
|
||||
string taskkill_exe = "C:\\Windows\\System32\\taskkill.exe";
|
||||
string command_line = "\"" + taskkill_exe + "\" /PID " +
|
||||
IntegerToString((long)process.process_id) + " /T /F";
|
||||
|
||||
int created = CreateProcessW(
|
||||
taskkill_exe,
|
||||
command_line,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
CREATE_NO_WINDOW,
|
||||
0,
|
||||
"C:\\Windows\\System32",
|
||||
startup_info,
|
||||
process_info
|
||||
);
|
||||
|
||||
if(created == 0 || process_info.hProcess == 0)
|
||||
{
|
||||
Print("[", label, "] taskkill launch failed. pid=", process.process_id,
|
||||
" err=", GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
if(process_info.hThread != 0)
|
||||
CloseHandle(process_info.hThread);
|
||||
|
||||
int wait_result = WaitForSingleObject(process_info.hProcess, TASKKILL_WAIT_MILLISECONDS);
|
||||
uint exit_code = 1;
|
||||
if(wait_result == WAIT_OBJECT_0)
|
||||
{
|
||||
if(GetExitCodeProcess(process_info.hProcess, exit_code) == 0)
|
||||
{
|
||||
Print("[", label, "] failed to get taskkill exit code. err=", GetLastError());
|
||||
exit_code = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[", label, "] taskkill wait failed. result=", wait_result,
|
||||
" err=", GetLastError());
|
||||
}
|
||||
|
||||
CloseHandle(process_info.hProcess);
|
||||
if(wait_result != WAIT_OBJECT_0 || exit_code != 0)
|
||||
{
|
||||
Print("[", label, "] taskkill failed. pid=", process.process_id,
|
||||
" exit_code=", exit_code);
|
||||
return false;
|
||||
}
|
||||
|
||||
Print("[", label, "] timed-out Python process tree terminated. pid=", process.process_id);
|
||||
ResetExternalProcessState(process);
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 外部プロセスがタイムアウトしているか判定する関数
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -312,7 +390,8 @@ bool StartBatchProcess(const string bat_file,
|
||||
const string running_file,
|
||||
const string label,
|
||||
ExternalProcessState &process,
|
||||
const string file_prefix)
|
||||
const string file_prefix,
|
||||
const int price_digits)
|
||||
{
|
||||
STARTUPINFO_W startup_info = {};
|
||||
PROCESS_INFORMATION process_info = {};
|
||||
@@ -320,7 +399,8 @@ bool StartBatchProcess(const string bat_file,
|
||||
startup_info.cb = (uint)sizeof(startup_info);
|
||||
|
||||
string cmd_exe = "C:\\Windows\\System32\\cmd.exe";
|
||||
string command_line = "\"" + cmd_exe + "\" /c \"\"" + bat_file + "\" \"" + file_prefix + "\"\"";
|
||||
string command_line = "\"" + cmd_exe + "\" /c \"\"" + bat_file + "\" \"" +
|
||||
file_prefix + "\" \"" + IntegerToString(price_digits) + "\"\"";
|
||||
string current_directory = BatchWorkingDirectory(bat_file);
|
||||
|
||||
int created = CreateProcessW(
|
||||
@@ -357,7 +437,8 @@ bool StartBatchProcess(const string bat_file,
|
||||
|
||||
CreateRunningFile(running_file, process.process_id);
|
||||
Print("[", label, "] process started. pid=", process.process_id,
|
||||
" file=", bat_file, " cwd=", current_directory, " prefix=", file_prefix);
|
||||
" file=", bat_file, " cwd=", current_directory,
|
||||
" prefix=", file_prefix, " digits=", price_digits);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -550,8 +631,17 @@ bool RecoverTimedOutProcess(const string done_file, const string running_file, c
|
||||
if(!UpdateExternalProcessStatus(process, label))
|
||||
{
|
||||
if(IsExternalProcessTimedOut(process, running_file))
|
||||
{
|
||||
Print("[", label, "] Python exceeded ", PYTHON_TIMEOUT_SECONDS,
|
||||
" seconds, but the process is still running. Keep waiting.");
|
||||
" seconds. Terminating process tree before retry.");
|
||||
if(TerminateExternalProcessTree(label, process))
|
||||
{
|
||||
DeleteRunningFile(running_file);
|
||||
DeleteDoneFile(done_file);
|
||||
return true;
|
||||
}
|
||||
Print("[", label, "] process tree termination failed. Keep waiting.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,8 +80,9 @@ bool ValidateEntryPreconditions(EAState &state)
|
||||
|
||||
if(IsTargetCandidateExpired(state))
|
||||
{
|
||||
Print("[Entry Skip] H1 target candidate expired. loaded_at=",
|
||||
TimeToString(state.target_loaded_at, TIME_DATE | TIME_SECONDS));
|
||||
Print("[Entry Skip] H1 target candidate expired. candidate_at=",
|
||||
TimeToString(TargetCandidateReferenceTime(state), TIME_DATE | TIME_SECONDS),
|
||||
" loaded_at=", TimeToString(state.target_loaded_at, TIME_DATE | TIME_SECONDS));
|
||||
state.chk_cnt = 0;
|
||||
return false;
|
||||
}
|
||||
@@ -91,6 +92,7 @@ bool ValidateEntryPreconditions(EAState &state)
|
||||
Print("[Entry Skip] H1 target candidate is too old for new execution. age=",
|
||||
TargetCandidateAgeText(state),
|
||||
" max_age_minutes=", input_entry_max_candidate_age_minutes,
|
||||
" candidate_at=", TimeToString(TargetCandidateReferenceTime(state), TIME_DATE | TIME_SECONDS),
|
||||
" loaded_at=", TimeToString(state.target_loaded_at, TIME_DATE | TIME_SECONDS));
|
||||
state.chk_cnt = 0;
|
||||
return false;
|
||||
@@ -113,19 +115,20 @@ bool ValidateEntryPreconditions(EAState &state)
|
||||
/**
|
||||
* @brief H1候補価格がENTRY_H1_LIMIT時間を超えて古くなっていないか判定します。
|
||||
*
|
||||
* @param state EA全体の状態。H1候補価格の読込時刻を参照します。
|
||||
* @param state EA全体の状態。H1候補価格の元になった確定足時刻を参照します。
|
||||
* @return 候補価格が期限切れの場合はtrue。
|
||||
*/
|
||||
bool IsTargetCandidateExpired(EAState &state)
|
||||
{
|
||||
if(state.target_loaded_at <= 0)
|
||||
datetime reference_time = TargetCandidateReferenceTime(state);
|
||||
if(reference_time <= 0)
|
||||
return false;
|
||||
|
||||
int expiration_seconds = ENTRY_H1_LIMIT * PeriodSeconds(PERIOD_H1);
|
||||
if(expiration_seconds <= 0)
|
||||
return false;
|
||||
|
||||
return (TimeCurrent() - state.target_loaded_at >= expiration_seconds);
|
||||
return (TimeCurrent() - reference_time >= expiration_seconds);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -134,14 +137,14 @@ bool IsTargetCandidateExpired(EAState &state)
|
||||
/**
|
||||
* @brief M15確認が遅れて整った古いH1候補で新規注文しないように判定します。
|
||||
*
|
||||
* @param state EA全体の状態。H1候補価格の読込時刻を参照します。
|
||||
* @param state EA全体の状態。H1候補価格の元になった確定足時刻を参照します。
|
||||
* @return 入力で指定した最大経過分数を超えている場合はtrue。
|
||||
*/
|
||||
bool IsTargetCandidateTooOldForExecution(EAState &state)
|
||||
{
|
||||
if(input_entry_max_candidate_age_minutes <= 0)
|
||||
return false;
|
||||
if(state.target_loaded_at <= 0)
|
||||
if(TargetCandidateReferenceTime(state) <= 0)
|
||||
return false;
|
||||
|
||||
int max_age_seconds = input_entry_max_candidate_age_minutes * 60;
|
||||
@@ -151,18 +154,33 @@ bool IsTargetCandidateTooOldForExecution(EAState &state)
|
||||
return (TargetCandidateAgeSeconds(state) > max_age_seconds);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| H1候補価格の鮮度判定に使う基準時刻を返す関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief Python結果の読込時刻ではなく、H1確定足由来の候補時刻を優先します。
|
||||
*/
|
||||
datetime TargetCandidateReferenceTime(EAState &state)
|
||||
{
|
||||
if(state.target_candidate_at > 0)
|
||||
return state.target_candidate_at;
|
||||
|
||||
return state.target_loaded_at;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| H1候補価格の経過秒数を返す関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief H1候補価格をEAへ読み込んでからの経過秒数を返します。
|
||||
* @brief H1候補価格の元になった確定足からの経過秒数を返します。
|
||||
*/
|
||||
int TargetCandidateAgeSeconds(EAState &state)
|
||||
{
|
||||
if(state.target_loaded_at <= 0)
|
||||
datetime reference_time = TargetCandidateReferenceTime(state);
|
||||
if(reference_time <= 0)
|
||||
return -1;
|
||||
|
||||
return (int)(TimeCurrent() - state.target_loaded_at);
|
||||
return (int)(TimeCurrent() - reference_time);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
@@ -214,6 +214,71 @@ bool TryParseStrictDouble(const string value, double &parsed)
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| H1候補IDを安全な12桁時刻トークンへ正規化する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief Pythonから受け取ったcandidate_idを注文コメントに使える数字だけへ丸めます。
|
||||
*/
|
||||
string NormalizeTargetCandidateId(const string value)
|
||||
{
|
||||
string text = TrimText(value);
|
||||
string output = "";
|
||||
int length = StringLen(text);
|
||||
|
||||
for(int i = 0; i < length && StringLen(output) < 12; i++)
|
||||
{
|
||||
int ch = StringGetCharacter(text, i);
|
||||
if(IsDecimalDigit(ch))
|
||||
output += StringSubstr(text, i, 1);
|
||||
}
|
||||
|
||||
if(StringLen(output) <= 0)
|
||||
return "0";
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| H1候補IDから確定足時刻を復元する関数
|
||||
//+------------------------------------------------------------------+
|
||||
/**
|
||||
* @brief `yyyyMMddHHmm` 形式のcandidate_idをdatetimeへ変換します。
|
||||
*/
|
||||
bool TryParseTargetCandidateTime(const string candidate_id, datetime &candidate_at)
|
||||
{
|
||||
candidate_at = 0;
|
||||
string text = NormalizeTargetCandidateId(candidate_id);
|
||||
if(StringLen(text) != 12)
|
||||
return false;
|
||||
|
||||
int year = (int)StringToInteger(StringSubstr(text, 0, 4));
|
||||
int month = (int)StringToInteger(StringSubstr(text, 4, 2));
|
||||
int day = (int)StringToInteger(StringSubstr(text, 6, 2));
|
||||
int hour = (int)StringToInteger(StringSubstr(text, 8, 2));
|
||||
int minute = (int)StringToInteger(StringSubstr(text, 10, 2));
|
||||
|
||||
if(year < 2000 || month < 1 || month > 12 ||
|
||||
day < 1 || day > 31 || hour < 0 || hour > 23 ||
|
||||
minute < 0 || minute > 59)
|
||||
return false;
|
||||
|
||||
string datetime_text = StringFormat("%04d.%02d.%02d %02d:%02d",
|
||||
year, month, day, hour, minute);
|
||||
datetime parsed = StringToTime(datetime_text);
|
||||
if(parsed <= 0)
|
||||
return false;
|
||||
|
||||
MqlDateTime parts;
|
||||
TimeToStruct(parsed, parts);
|
||||
if(parts.year != year || parts.mon != month || parts.day != day ||
|
||||
parts.hour != hour || parts.min != minute)
|
||||
return false;
|
||||
|
||||
candidate_at = parsed;
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| バッチファイル(Pythonスクリプト)を実行する関数
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -230,7 +295,9 @@ bool ExecuteBatchTrend()
|
||||
return false;
|
||||
|
||||
DeleteDoneFile(done_trend_file);
|
||||
return StartBatchProcess(get_trend_reply_bat, running_trend_file, "trend", g_trend_process, mt5_file_prefix);
|
||||
int price_digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
return StartBatchProcess(get_trend_reply_bat, running_trend_file, "trend",
|
||||
g_trend_process, mt5_file_prefix, price_digits);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -249,7 +316,9 @@ bool ExecuteBatchEntry()
|
||||
return false;
|
||||
|
||||
DeleteDoneFile(done_entry_file);
|
||||
return StartBatchProcess(get_entry_reply_bat, running_entry_file, "entry", g_entry_process, mt5_file_prefix);
|
||||
int price_digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
return StartBatchProcess(get_entry_reply_bat, running_entry_file, "entry",
|
||||
g_entry_process, mt5_file_prefix, price_digits);
|
||||
}
|
||||
|
||||
|
||||
@@ -504,6 +573,7 @@ void ResetTargetZones(EAState &state)
|
||||
{
|
||||
state.zone_res_chk = 0;
|
||||
state.zone_candidate_id = "0";
|
||||
state.target_candidate_at = 0;
|
||||
|
||||
for(int t = 1; t <= 4; t++)
|
||||
{
|
||||
@@ -611,7 +681,12 @@ void LoadTargetZones(EAState &state)
|
||||
|
||||
if(line_index == 3)
|
||||
{
|
||||
state.zone_candidate_id = line;
|
||||
state.zone_candidate_id = NormalizeTargetCandidateId(line);
|
||||
datetime candidate_at = 0;
|
||||
if(TryParseTargetCandidateTime(state.zone_candidate_id, candidate_at))
|
||||
state.target_candidate_at = candidate_at;
|
||||
else
|
||||
state.target_candidate_at = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -634,6 +709,7 @@ void LoadTargetZones(EAState &state)
|
||||
|
||||
Print("target_zones: res=", state.zone_res_chk,
|
||||
" id=", state.zone_candidate_id,
|
||||
" candidate_at=", TimeToString(state.target_candidate_at, TIME_DATE | TIME_MINUTES),
|
||||
" | T1 zone=", state.zone_low[1], "-", state.zone_high[1],
|
||||
" tp=", state.zone_tp[1], " sl=", state.zone_sl[1],
|
||||
" | T2 zone=", state.zone_low[2], "-", state.zone_high[2],
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user