Add regime/probability filters and fix stale-signal, restart, and cooldown gaps
OHLC EA improvements: - Add ADX regime filter (InpADXMax, default 25): block mean-reversion entries in trending markets, restoring the documented ADX gate - Require live Z-Score (>= 90% of threshold) alongside the closed-bar signal so entries no longer chase price after an intra-bar snap-back, and no instant re-entry fires after a Gravity Exit win - Add loss cooldown (InpCooldownMins, default 10): pause entries after a losing trade instead of re-entering the same extended move - Remove the soft 0.75x Z threshold that entered at weak extremes (~1.5 sigma) in stable markets; full threshold always applies - Recover open position on restart (adopt ticket + rebuild trade record) and seed Z-Score at init so a recovered position is managed instead of being orphaned or instantly closed on a bogus Z=0 - Restore the absolute spread cap (InpMaxSpreadPts was declared but unused) - Give the velocity filter a 10-second time window so slow drift across 10 ticks in quiet markets no longer false-triggers the spike block - Capture position ticket via DEAL_POSITION_ID with ordered fallbacks - Extend news load window past midnight for early-next-day events - Guard daily-loss check against zero starting balance EveryTick EA: remove dead no-op confirmation block https://claude.ai/code/session_01GPhWxikvDKmDNwQGhs6gFp
This commit is contained in:
@@ -446,21 +446,14 @@ if(baseFilters && IsNewBar())
|
||||
double maPrev[1], sdPrev[1];
|
||||
if(CopyBuffer(handleMA,0,1,1,maPrev)<1 || CopyBuffer(handleSD,0,1,1,sdPrev)<1) return;
|
||||
if(sdPrev[0] <= 0) return;
|
||||
|
||||
|
||||
double closedZ = (bid - maPrev[0]) / sdPrev[0];
|
||||
|
||||
|
||||
if(closedZ < -InpEntryZ && IsAlignedWithH1Trend(true))
|
||||
ExecuteTrade(ORDER_TYPE_BUY, ask, atr[0], closedZ);
|
||||
else if(closedZ > InpEntryZ && IsAlignedWithH1Trend(false))
|
||||
ExecuteTrade(ORDER_TYPE_SELL, bid, atr[0], closedZ);
|
||||
}
|
||||
|
||||
static double lastZ = 0;
|
||||
if(MathAbs(zScore) > InpEntryZ && MathAbs(lastZ) > InpEntryZ * 0.8) // required two ticks confirmation
|
||||
{
|
||||
// then do the entry checks
|
||||
}
|
||||
lastZ = zScore;
|
||||
}
|
||||
|
||||
Comment("--- N30 GOLD REVERSION (SIMPLE) ---\n",
|
||||
|
||||
+145
-37
@@ -23,12 +23,15 @@ input int InpStartHour = 10; // Trade window start hour
|
||||
input int InpEndHour = 20; // Trade window end hour (exclusive)
|
||||
input int InpFridayCloseHour = 20; // Friday hour to close and stop
|
||||
input int InpMaxHoldMinutes = 30; // Max trade duration in minutes
|
||||
input int InpCooldownMins = 10; // Pause after a losing trade, minutes (0 = disabled)
|
||||
input int InpMagic = 777333; // Magic number
|
||||
|
||||
//--- Inputs: Indicators
|
||||
input int InpMAPeriod = 20; // MA / StdDev period
|
||||
input int InpATRPeriod = 14; // ATR period
|
||||
input int InpFilterPeriodH1 = 50; // H1 SMA period for trend filter (0 = disabled)
|
||||
input int InpADXPeriod = 14; // ADX period
|
||||
input double InpADXMax = 25.0; // Skip entries when ADX above this (0 = disabled)
|
||||
|
||||
//--- Inputs: Execution
|
||||
input int InpSlippage = 30; // Max slippage in points
|
||||
@@ -65,11 +68,14 @@ struct TradeRecord {
|
||||
};
|
||||
|
||||
//--- Global Handles & State
|
||||
int handleMA, handleSD, handleATR, handleMA_H1;
|
||||
int handleMA, handleSD, handleATR, handleMA_H1, handleADX;
|
||||
ulong glTicket = 0; // active position ticket (0 = no open trade)
|
||||
string glPendingCloseReason = ""; // reason set by CloseAllOwnPositions, read on next tick
|
||||
TradeRecord glTradeRecord; // snapshot of the open trade
|
||||
int glTradeCount = 0; // cumulative trade counter (persists across sessions via file)
|
||||
double glZScore = 0; // Z-Score of last closed bar
|
||||
bool glZValid = false; // false until first successful Z computation (guards exits after restart)
|
||||
datetime glLossCooldownUntil = 0; // no new entries until this time after a losing trade
|
||||
double spreadBuffer[20];
|
||||
int spreadIdx = 0;
|
||||
bool spreadBufferFull = false;
|
||||
@@ -97,7 +103,8 @@ int glEntryConfirmDir = 0; // 1 = buy signal, -1 = sell signal, 0 = no
|
||||
|
||||
//--- Price Velocity filter: block entry when price moves > 25% of M1 ATR within 10 ticks
|
||||
#define VELOCITY_TICKS 10
|
||||
double glVelBuf[VELOCITY_TICKS]; // circular buffer of last 10 bid prices
|
||||
double glVelBuf[VELOCITY_TICKS]; // circular buffer of last 10 bid prices
|
||||
datetime glVelTime[VELOCITY_TICKS]; // arrival time of each buffered tick
|
||||
int glVelIdx = 0; // write index
|
||||
bool glVelBufFull = false; // true once buffer has been filled once
|
||||
datetime glVelBlockUntil = 0; // entry blocked until this server time
|
||||
@@ -129,6 +136,11 @@ int OnInit() {
|
||||
else
|
||||
handleMA_H1 = INVALID_HANDLE;
|
||||
|
||||
if(InpADXMax > 0)
|
||||
handleADX = iADX(TradeSymbol, _Period, InpADXPeriod);
|
||||
else
|
||||
handleADX = INVALID_HANDLE;
|
||||
|
||||
if(handleMA == INVALID_HANDLE || handleSD == INVALID_HANDLE ||
|
||||
handleATR == INVALID_HANDLE) {
|
||||
Print("Failed to create indicator handles");
|
||||
@@ -138,6 +150,10 @@ int OnInit() {
|
||||
Print("Failed to create H1 MA handle");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
if(InpADXMax > 0 && handleADX == INVALID_HANDLE) {
|
||||
Print("Failed to create ADX handle");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
if(InpUseNewsFilter) LoadNewsEvents();
|
||||
InitTradeLog();
|
||||
@@ -159,19 +175,69 @@ int OnInit() {
|
||||
|
||||
// Reset price velocity filter on (re)init
|
||||
ArrayInitialize(glVelBuf, 0.0);
|
||||
for(int v = 0; v < VELOCITY_TICKS; v++) glVelTime[v] = 0;
|
||||
glVelIdx = 0;
|
||||
glVelBufFull = false;
|
||||
glVelBlockUntil = 0;
|
||||
|
||||
// Seed Z-Score from the last closed bar so exits don't act on a bogus Z=0 after restart
|
||||
glZValid = false;
|
||||
UpdateZScore();
|
||||
|
||||
// Recover an open position after restart so it keeps being managed
|
||||
// (without this, an orphaned position gets no trailing/exits and a
|
||||
// fresh zScore of 0 would have instantly closed it as a false Gravity Exit)
|
||||
glTicket = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0) continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != TradeSymbol) continue;
|
||||
if(PositionGetInteger(POSITION_MAGIC) != (long)InpMagic) continue;
|
||||
|
||||
glTicket = ticket;
|
||||
glTradeRecord.ticket = ticket;
|
||||
glTradeRecord.openTime = (datetime)PositionGetInteger(POSITION_TIME);
|
||||
glTradeRecord.openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
glTradeRecord.lots = PositionGetDouble(POSITION_VOLUME);
|
||||
glTradeRecord.type = (int)PositionGetInteger(POSITION_TYPE);
|
||||
glTradeRecord.openBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
glTradeRecord.openEquity = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
glTradeRecord.zScore = 0;
|
||||
glTradeRecord.atr = 0;
|
||||
glTradeRecord.spreadAtEntry = 0;
|
||||
glTradeRecord.riskPct = 0;
|
||||
glTradeRecord.sl = PositionGetDouble(POSITION_SL);
|
||||
glTradeRecord.tp = PositionGetDouble(POSITION_TP);
|
||||
glTradeRecord.h1Status = "RECOVERED";
|
||||
glTradeRecord.comment = PositionGetString(POSITION_COMMENT);
|
||||
Print("Recovered open position after restart: ticket=", ticket);
|
||||
break;
|
||||
}
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
// UpdateZScore — Z-Score of the last closed bar (shift 1)
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateZScore() {
|
||||
double ma1[1], sd1[1];
|
||||
if(CopyBuffer(handleMA,0,1,1,ma1)>=1 && CopyBuffer(handleSD,0,1,1,sd1)>=1) {
|
||||
double close1 = iClose(TradeSymbol, _Period, 1);
|
||||
if(sd1[0] > 0.0) {
|
||||
glZScore = (close1 - ma1[0]) / sd1[0];
|
||||
glZValid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason) {
|
||||
if(handleMA != INVALID_HANDLE) IndicatorRelease(handleMA);
|
||||
if(handleSD != INVALID_HANDLE) IndicatorRelease(handleSD);
|
||||
if(handleATR != INVALID_HANDLE) IndicatorRelease(handleATR);
|
||||
if(handleMA_H1 != INVALID_HANDLE) IndicatorRelease(handleMA_H1);
|
||||
if(handleADX != INVALID_HANDLE) IndicatorRelease(handleADX);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -190,6 +256,7 @@ void CheckDailyReset() {
|
||||
bool IsDailyLossLimitHit() {
|
||||
if(!InpUseDailyLossLimit) return false;
|
||||
if(dailyLossHit) return true;
|
||||
if(dailyStartBalance <= 0) return false;
|
||||
|
||||
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
||||
double lossPercent = ((dailyStartBalance - equity) / dailyStartBalance) * 100.0;
|
||||
@@ -212,7 +279,8 @@ void LoadNewsEvents() {
|
||||
TimeToStruct(TimeCurrent(), dt);
|
||||
|
||||
datetime dayStart = TimeCurrent() - (dt.hour * 3600 + dt.min * 60 + dt.sec);
|
||||
datetime dayEnd = dayStart + 86400;
|
||||
// Extend past midnight so early-next-day events still trigger the pre-news pause
|
||||
datetime dayEnd = dayStart + 86400 + InpNewsMinsBefore * 60;
|
||||
|
||||
MqlCalendarValue values[];
|
||||
if(!CalendarValueHistory(values, dayStart, dayEnd)) return;
|
||||
@@ -437,15 +505,19 @@ void OnTick() {
|
||||
double spreadMA = (count > 0) ? (spreadSum / count) : currentSpread;
|
||||
|
||||
// --- PRICE VELOCITY FILTER: update 10-tick bid buffer ---
|
||||
glVelBuf[glVelIdx] = bid;
|
||||
glVelBuf[glVelIdx] = bid;
|
||||
glVelTime[glVelIdx] = TimeCurrent();
|
||||
glVelIdx++;
|
||||
if(glVelIdx >= VELOCITY_TICKS) { glVelIdx = 0; glVelBufFull = true; }
|
||||
if(glVelBufFull) {
|
||||
// oldest value is at current write position (about to be overwritten next tick)
|
||||
double oldest = glVelBuf[glVelIdx % VELOCITY_TICKS];
|
||||
double newest = glVelBuf[(glVelIdx + VELOCITY_TICKS - 1) % VELOCITY_TICKS];
|
||||
double velMove = MathAbs(newest - oldest);
|
||||
if(velMove > 0.25 * atr[0])
|
||||
int oldIdx = glVelIdx % VELOCITY_TICKS;
|
||||
int newIdx = (glVelIdx + VELOCITY_TICKS - 1) % VELOCITY_TICKS;
|
||||
double velMove = MathAbs(glVelBuf[newIdx] - glVelBuf[oldIdx]);
|
||||
long velSpan = (long)(glVelTime[newIdx] - glVelTime[oldIdx]);
|
||||
// Only a genuine spike counts: 10 ticks can span minutes in quiet markets,
|
||||
// and slow drift of 25% ATR over that span is normal, not a knife
|
||||
if(velSpan <= 10 && velMove > 0.25 * atr[0])
|
||||
glVelBlockUntil = TimeCurrent() + 3; // block entries for 3 seconds
|
||||
}
|
||||
|
||||
@@ -456,15 +528,14 @@ void OnTick() {
|
||||
|
||||
// --- Z-Score Calculation (Shift 1 for OHLC alignment) ---
|
||||
// We only update Z-Score once per bar to match OHLC backtest signals
|
||||
static double zScore = 0;
|
||||
if(isNewBar) {
|
||||
double ma1[1], sd1[1];
|
||||
if(CopyBuffer(handleMA,0,1,1,ma1)>=1 && CopyBuffer(handleSD,0,1,1,sd1)>=1) {
|
||||
double close1 = iClose(TradeSymbol, _Period, 1);
|
||||
zScore = (sd1[0] > 0.0) ? (close1 - ma1[0]) / sd1[0] : 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
if(isNewBar) UpdateZScore();
|
||||
double zScore = glZScore;
|
||||
|
||||
// --- Live Z-Score (current bid vs current MA/SD) ---
|
||||
// Guards against stale signals: the closed-bar Z stays extreme for the whole
|
||||
// next bar even after price has already snapped back intra-bar
|
||||
double liveZ = (sd[0] > 0.0) ? (bid - ma[0]) / sd[0] : 0.0;
|
||||
|
||||
// --- Volatility Ratio & Dynamic Z-Score Threshold ---
|
||||
double atrBuf100[100];
|
||||
double avgATR100 = 0;
|
||||
@@ -476,22 +547,36 @@ void OnTick() {
|
||||
avgATR100 = atr[0]; // fallback: ratio = 1.0 (no adjustment)
|
||||
}
|
||||
double volRatio = (avgATR100 > 0) ? (atr[0] / avgATR100) : 1.0;
|
||||
double dynamicZ = InpEntryZ + (volRatio > 1.5 ? 0.5 : 0.0);
|
||||
double softZ = InpEntryZ * 0.75;
|
||||
// Stable market (volRatio 0.7–1.1): lower soft threshold captures more standard reversions
|
||||
// Elevated volatility (volRatio > 1.2): demand full dynamicZ to avoid noise spikes
|
||||
double entryZ = (volRatio >= 0.7 && volRatio <= 1.1) ? softZ : dynamicZ;
|
||||
// Full threshold always; raised +0.5 in elevated volatility to avoid noise spikes.
|
||||
// (The old "soft" 0.75x threshold entered at Z~1.5 — too weak an extreme for
|
||||
// mean reversion, producing many low-edge trades that mostly paid spread.)
|
||||
double entryZ = InpEntryZ + (volRatio > 1.5 ? 0.5 : 0.0);
|
||||
|
||||
bool nearNews = IsNearNews();
|
||||
bool lossLimitHit = IsDailyLossLimitHit();
|
||||
bool redNewsImminent = IsRedNewsImminent();
|
||||
bool weekendRisk = IsWeekendRisk();
|
||||
|
||||
// --- Spread Check (ATR-relative only) ---
|
||||
// --- Spread Check ---
|
||||
double atrPts = atr[0] / point;
|
||||
double maxAllowedSpread = atrPts * 0.12; // block entry when spread > 12% of current M1 ATR
|
||||
bool spreadOk = (currentSpread <= maxAllowedSpread) && // ATR-relative gate
|
||||
(currentSpread <= spreadMA * 1.5); // liquidity-gap guard: no entry if spread > 1.5x 20-tick average
|
||||
(currentSpread <= spreadMA * 1.5) && // liquidity-gap guard: no entry if spread > 1.5x 20-tick average
|
||||
(currentSpread <= InpMaxSpreadPts); // absolute cap
|
||||
|
||||
// --- ADX Regime Filter: mean reversion only works in ranging markets ---
|
||||
bool adxOk = true;
|
||||
double adxVal = 0;
|
||||
if(InpADXMax > 0 && handleADX != INVALID_HANDLE) {
|
||||
double adxBuf[1];
|
||||
if(CopyBuffer(handleADX, 0, 0, 1, adxBuf) >= 1) {
|
||||
adxVal = adxBuf[0];
|
||||
adxOk = (adxVal <= InpADXMax);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Loss Cooldown: no re-entry right after a losing trade ---
|
||||
bool cooldownOk = (TimeCurrent() >= glLossCooldownUntil);
|
||||
|
||||
// --- Pre-compute filter states ---
|
||||
bool inWindow = (dt.hour >= InpStartHour && dt.hour < InpEndHour);
|
||||
@@ -545,10 +630,14 @@ void OnTick() {
|
||||
CloseAllOwnPositions("Time Exit (" + tradeDurStr + ")");
|
||||
} else {
|
||||
// Z-Score TP: Gravity Exit — exit threshold decays toward 0 as trade ages
|
||||
// glZValid guard: never act on the uninitialized Z=0 right after a restart,
|
||||
// which would instantly close a recovered position as a false reversion
|
||||
long posType = PositionGetInteger(POSITION_TYPE);
|
||||
bool zRevert = false;
|
||||
if(posType == POSITION_TYPE_BUY && zScore >= -gravityExitZ) zRevert = true;
|
||||
if(posType == POSITION_TYPE_SELL && zScore <= gravityExitZ) zRevert = true;
|
||||
if(glZValid) {
|
||||
if(posType == POSITION_TYPE_BUY && zScore >= -gravityExitZ) zRevert = true;
|
||||
if(posType == POSITION_TYPE_SELL && zScore <= gravityExitZ) zRevert = true;
|
||||
}
|
||||
|
||||
if(zRevert) {
|
||||
CloseAllOwnPositions("Gravity Exit (Z=" + DoubleToString(zScore, 2) + " tgt=" + DoubleToString(gravityExitZ, 2) + ")");
|
||||
@@ -564,13 +653,15 @@ void OnTick() {
|
||||
}
|
||||
} else { // --- ENTRY LOGIC (every tick, gated by flash-spike filter) ---
|
||||
if(CountOwnPositions() < InpMaxPositions) {
|
||||
bool baseFilters = (inWindow && spreadOk && !nearNews);
|
||||
bool baseFilters = (inWindow && spreadOk && !nearNews && adxOk && cooldownOk && glZValid);
|
||||
|
||||
// Determine current signal direction (0 = no signal)
|
||||
// Determine current signal direction (0 = no signal).
|
||||
// liveZ must still be stretched (90% of threshold): the closed-bar signal
|
||||
// alone would chase entries after the intra-bar snap-back already happened
|
||||
int signalDir = 0;
|
||||
if(baseFilters) {
|
||||
if(zScore < -entryZ && IsAlignedWithH1Trend(true)) signalDir = 1;
|
||||
else if(zScore > entryZ && IsAlignedWithH1Trend(false)) signalDir = -1;
|
||||
if(zScore < -entryZ && liveZ <= -entryZ * 0.9 && IsAlignedWithH1Trend(true)) signalDir = 1;
|
||||
else if(zScore > entryZ && liveZ >= entryZ * 0.9 && IsAlignedWithH1Trend(false)) signalDir = -1;
|
||||
}
|
||||
|
||||
// --- Confirmation gate: accumulate or reset ---
|
||||
@@ -606,10 +697,12 @@ void OnTick() {
|
||||
|
||||
Comment("--- N30 GOLD REVERSION (SIMPLE) ---\n",
|
||||
"Z-Score(1): ", DoubleToString(zScore, 2),
|
||||
" | Z-Target: ", DoubleToString(entryZ, 2),
|
||||
" (", (volRatio >= 0.7 && volRatio <= 1.1 ? "SOFT" : "FULL"), ")\n",
|
||||
" | Live Z: ", DoubleToString(liveZ, 2),
|
||||
" | Z-Target: ", DoubleToString(entryZ, 2), "\n",
|
||||
"Volatility Ratio: ", DoubleToString(volRatio, 2),
|
||||
(volRatio > 1.5 ? " [HIGH - Z raised +0.5]" : (volRatio >= 0.7 && volRatio <= 1.1 ? " [stable - soft threshold]" : " [normal]")), "\n",
|
||||
(volRatio > 1.5 ? " [HIGH - Z raised +0.5]" : " [normal]"), "\n",
|
||||
"ADX: ", DoubleToString(adxVal, 1), " / Max: ", DoubleToString(InpADXMax, 1),
|
||||
" ", (adxOk ? "OK" : "BLOCKED [trending]"), "\n",
|
||||
"H1 Trend: ", h1Status, "\n",
|
||||
"Spread: ", DoubleToString(currentSpread, 1),
|
||||
" / MaxAllowed: ", DoubleToString(maxAllowedSpread, 1),
|
||||
@@ -617,6 +710,7 @@ void OnTick() {
|
||||
"News: ", (nearNews ? "BLOCKED" : "clear"),
|
||||
(redNewsImminent ? " [CLOSING NOW]" : ""), "\n",
|
||||
"Velocity: ", (TimeCurrent() < glVelBlockUntil ? "BLOCKED (" + IntegerToString((int)(glVelBlockUntil - TimeCurrent())) + "s)" : "ok"), "\n",
|
||||
"Loss Cooldown: ", (cooldownOk ? "none" : IntegerToString((int)((glLossCooldownUntil - TimeCurrent()) / 60)) + "m remaining"), "\n",
|
||||
"Trade Duration: ", tradeDurStr);
|
||||
}
|
||||
|
||||
@@ -775,14 +869,21 @@ void ExecuteTrade(ENUM_ORDER_TYPE type, double p, double a, double zScore) {
|
||||
if(!OrderSend(req, res) || res.retcode != TRADE_RETCODE_DONE) {
|
||||
Print("Entry failed: retcode=", res.retcode, " comment=", res.comment);
|
||||
} else {
|
||||
// Capture position ticket: res.deal is the position ticket on netting accounts;
|
||||
// fall back to res.order for hedging accounts.
|
||||
glTicket = res.deal;
|
||||
bool posSelected = PositionSelectByTicket(glTicket);
|
||||
// Capture position ticket: the deal's DEAL_POSITION_ID is the position
|
||||
// ticket on both netting and hedging accounts; fall back to res.order
|
||||
// then res.deal if the deal isn't in history yet.
|
||||
glTicket = 0;
|
||||
if(res.deal > 0 && HistoryDealSelect(res.deal))
|
||||
glTicket = (ulong)HistoryDealGetInteger(res.deal, DEAL_POSITION_ID);
|
||||
bool posSelected = (glTicket != 0) && PositionSelectByTicket(glTicket);
|
||||
if(!posSelected) {
|
||||
glTicket = res.order;
|
||||
posSelected = PositionSelectByTicket(glTicket);
|
||||
}
|
||||
if(!posSelected) {
|
||||
glTicket = res.deal;
|
||||
posSelected = PositionSelectByTicket(glTicket);
|
||||
}
|
||||
// Snapshot open-trade details for the trade log
|
||||
glTradeRecord.ticket = glTicket;
|
||||
glTradeRecord.openTime = posSelected ? (datetime)PositionGetInteger(POSITION_TIME) : TimeCurrent();
|
||||
@@ -897,6 +998,13 @@ void WriteTradeResult(datetime closeTime, double closePrice,
|
||||
glTradeCount++;
|
||||
double netPnL = grossProfit + swap + commission;
|
||||
double closingBalance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
|
||||
// Loss cooldown: the frozen closed-bar Z is often still extreme right after a
|
||||
// stop-out/time-exit, so an immediate re-entry walks straight back into the move
|
||||
if(netPnL < 0 && InpCooldownMins > 0) {
|
||||
glLossCooldownUntil = TimeCurrent() + (long)InpCooldownMins * 60;
|
||||
Print("Losing trade — entry cooldown until ", TimeToString(glLossCooldownUntil, TIME_DATE|TIME_MINUTES));
|
||||
}
|
||||
long durationSec = (long)closeTime - (long)glTradeRecord.openTime;
|
||||
int durationMins = (int)(durationSec / 60);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user