- Replaced standard library signal modules with custom implementations to allow for named patterns and improved voting. - Added new input parameters for module weights, allowing for optimization of individual signal contributions. - Enhanced the management of trades with new options for breakeven and management cut. - Introduced a mechanism for dynamic ranking of signal weights based on historical performance. - Improved initialization logic to ensure proper registration of filters and handling of trading conditions. - Added detailed logging for trading permissions and account status during initialization.
457 lines
21 KiB
MQL5
457 lines
21 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| SignalDipBuy.mqh |
|
|
//| AnimateDread |
|
|
//| |
|
|
//| THE ONE EDGE THE DATA HAS: buy the high-volatility low on a US |
|
|
//| equity index at the daily close, hold about a week. |
|
|
//| |
|
|
//| MEASURED, not authored. The honest daily state scan (states x |
|
|
//| hold, picked in-sample <2019, pooled out-of-sample against the |
|
|
//| instrument's own unconditional drift, cost and swap charged) was |
|
|
//| run on every instrument with data - ten Dukascopy series and the |
|
|
//| broker's own index history. One cell family survived, and it is |
|
|
//| the same on all three US indices: |
|
|
//| SP500 posxvol=0x3 L5 +103 bp OOS (t 3.1) +17..+24 over drift|
|
|
//| NAS100 r20xrsi=0x0 L5 +109 bp OOS (t 3.7) +11 over drift |
|
|
//| US30 r20xrsi=0x0 L5 +102 bp OOS (t 3.1) +18 over drift |
|
|
//| 20-day range LOW x high volatility, or bottom-quintile 20-day |
|
|
//| return x lowest RSI(2): long, hold five days. The named form is |
|
|
//| Connors' RSI(2)<10 above the 200-day, which the 176-cell swing |
|
|
//| screen found flat across its whole neighbourhood (thresholds |
|
|
//| 5-25, every exit, with or without the stop: all positive). |
|
|
//| |
|
|
//| WHERE IT DOES NOT WORK, so nobody points it there: DAX40 and |
|
|
//| UK100 (picks LOSE out of sample), every forex pair (null on all |
|
|
//| five), gold (drift, not selection). US indices, long only. A long |
|
|
//| bias on an index is a property of the instrument, not a flaw. |
|
|
//| |
|
|
//| HOW IT SHOULD BE RUN. This is not a vote member. At thirteen |
|
|
//| voters one filter at 100 scores 7.7%, below any threshold, so it |
|
|
//| must be the ONLY registered module (UseClassics, UseWyckoff, |
|
|
//| UseNeural, UseRegime all off) with Direction = DIR_LONG. Its |
|
|
//| ShortCondition is the EXIT ("close above the short average"), |
|
|
//| reached through ThresholdClose - it never opens a short. |
|
|
//| |
|
|
//| THE STOP MUST BE WIDE. Measured: a 0.5-ATR stop destroyed the |
|
|
//| edge (+2.8 bp, 101 of 152 stopped) - the dip runs THROUGH a tight |
|
|
//| stop before it reverts. 2-3 ATR kept it. StopMode SL_ATR_x3, |
|
|
//| TargetMode TP_NONE: the exit is the signal, not a price. |
|
|
//| |
|
|
//| REPLICATED ON THIS EA (2026-09-13, module alone, long only, 3-ATR |
|
|
//| stop, exit = signal, costs in), per trade in bp of notional: |
|
|
//| 2012-2026 SP500 +34.7 (n 84, 76% won) NAS100 +37.2 (145, 62%) |
|
|
//| US30 +22.8 (n 84, 68%) pooled ~+33 on 313, 3/3 |
|
|
//| 2019-2026 SP500 +46.2 (n 73, PF 2.32) NAS100 +41.4 (67, 1.56) |
|
|
//| US30 +25.0 (n 72, PF 1.37) - out of sample only |
|
|
//| The broker's SP500/US30 history is daily-only before 2017 (one |
|
|
//| tick per day, stamped in the shut hour), so those years cannot |
|
|
//| fill in the tester; the effective sample there starts 2018. |
|
|
//| |
|
|
//| CEILING, stated so it is not oversold: ~9 trades/yr/index, ~4%/yr |
|
|
//| on notional unlevered; the three-index basket at 1% risk is |
|
|
//| CAGR +2.6% at 10.6% max drawdown, and the losing years (2018, |
|
|
//| 2020, 2022) are the bear years on ALL THREE at once. Real, small, |
|
|
//| correlated. Size for the 5% account rule accordingly. |
|
|
//| |
|
|
//| THE SECOND FORM OF THE SAME DIP (2026-09-13, research/style_scan |
|
|
//| .py): seven strategy styles x 13 instruments x D1 and H4, honest |
|
|
//| protocol. Only MEAN REVERSION, LONG, on equity indices survived - |
|
|
//| and its deepest form is a z-score, not RSI(2): close more than z |
|
|
//| standard deviations below its 20-bar mean, exit at that mean or |
|
|
//| after 10 bars. On H4 SP500, 2019-2026, z<-1.5 made +22.7 bp per |
|
|
//| trade net (n 1,428, t 5.4) on Dukascopy and +18.5 (n 1,415, t 4.4)|
|
|
//| on the broker's own bars - the same variant on two data sources - |
|
|
//| at ~15 trades a month, which the RSI(2) daily form (8-14 a YEAR) |
|
|
//| never could. In-sample (<2019) it was positive but weak (+8.8), |
|
|
//| so it is an era-strengthened claim, stated as such. The z-score |
|
|
//| needs no trend filter in the scan (TrendPeriod 0 switches it off).|
|
|
//| WARRIOR_DIP_ENTRY lives in Enumerations\WarriorEnums.mqh. |
|
|
//| |
|
|
//| THE META-LABEL (System\DipMeta.mqh). With MetaCut above 0.50 the |
|
|
//| module asks an ALGLIB forest + MLP whether THIS dip pays before |
|
|
//| voting. The models are fitted here, in the terminal, on every |
|
|
//| past dip this same rule found and on what this same exit made of |
|
|
//| it - a simulation on closed bars, so the label is exactly the |
|
|
//| trade the EA would have taken. Refitted every MetaRefit bars from |
|
|
//| history-so-far: walk-forward, never saved. A skipped dip is still |
|
|
//| NAMED (Pattern_0) so the journal records its outcome too. |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef WARRIOR_SIGNALDIPBUY_MQH
|
|
#define WARRIOR_SIGNALDIPBUY_MQH
|
|
|
|
#include "..\Expert\WarriorSignal.mqh"
|
|
#include "..\Enumerations\WarriorEnums.mqh"
|
|
#include "..\System\DipMeta.mqh"
|
|
|
|
class CSignalDipBuy : public CWarriorSignal
|
|
{
|
|
protected:
|
|
CiRSI m_rsi; // RSI(2) on close
|
|
int m_rsiPeriod;
|
|
double m_rsiEntry; // RSI(2) at or below this = a dip
|
|
int m_trendPeriod; // the long-term filter: close above this SMA (0 = off)
|
|
int m_exitPeriod; // exit when close crosses above this SMA
|
|
int m_maxBars; // time stop, in bars held
|
|
WARRIOR_DIP_ENTRY m_entry; // which dip
|
|
double m_zEntry; // z-score depth for DIP_ZSCORE
|
|
int m_zPeriod; // the mean and deviation window for the z-score
|
|
double m_stopAtr; // the stop the EA places, for the label simulation
|
|
int m_pattern_0; // the dip entry
|
|
int m_pattern_1; // the exit (voted as a "short" so ThresholdClose can act)
|
|
//--- THE META-LABEL
|
|
CDipMeta m_meta;
|
|
double m_metaCut; // <= 0.5 = off
|
|
int m_metaRefit; // refit every N bars
|
|
int m_metaMinBars; // history before the first attempt
|
|
int m_metaTrainedAtBars;
|
|
datetime m_metaLastTry;
|
|
int m_metaFits;
|
|
int m_metaAsked, m_metaSkipped;
|
|
double m_metaLastP;
|
|
|
|
double Sma(const int shift, const int period) const;
|
|
double ZScore(const int shift, const int period) const;
|
|
double Atr(const int shift, const int period) const;
|
|
int BarsHeld(void) const;
|
|
bool DipAt(const int shift) const;
|
|
bool BuildFeatures(double &x[], const int shift) const;
|
|
bool LabelAt(const int shift, double &label) const;
|
|
void MetaTrainIfDue(void);
|
|
bool MetaTrainFromHistory(void);
|
|
|
|
public:
|
|
CSignalDipBuy(void);
|
|
~CSignalDipBuy(void);
|
|
|
|
void RsiEntry(const double v) { m_rsiEntry = v; }
|
|
void ExitPeriod(const int v) { m_exitPeriod = v; }
|
|
void MaxBars(const int v) { m_maxBars = v; }
|
|
void TrendPeriod(const int v) { m_trendPeriod = v; }
|
|
void Entry(const WARRIOR_DIP_ENTRY e) { m_entry = e; }
|
|
void ZEntry(const double v) { m_zEntry = v; }
|
|
void StopAtr(const double v) { m_stopAtr = v; }
|
|
void MetaCut(const double v) { m_metaCut = v; }
|
|
void MetaRefit(const int v) { m_metaRefit = v; }
|
|
void Pattern_0(const int v) { m_pattern_0 = v; }
|
|
void Pattern_1(const int v) { m_pattern_1 = v; }
|
|
virtual void ApplyPatternWeight(int pattern, int weight)
|
|
{
|
|
if(pattern == 0) m_pattern_0 = weight;
|
|
if(pattern == 1) m_pattern_1 = weight;
|
|
}
|
|
virtual bool ValidationSettings(void) override;
|
|
virtual bool InitIndicators(CIndicators *indicators) override;
|
|
virtual int LongCondition(void) override;
|
|
virtual int ShortCondition(void) override;
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
CSignalDipBuy::CSignalDipBuy(void) : m_rsiPeriod(2), m_rsiEntry(10.0), m_trendPeriod(200),
|
|
m_exitPeriod(5), m_maxBars(10),
|
|
m_entry(DIP_RSI2), m_zEntry(1.5), m_zPeriod(20), m_stopAtr(3.0),
|
|
m_pattern_0(100), m_pattern_1(100),
|
|
m_metaCut(0.0), m_metaRefit(500), m_metaMinBars(400),
|
|
m_metaTrainedAtBars(0), m_metaLastTry(0), m_metaFits(0),
|
|
m_metaAsked(0), m_metaSkipped(0), m_metaLastP(-1.0)
|
|
{
|
|
m_id = "DIPBUY";
|
|
m_pattern_count = 2;
|
|
m_used_series = USE_SERIES_OPEN + USE_SERIES_HIGH + USE_SERIES_LOW + USE_SERIES_CLOSE + USE_SERIES_TIME;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
CSignalDipBuy::~CSignalDipBuy(void)
|
|
{
|
|
if(m_metaCut > 0.5)
|
|
PrintFormat("CSignalDipBuy: META - %d fit(s); asked %d time(s), skipped %d (%.0f%%); last forest AUC %.3f, MLP AUC %.3f.",
|
|
m_metaFits, m_metaAsked, m_metaSkipped,
|
|
(m_metaAsked > 0 ? 100.0 * m_metaSkipped / m_metaAsked : 0.0),
|
|
m_meta.ForestAUC(), m_meta.MlpAUC());
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
bool CSignalDipBuy::ValidationSettings(void)
|
|
{
|
|
if(!CWarriorSignal::ValidationSettings())
|
|
return false;
|
|
if(m_rsiEntry <= 0.0 || m_rsiEntry >= 50.0 || m_exitPeriod < 2 ||
|
|
(m_trendPeriod != 0 && m_trendPeriod < 50) || m_zEntry < 0.5 || m_zEntry > 4.0 || m_maxBars < 1)
|
|
{
|
|
Print("CSignalDipBuy: settings outside the measured neighbourhood (RSI 5-25, z 1.5-2.5, exit sma5..sma20, trend 0 or >=50)");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
bool CSignalDipBuy::InitIndicators(CIndicators *indicators)
|
|
{
|
|
if(indicators == NULL || !CWarriorSignal::InitIndicators(indicators))
|
|
return false;
|
|
if(!indicators.Add(GetPointer(m_rsi)) ||
|
|
!m_rsi.Create(m_symbol.Name(), m_period, m_rsiPeriod, PRICE_CLOSE))
|
|
{
|
|
Print("CSignalDipBuy: could not create RSI");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
double CSignalDipBuy::Sma(const int shift, const int period) const
|
|
{
|
|
double s = 0.0;
|
|
for(int i = 0; i < period; i++)
|
|
s += Close(shift + i);
|
|
return s / period;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| (close - mean) / standard deviation over `period` closed bars, |
|
|
//| the population deviation as the scan computed it. |
|
|
//+------------------------------------------------------------------+
|
|
double CSignalDipBuy::ZScore(const int shift, const int period) const
|
|
{
|
|
double s = 0.0, s2 = 0.0;
|
|
for(int i = 0; i < period; i++)
|
|
{
|
|
const double c = Close(shift + i);
|
|
if(c <= 0.0)
|
|
return 0.0; // past the series buffer: no dip, not a deep one
|
|
s += c; s2 += c * c;
|
|
}
|
|
const double mean = s / period;
|
|
const double var = s2 / period - mean * mean;
|
|
if(var <= 0.0)
|
|
return 0.0;
|
|
return (Close(shift) - mean) / MathSqrt(var);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Average true range over `period` bars from `shift` - the same |
|
|
//| arithmetic as iATR, so the simulated stop is the one the vote |
|
|
//| places. |
|
|
//+------------------------------------------------------------------+
|
|
double CSignalDipBuy::Atr(const int shift, const int period) const
|
|
{
|
|
double s = 0.0;
|
|
for(int i = 0; i < period; i++)
|
|
{
|
|
const int b = shift + i;
|
|
const double pc = Close(b + 1);
|
|
double tr = High(b) - Low(b);
|
|
tr = MathMax(tr, MathAbs(High(b) - pc));
|
|
tr = MathMax(tr, MathAbs(Low(b) - pc));
|
|
s += tr;
|
|
}
|
|
return s / period;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
int CSignalDipBuy::BarsHeld(void) const
|
|
{
|
|
if(!PositionSelect(m_symbol.Name()))
|
|
return 0;
|
|
return iBarShift(m_symbol.Name(), m_period, (datetime)PositionGetInteger(POSITION_TIME));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The dip rule at any closed bar, so history is judged by exactly |
|
|
//| the rule that fires live. RSI is read from the indicator buffer, |
|
|
//| which is deepened with the prices. |
|
|
//+------------------------------------------------------------------+
|
|
bool CSignalDipBuy::DipAt(const int shift) const
|
|
{
|
|
bool dip = false;
|
|
if(m_entry == DIP_ZSCORE)
|
|
dip = (ZScore(shift, m_zPeriod) <= -m_zEntry);
|
|
else
|
|
{
|
|
const double rsi = m_rsi.Main(shift);
|
|
if(rsi == EMPTY_VALUE || !MathIsValidNumber(rsi))
|
|
return false;
|
|
dip = (rsi <= m_rsiEntry);
|
|
}
|
|
//--- A trend average that reaches past the buffer averages in zeros and reads as "above".
|
|
const bool trendOk = (m_trendPeriod <= 0) ||
|
|
(Close(shift + m_trendPeriod) > 0.0 && Close(shift) > Sma(shift, m_trendPeriod));
|
|
return dip && trendOk;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The state at the signal bar - nothing after it is read. |
|
|
//+------------------------------------------------------------------+
|
|
bool CSignalDipBuy::BuildFeatures(double &x[], const int shift) const
|
|
{
|
|
ArrayResize(x, DIPMETA_FEATURES);
|
|
const double atr = Atr(shift, 14);
|
|
if(atr <= 0.0 || !MathIsValidNumber(atr))
|
|
return false;
|
|
const double atr100 = Atr(shift, 100);
|
|
const double c = Close(shift), o = Open(shift), h = High(shift), l = Low(shift);
|
|
double hi20 = h, lo20 = l;
|
|
for(int i = 1; i < 20; i++)
|
|
{
|
|
hi20 = MathMax(hi20, High(shift + i));
|
|
lo20 = MathMin(lo20, Low(shift + i));
|
|
}
|
|
int streak = 0;
|
|
for(int i = 0; i < 10; i++)
|
|
{
|
|
if(Close(shift + i) < Close(shift + i + 1)) streak++;
|
|
else break;
|
|
}
|
|
MqlDateTime t;
|
|
TimeToStruct(Time(shift), t);
|
|
x[0] = ZScore(shift, m_zPeriod);
|
|
x[1] = (c - Sma(shift, 200)) / atr;
|
|
x[2] = (c - Close(shift + 1)) / atr;
|
|
x[3] = (c - Close(shift + 5)) / atr;
|
|
x[4] = streak;
|
|
x[5] = (hi20 > lo20) ? (c - lo20) / (hi20 - lo20) : 0.5;
|
|
x[6] = (h - l) / atr;
|
|
x[7] = (h > l) ? (c - l) / (h - l) : 0.5;
|
|
x[8] = (o - Close(shift + 1)) / atr;
|
|
x[9] = (atr100 > 0.0) ? atr / atr100 : 1.0;
|
|
x[10] = EfficiencyRatio(shift, 20);
|
|
x[11] = VarianceRatio(shift, 60, 5);
|
|
x[12] = RegimeCode(shift);
|
|
x[13] = t.day_of_week / 6.0;
|
|
x[14] = t.hour / 24.0;
|
|
for(int f = 0; f < DIPMETA_FEATURES; f++)
|
|
if(!MathIsValidNumber(x[f]))
|
|
return false;
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| WHAT THIS MODULE'S OWN EXIT MADE OF THE DIP AT `shift`: fill at |
|
|
//| the next bar's open; a stop m_stopAtr ATR below it, filled at the |
|
|
//| stop; else the first close above the exit average, filled at the |
|
|
//| open after it (the vote reads the closed bar and acts on the next |
|
|
//| tick); else the time stop, filled the same way. 1 = it paid. |
|
|
//+------------------------------------------------------------------+
|
|
bool CSignalDipBuy::LabelAt(const int shift, double &label) const
|
|
{
|
|
if(shift < m_maxBars + 2)
|
|
return false; // the outcome is not fully on closed bars yet
|
|
const double atr = Atr(shift, 14);
|
|
if(atr <= 0.0)
|
|
return false;
|
|
const double fill = Open(shift - 1);
|
|
const double stop = fill - m_stopAtr * atr;
|
|
double exitPx = 0.0;
|
|
for(int k = 1; k <= m_maxBars; k++)
|
|
{
|
|
const int b = shift - k;
|
|
if(m_stopAtr > 0.0 && Low(b) <= stop)
|
|
{ exitPx = stop; break; }
|
|
if(Close(b) > Sma(b, m_exitPeriod) || k == m_maxBars)
|
|
{ exitPx = Open(b - 1); break; }
|
|
}
|
|
if(exitPx <= 0.0)
|
|
return false;
|
|
label = (exitPx > fill) ? 1.0 : 0.0;
|
|
return true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Rebuild the rows from history-so-far and refit. Bars() in the |
|
|
//| tester is history accrued to the bar being decided, so the fit |
|
|
//| can only ever have seen the past. |
|
|
//+------------------------------------------------------------------+
|
|
bool CSignalDipBuy::MetaTrainFromHistory(void)
|
|
{
|
|
const int bars = Bars(m_symbol.Name(), m_period);
|
|
const int first = 310; // ATR100 + SMA200 + the label's own window, with margin
|
|
const int last = bars - 2;
|
|
if(last - first < 100)
|
|
return false;
|
|
const int want = MathMin(bars, WARRIOR_NET_HISTORY);
|
|
const bool deepRsi = (want <= m_rsi.BufferSize()) || m_rsi.BufferResize(want);
|
|
if(!DeepenPrices(want) || !deepRsi)
|
|
{
|
|
PrintFormat("CSignalDipBuy: could not deepen series to %d bar(s) - meta not trained.", want);
|
|
return false;
|
|
}
|
|
CMatrixDouble xy(last - first + 1, DIPMETA_FEATURES + 1);
|
|
int rows = 0, dips = 0;
|
|
double x[], label;
|
|
for(int s = last; s >= first; s--) // oldest to newest: the split is chronological
|
|
{
|
|
if(!DipAt(s))
|
|
continue;
|
|
dips++;
|
|
if(!BuildFeatures(x, s) || !LabelAt(s, label))
|
|
continue;
|
|
for(int f = 0; f < DIPMETA_FEATURES; f++)
|
|
xy.Set(rows, f, x[f]);
|
|
xy.Set(rows, DIPMETA_FEATURES, label);
|
|
rows++;
|
|
}
|
|
PrintFormat("CSignalDipBuy: META fit #%d at %d bars - %d dip(s) in history, %d labelled row(s).",
|
|
m_metaFits + 1, bars, dips, rows);
|
|
//--- Embargo = the label's window: the last training dips share their outcome bars with the
|
|
//--- first validation dips. Dips cluster, so two horizons rather than one.
|
|
return m_meta.Train(xy, rows, 2 * (m_maxBars + 2));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
void CSignalDipBuy::MetaTrainIfDue(void)
|
|
{
|
|
if(m_metaCut <= 0.5)
|
|
return;
|
|
const datetime bar = iTime(m_symbol.Name(), m_period, 0);
|
|
if(bar == m_metaLastTry)
|
|
return;
|
|
const int bars = Bars(m_symbol.Name(), m_period);
|
|
if(bars < m_metaMinBars)
|
|
return;
|
|
if(m_metaTrainedAtBars > 0 && (m_metaRefit <= 0 || bars < m_metaTrainedAtBars + m_metaRefit))
|
|
return;
|
|
m_metaLastTry = bar;
|
|
m_metaTrainedAtBars = bars; // back off whether it succeeds or fails
|
|
if(MetaTrainFromHistory())
|
|
m_metaFits++;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| THE DIP: evaluated on the closed bar, acted on at the next open - |
|
|
//| exactly the "fill at next open" the screen priced. With the meta |
|
|
//| on, a dip the model rates below the cut is named but not voted. |
|
|
//+------------------------------------------------------------------+
|
|
int CSignalDipBuy::LongCondition(void)
|
|
{
|
|
MetaTrainIfDue();
|
|
const int idx = StartIndex();
|
|
if(!DipAt(idx))
|
|
return 0;
|
|
m_active_pattern = "Pattern_0";
|
|
m_active_direction = "Buy";
|
|
if(m_metaCut > 0.5 && m_meta.Ready())
|
|
{
|
|
double x[];
|
|
if(BuildFeatures(x, idx))
|
|
{
|
|
m_metaLastP = m_meta.Score(x);
|
|
if(m_metaLastP >= 0.0)
|
|
{
|
|
m_metaAsked++;
|
|
if(m_metaLastP < m_metaCut)
|
|
{
|
|
m_metaSkipped++;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return m_pattern_0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| THE EXIT, expressed as a short vote so the standard library's |
|
|
//| CheckCloseLong() (-direction >= ThresholdClose) can act on it. |
|
|
//| With Direction = DIR_LONG it can never OPEN a short. Two ways |
|
|
//| out: the close crosses back above the short average - the dip |
|
|
//| has reverted - or the time stop, because a dip that has not |
|
|
//| reverted in ten bars is not the setup that was measured. |
|
|
//+------------------------------------------------------------------+
|
|
int CSignalDipBuy::ShortCondition(void)
|
|
{
|
|
const int idx = StartIndex();
|
|
const bool reverted = (Close(idx) > Sma(idx, m_exitPeriod));
|
|
const bool timedOut = (m_maxBars > 0 && BarsHeld() >= m_maxBars);
|
|
if(reverted || timedOut)
|
|
{
|
|
m_active_pattern = "Pattern_1";
|
|
m_active_direction = "Sell";
|
|
return m_pattern_1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif // WARRIOR_SIGNALDIPBUY_MQH
|