//+------------------------------------------------------------------+ //| US500_H1_ArticleEA.mq5 | //| ai/yt: article-split ONNX (train 2010–2019 / OOS 2020–2024) | //| Train: python train_article_split.py → models/*.onnx | //| Attach to US500 (or broker equivalent) H1 chart. | //+------------------------------------------------------------------+ #property copyright "Profitable EA Project" #property version "1.05" #property description "Embedded US500 H1 article-split ONNX; scaler from US500_H1_article_split_meta.json" #include #resource "models\\US500_H1_article_split.onnx" as uchar ExtModel[] #define FEAT_COUNT 24 #define PRED_HIST_CAP 32 #define REL_EPS 1e-9 input group "Model" input int InpLookback = 48; input int InpEntryMode = 1; input double InpProbBuy = 0.18; input double InpProbSell = 0.18; input double InpMinBeatHold = 0.0; input int InpExitMode = 1; // 0=fixed prob; 1/2=close must beat HOLD and stay-in-trade (2 legacy; old 2 vs-HOLD-only removed) input double InpProbCloseL = 0.18; input double InpProbCloseS = 0.18; input double InpMinCloseBeatHold = 0.0; input int InpMinBarsInTradeModelExit = 1; // min bars before model exit (0=off); pure mode uses 5-class winner input bool InpPureRelative = true; // true: no prob cutoffs/edges — entry=trio strict winner, exit=5-class strict winner != side input bool InpUseCloseHeadExit = true; // legacy only when InpPureRelative=false (CL/CS vs HOLD/stay; see InpExitMode) input bool InpUseDirFlipExit = true; // legacy only when InpPureRelative=false (gap edges InpFlipExitEdge) input double InpFlipExitEdge = 0.03; // legacy dir-flip min gap (ignored when InpPureRelative) input int InpMinBarsAfterExit = 6; // after any close, wait this many flat bars before a new entry (0=off) input int InpCooldownBarsAfterAdverse = 12; // extra flat-bar pause after adverse (ATR) stop; 0 = use only MinBarsAfterExit input group "Decision (aggregate + sample, lowers trade churn)" input int InpSampleEveryNBars = 2; // run ONNX / refresh history every N new bars (>=1) input int InpAggWindow = 4; // rolling mean over last K samples (>=1) input int InpMinAggSamples = 2; // need this many samples in window before new entries input int InpMinBarsBetweenEntries = 0; // after an open, wait this many flat bars before next entry (0=off) input double InpMinDirEdge = 0.03; // legacy entry mode 1 only (ignored when InpPureRelative) input bool InpRequireStayOverClose = true; // legacy entry (ignored when InpPureRelative) input group "Session (match Python SESSION_HOUR_OFFSET)" input int InpSessionHourOffset = 0; input group "Scaler override (empty = use built-in US500 train split)" input string InpFeatMinStr = ""; input string InpFeatMaxStr = ""; input group "Risk" input double InpLotSize = 0.01; input int InpMagic = 902503; input int InpSlippage = 30; input group "Hard exits (fixed ATR in price — optional)" input bool InpUseAdverseAtrExit = false; // stop by adverse move in ATR multiples (off = model-only risk) input bool InpUseProfitAtrExit = false; // take-profit in ATR multiples (needs InpTakeProfitATR > 0) input double InpMaxAdverseATR = 3.5; input double InpTakeProfitATR = 0.0; double g_feat_min[FEAT_COUNT]; double g_feat_max[FEAT_COUNT]; CTrade trade; long g_onnx = INVALID_HANDLE; datetime g_last_bar = 0; double g_pred_hist[PRED_HIST_CAP][5]; int g_pred_hist_len = 0; double g_smooth[5] = {0.2, 0.2, 0.2, 0.2, 0.2}; ulong g_bar_index = 0; int g_entry_cooldown_bars = 0; int g_agg_w = 4; int g_sample_n = 2; int g_min_agg_samples = 2; void InitDefaultScalerBounds() { // MinMax bounds from ai/yt/models/US500_H1_article_split_meta.json (train-only scaler) double def_min[FEAT_COUNT] = { 1352.5, 1352.5999755859375, 1347.9000244140625, 1352.0999755859375, 0.0, 0.04497450217604637, -0.030356179922819138, -0.04839427396655083, 0.00028562467196024954, -0.047754231840372086, 1.0, 0.00017100000695791095, 0.0, 0.01168255414813757, 0.0760856345295906, -0.49618232250213623, -1.6348180770874023, -1.731970191001892, 0.000006116794793342706, 0.0, 0.0, 0.0, 0.0, 0.0 }; double def_max[FEAT_COUNT] = { 3250.199951171875, 3251.5, 3249.5, 3250.199951171875, 26050000896.0, 0.887104868888855, 0.09538312256336212, 0.10739167034626007, 0.02898731827735901, 0.036042287945747375, 1.0754634141921997, 6759499776.0, 20.0, 0.9637425541877747, 0.8267387747764587, 0.5573697686195374, 1.2618913650512695, 1.8784747123718262, 0.9100509881973267, 1.0, 1.0, 1.0, 1.0, 1.0 }; for(int i = 0; i < FEAT_COUNT; i++) { g_feat_min[i] = def_min[i]; g_feat_max[i] = def_max[i]; } } bool ParseFeatCsv(const string s, double &arr[]) { if(StringLen(s) < 3) return false; string parts[]; int n = StringSplit(s, ',', parts); if(n != FEAT_COUNT) return false; for(int i = 0; i < FEAT_COUNT; i++) arr[i] = StringToDouble(parts[i]); return true; } int OnInit() { InitDefaultScalerBounds(); trade.SetExpertMagicNumber(InpMagic); trade.SetDeviationInPoints(InpSlippage); trade.SetTypeFilling(ORDER_FILLING_IOC); if(StringLen(InpFeatMinStr) > 0 && ParseFeatCsv(InpFeatMinStr, g_feat_min)) Print("US500 Article EA: loaded InpFeatMinStr (24)"); if(StringLen(InpFeatMaxStr) > 0 && ParseFeatCsv(InpFeatMaxStr, g_feat_max)) Print("US500 Article EA: loaded InpFeatMaxStr (24)"); g_onnx = OnnxCreateFromBuffer(ExtModel, ONNX_DEBUG_LOGS); if(g_onnx == INVALID_HANDLE) { Print("OnnxCreateFromBuffer failed ", GetLastError()); return INIT_FAILED; } const long inShape[] = {1, InpLookback, FEAT_COUNT}; if(!OnnxSetInputShape(g_onnx, 0, inShape)) { Print("OnnxSetInputShape failed ", GetLastError()); OnnxRelease(g_onnx); return INIT_FAILED; } const long outShape[] = {1, 5}; if(!OnnxSetOutputShape(g_onnx, 0, outShape)) { Print("OnnxSetOutputShape failed ", GetLastError()); OnnxRelease(g_onnx); return INIT_FAILED; } g_agg_w = MathMax(1, MathMin(InpAggWindow, PRED_HIST_CAP)); g_sample_n = MathMax(1, InpSampleEveryNBars); g_min_agg_samples = MathMax(1, MathMin(InpMinAggSamples, g_agg_w)); g_pred_hist_len = 0; g_bar_index = 0; g_entry_cooldown_bars = 0; for(int k = 0; k < 5; k++) g_smooth[k] = 0.2; const bool has_atr = InpUseAdverseAtrExit || (InpUseProfitAtrExit && InpTakeProfitATR > 0.0); const bool has_model_exit = InpPureRelative || InpUseCloseHeadExit || InpUseDirFlipExit; if(!has_atr && !has_model_exit) Print("US500_H1_ArticleEA: WARNING — no exit path enabled (enable InpPureRelative and/or legacy exits / ATR)"); Print("US500_H1_ArticleEA: ONNX OK. Chart TF=", EnumToString(PERIOD_CURRENT), "; Lookback=", InpLookback, " sampleEvery=", g_sample_n, " aggWindow=", g_agg_w, " minAggSamples=", g_min_agg_samples, " pureRelative=", InpPureRelative, " entryCooldownBars=", InpMinBarsBetweenEntries, " minDirEdge=", InpMinDirEdge, " stayOverClose=", InpRequireStayOverClose, " exitMode=", InpExitMode, " minBarsInTradeModelExit=", InpMinBarsInTradeModelExit, " closeHeadExit=", InpUseCloseHeadExit, " dirFlipExit=", InpUseDirFlipExit, " flipExitEdge=", InpFlipExitEdge, " minBarsAfterExit=", InpMinBarsAfterExit, " cooldownAfterAdverse=", InpCooldownBarsAfterAdverse, " useAdverseATR=", InpUseAdverseAtrExit, " useProfitATR=", InpUseProfitAtrExit, " maxAdverseATR=", InpMaxAdverseATR, " takeProfitATR=", InpTakeProfitATR); return INIT_SUCCEEDED; } void OnDeinit(const int r) { if(g_onnx != INVALID_HANDLE) OnnxRelease(g_onnx); } double AtrNow() { double b[]; ArraySetAsSeries(b, true); int h = iATR(_Symbol, PERIOD_CURRENT, 14); if(h == INVALID_HANDLE) return 0; if(CopyBuffer(h, 0, 0, 2, b) < 1) { IndicatorRelease(h); return 0; } double v = b[0]; IndicatorRelease(h); return v; } bool AdverseExit(const long type, const double open_price) { if(!InpUseAdverseAtrExit || InpMaxAdverseATR <= 0.0) return false; double atr = AtrNow(); if(atr <= 0) return false; double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); if(type == POSITION_TYPE_BUY) { double adv = (open_price - bid) / atr; return adv >= InpMaxAdverseATR; } double adv = (ask - open_price) / atr; return adv >= InpMaxAdverseATR; } bool ProfitExit(const long type, const double open_price) { if(!InpUseProfitAtrExit || InpTakeProfitATR <= 0.0) return false; double atr = AtrNow(); if(atr <= 0.0) return false; double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); if(type == POSITION_TYPE_BUY) return (bid - open_price) >= InpTakeProfitATR * atr; return (open_price - ask) >= InpTakeProfitATR * atr; } bool ModelCloseLong(const double p0, const double p1, const double p3) { if(InpExitMode == 0) return (p3 >= InpProbCloseL); // Modes 1/2 (and default): close-long must beat HOLD and stay-long (BUY). Old mode-2 "vs HOLD only" fired almost every bar on softmax. return (p3 > p0 + InpMinCloseBeatHold && p3 > p1); } bool ModelCloseShort(const double p0, const double p2, const double p4) { if(InpExitMode == 0) return (p4 >= InpProbCloseS); return (p4 > p0 + InpMinCloseBeatHold && p4 > p2); } bool ModelDirFlipExitLong(const double p0, const double p1, const double p2) { if(!InpUseDirFlipExit) return false; const double e = MathMax(0.0, InpFlipExitEdge); return (p2 > p1 + e && p2 > p0 + InpMinBeatHold); } bool ModelDirFlipExitShort(const double p0, const double p1, const double p2) { if(!InpUseDirFlipExit) return false; const double e = MathMax(0.0, InpFlipExitEdge); return (p1 > p2 + e && p1 > p0 + InpMinBeatHold); } int TrioStrictWinner012(const double p0, const double p1, const double p2) { if(p0 > p1 + REL_EPS && p0 > p2 + REL_EPS) return 0; if(p1 > p0 + REL_EPS && p1 > p2 + REL_EPS) return 1; if(p2 > p0 + REL_EPS && p2 > p1 + REL_EPS) return 2; return -1; } int FiveStrictWinner01234(const double p0, const double p1, const double p2, const double p3, const double p4) { const double p[5] = {p0, p1, p2, p3, p4}; int best = 0; for(int k = 1; k < 5; k++) if(p[k] > p[best]) best = k; const double m = p[best]; int cnt = 0; for(int k = 0; k < 5; k++) if(p[k] + REL_EPS >= m) cnt++; if(cnt != 1) return -1; return best; } int PositionBarsInTrade() { if(!PositionSelect(_Symbol)) return 0; const datetime tOpen = (datetime)PositionGetInteger(POSITION_TIME); const int sh = iBarShift(_Symbol, PERIOD_CURRENT, tOpen, false); if(sh < 0) return 9999; return sh + 1; } void ApplyExitCooldown(const bool adverse_stop) { int b = MathMax(0, InpMinBarsAfterExit); if(adverse_stop) b = MathMax(b, MathMax(0, InpCooldownBarsAfterAdverse)); if(b > 0) g_entry_cooldown_bars = MathMax(g_entry_cooldown_bars, b); } void PushPrediction(const double p0, const double p1, const double p2, const double p3, const double p4, const int maxKeep) { for(int i = PRED_HIST_CAP - 1; i > 0; i--) for(int k = 0; k < 5; k++) g_pred_hist[i][k] = g_pred_hist[i - 1][k]; g_pred_hist[0][0] = p0; g_pred_hist[0][1] = p1; g_pred_hist[0][2] = p2; g_pred_hist[0][3] = p3; g_pred_hist[0][4] = p4; int cap = MathMax(1, MathMin(maxKeep, PRED_HIST_CAP)); g_pred_hist_len = MathMin(g_pred_hist_len + 1, cap); } void RecomputeSmooth(const int aggWindow) { int w = MathMax(1, MathMin(aggWindow, PRED_HIST_CAP)); int n = MathMin(w, g_pred_hist_len); if(n < 1) return; for(int k = 0; k < 5; k++) { double s = 0.0; for(int i = 0; i < n; i++) s += g_pred_hist[i][k]; g_smooth[k] = s / (double)n; } } void ScaleFeatures(const float &raw[], float &out[]) { for(int f = 0; f < FEAT_COUNT; f++) { double den = g_feat_max[f] - g_feat_min[f]; if(den < 1e-12) den = 1e-12; double x = (double)raw[f] - g_feat_min[f]; out[f] = (float)MathMax(0.0, MathMin(1.0, x / den)); } } bool PrepareMatrix(matrixf &M) { int L = InpLookback; double open[], high[], low[], close[]; long vol[]; datetime bt[]; ArraySetAsSeries(open, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(close, true); ArraySetAsSeries(vol, true); ArraySetAsSeries(bt, true); int need = L + 55; if(CopyOpen(_Symbol, PERIOD_CURRENT, 0, need, open) < L) return false; if(CopyHigh(_Symbol, PERIOD_CURRENT, 0, need, high) < L) return false; if(CopyLow(_Symbol, PERIOD_CURRENT, 0, need, low) < L) return false; if(CopyClose(_Symbol, PERIOD_CURRENT, 0, need, close) < L) return false; if(CopyTickVolume(_Symbol, PERIOD_CURRENT, 0, need, vol) < L) return false; if(CopyTime(_Symbol, PERIOD_CURRENT, 0, need, bt) < L) return false; double rsi7[], rsi14[], rsi21[], ema20[], ema50[], atr[]; ArraySetAsSeries(rsi7, true); ArraySetAsSeries(rsi14, true); ArraySetAsSeries(rsi21, true); ArraySetAsSeries(ema20, true); ArraySetAsSeries(ema50, true); ArraySetAsSeries(atr, true); int h7 = iRSI(_Symbol, PERIOD_CURRENT, 7, PRICE_CLOSE); int h14 = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); int h21 = iRSI(_Symbol, PERIOD_CURRENT, 21, PRICE_CLOSE); int hE20 = iMA(_Symbol, PERIOD_CURRENT, 20, 0, MODE_EMA, PRICE_CLOSE); int hE50 = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE); int hA = iATR(_Symbol, PERIOD_CURRENT, 14); if(h7 == INVALID_HANDLE || h14 == INVALID_HANDLE || h21 == INVALID_HANDLE || hE20 == INVALID_HANDLE || hE50 == INVALID_HANDLE || hA == INVALID_HANDLE) return false; if(CopyBuffer(h7, 0, 0, need, rsi7) < L || CopyBuffer(h14, 0, 0, need, rsi14) < L || CopyBuffer(h21, 0, 0, need, rsi21) < L || CopyBuffer(hE20, 0, 0, need, ema20) < L || CopyBuffer(hE50, 0, 0, need, ema50) < L || CopyBuffer(hA, 0, 0, need, atr) < L) { IndicatorRelease(h7); IndicatorRelease(h14); IndicatorRelease(h21); IndicatorRelease(hE20); IndicatorRelease(hE50); IndicatorRelease(hA); return false; } IndicatorRelease(h7); IndicatorRelease(h14); IndicatorRelease(h21); IndicatorRelease(hE20); IndicatorRelease(hE50); IndicatorRelease(hA); M.Resize(L, FEAT_COUNT); const double RSI_OB = 70.0; const double RSI_OS = 30.0; for(int i = 0; i < L; i++) { double vma = 0; int cnt = 0; for(int k = i; k < i + 20 && k < ArraySize(vol); k++) { vma += (double)vol[k]; cnt++; } if(cnt < 1) cnt = 1; vma /= cnt; double r0 = rsi14[i]; double r1 = (i + 1 < ArraySize(rsi14)) ? rsi14[i + 1] : r0; double r2 = (i + 2 < ArraySize(rsi14)) ? rsi14[i + 2] : r1; double rv7 = rsi7[i]; double rv21 = rsi21[i]; double spread = (r0 - rv7) / 50.0; if(spread > 1.0) spread = 1.0; if(spread < -1.0) spread = -1.0; double vel = (r0 - r1) / 25.0; double acc = ((r0 - r1) - (r1 - r2)) / 25.0; double dist_mid = MathAbs(r0 - 50.0) / 50.0; double c_ob = (r1 < RSI_OB && r0 >= RSI_OB) ? 1.0 : 0.0; double c_os = (r1 > RSI_OS && r0 <= RSI_OS) ? 1.0 : 0.0; double c50u = (r1 < 50.0 && r0 >= 50.0) ? 1.0 : 0.0; double c50d = (r1 > 50.0 && r0 <= 50.0) ? 1.0 : 0.0; MqlDateTime st; TimeToStruct(bt[i], st); int hr = (st.hour + InpSessionHourOffset) % 24; if(hr < 0) hr += 24; double asian = (hr >= 0 && hr < 8) ? 1.0 : 0.0; float raw[FEAT_COUNT]; raw[0] = (float)open[i]; raw[1] = (float)high[i]; raw[2] = (float)low[i]; raw[3] = (float)close[i]; raw[4] = (float)((double)vol[i] / 1000000.0); raw[5] = (float)(r0 / 100.0); raw[6] = (float)((ema20[i] - close[i]) / close[i]); raw[7] = (float)((ema50[i] - close[i]) / close[i]); raw[8] = (float)(atr[i] / close[i]); double pc = (i < L - 1) ? (close[i] - close[i + 1]) / close[i + 1] : 0.0; raw[9] = (float)pc; raw[10] = (float)(high[i] / low[i]); raw[11] = (float)(vma / 1000000.0); raw[12] = (float)(vma > 0 ? (double)vol[i] / vma : 1.0); raw[13] = (float)(rv7 / 100.0); raw[14] = (float)(rv21 / 100.0); raw[15] = (float)spread; raw[16] = (float)vel; raw[17] = (float)acc; raw[18] = (float)dist_mid; raw[19] = (float)c_ob; raw[20] = (float)c_os; raw[21] = (float)c50u; raw[22] = (float)c50d; raw[23] = (float)asian; float sc[FEAT_COUNT]; ScaleFeatures(raw, sc); for(int j = 0; j < FEAT_COUNT; j++) M[i][j] = sc[j]; } return true; } void OnTick() { datetime t = iTime(_Symbol, PERIOD_CURRENT, 0); if(t == g_last_bar) return; g_last_bar = t; const bool had_pos = PositionSelect(_Symbol); const bool flat = !had_pos; if(flat && g_entry_cooldown_bars > 0) g_entry_cooldown_bars--; g_bar_index++; const bool do_sample = (g_sample_n < 2) || ((g_bar_index % (ulong)g_sample_n) == 0); bool fresh_predict = false; if(do_sample) { matrixf Min; if(!PrepareMatrix(Min)) { Print("US500 Article EA: PrepareMatrix failed"); if(!had_pos) return; } else { vectorf out; out.Resize(5); if(!OnnxRun(g_onnx, ONNX_NO_CONVERSION, Min, out)) { Print("OnnxRun failed ", GetLastError()); if(!had_pos) return; } else { PushPrediction(out[0], out[1], out[2], out[3], out[4], g_agg_w); RecomputeSmooth(g_agg_w); fresh_predict = true; Print("US500 Article H1 raw HOLD=", out[0], " BUY=", out[1], " SELL=", out[2], " CL=", out[3], " CS=", out[4], " | smooth HOLD=", g_smooth[0], " BUY=", g_smooth[1], " SELL=", g_smooth[2], " CL=", g_smooth[3], " CS=", g_smooth[4]); } } } const double p0 = g_smooth[0]; const double p1 = g_smooth[1]; const double p2 = g_smooth[2]; const double p3 = g_smooth[3]; const double p4 = g_smooth[4]; if(flat) { if(!do_sample || !fresh_predict) return; if(g_pred_hist_len < g_min_agg_samples) return; if(g_entry_cooldown_bars > 0) return; if(InpEntryMode == 1) { if(InpPureRelative) { const int w3 = TrioStrictWinner012(p0, p1, p2); if(w3 == 1) { if(trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "US500 article BUY")) g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries); } else if(w3 == 2) { if(trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "US500 article SELL")) g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries); } } else { double dir = MathMax(p1, p2); if(dir <= p0 + InpMinBeatHold) return; const double edge = MathMax(0.0, InpMinDirEdge); const bool stay_ok_buy = (!InpRequireStayOverClose) || (p1 > p3); const bool stay_ok_sell = (!InpRequireStayOverClose) || (p2 > p4); if(p1 >= p2 && p1 > p0 + InpMinBeatHold && (p1 - p2) >= edge && stay_ok_buy) { if(trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "US500 article BUY")) g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries); } else if(p2 > p1 && p2 > p0 + InpMinBeatHold && (p2 - p1) >= edge && stay_ok_sell) { if(trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "US500 article SELL")) g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries); } } } else { if(p1 >= InpProbBuy && p1 >= p2) { if(trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "US500 article BUY")) g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries); } else if(p2 >= InpProbSell && p2 > p1) { if(trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "US500 article SELL")) g_entry_cooldown_bars = MathMax(0, InpMinBarsBetweenEntries); } } return; } long typ = (long)PositionGetInteger(POSITION_TYPE); double opn = PositionGetDouble(POSITION_PRICE_OPEN); if(AdverseExit(typ, opn)) { if(trade.PositionClose(_Symbol)) ApplyExitCooldown(true); return; } if(ProfitExit(typ, opn)) { if(trade.PositionClose(_Symbol)) ApplyExitCooldown(false); return; } const int bars_in = PositionBarsInTrade(); const bool allow_model_exit = (InpMinBarsInTradeModelExit <= 0) || (bars_in >= InpMinBarsInTradeModelExit); if(allow_model_exit) { bool want_close = false; if(InpPureRelative) { const int w5 = FiveStrictWinner01234(p0, p1, p2, p3, p4); if(typ == POSITION_TYPE_BUY) want_close = (w5 != -1 && w5 != 1); else want_close = (w5 != -1 && w5 != 2); } else { if(typ == POSITION_TYPE_BUY) { const bool head = InpUseCloseHeadExit && ModelCloseLong(p0, p1, p3); const bool flip = ModelDirFlipExitLong(p0, p1, p2); want_close = (head || flip); } else { const bool head = InpUseCloseHeadExit && ModelCloseShort(p0, p2, p4); const bool flip = ModelDirFlipExitShort(p0, p1, p2); want_close = (head || flip); } } if(want_close) { if(trade.PositionClose(_Symbol)) ApplyExitCooldown(false); } } }