Files
zhutoutoutousan 605faf5310 Prepare source-only public release for develop.
Add cluster audit pipeline, united EA updates, brochure generators, and publication hygiene (gitignore, MT5 path desensitization, pre-upload scan). Remove tracked reports, models, and binary artifacts from the repo.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 15:03:43 +02:00

701 lines
21 KiB
Plaintext

//+------------------------------------------------------------------+
//| USDCHF Playbook — six behavioral rules (momentum, traps, zones) |
//+------------------------------------------------------------------+
#property copyright "lab/USDCHF"
#property version "1.20"
#property strict
#include <Trade/Trade.mqh>
input group "=== Symbol / TF ==="
input ENUM_TIMEFRAMES Timeframe = PERIOD_M15;
input ENUM_TIMEFRAMES HtfTimeframe = PERIOD_H4;
input ENUM_TIMEFRAMES DailyTimeframe = PERIOD_D1;
input int MagicNumber = 20260625;
input group "=== Daily swing bias (rule 6) ==="
input bool UseDailyBias = true;
input int DailyEmaPeriod = 50;
input group "=== HTF zones (rule 3) ==="
input int HtfZoneBars = 20;
input double MinBreakBodyRatio = 0.55;
input group "=== Double trap (rule 2) ==="
input bool UseDoubleTrap = true;
input group "=== Session (rules 1 & 4) ==="
input int NyChaosStartHour = 12;
input int NyChaosEndHour = 15;
input int MomentumStartHour = 15;
input int MomentumEndHour = 2;
input group "=== LTF entry ==="
input int LtfFastEma = 8;
input int LtfSlowEma = 21;
input int EntryMode = 1; // 0=h4 break 1=mixed 2=trap 3=LTF momentum
input bool AllowLtfPullback = true;
input bool AllowEmaCross = true;
input double MinEmaGapPips = 0.5;
input group "=== Risk ==="
input double LotSize = 0.10;
input int AtrPeriod = 14;
input double AtrSlMult = 1.8;
input double AtrTpMult = 4.0;
input bool UseTrailing = true;
input double TrailAtrMult = 1.2;
input int MaxBarsInTrade = 96;
input bool ExtendHoldMomentum = true;
input int CooldownBars = 1;
input int MaxSpreadPips = 8;
input group "=== News compression (rule 5) ==="
input bool UseCompressionFilter = true;
input double CompressAtrRatio = 0.70;
input int CompressLookback = 48;
input bool UseMomentumWindow = true;
input group "=== Combo modules (组合逻辑) ==="
input int ComboMode = 0; // 0=任一触发 1=主信号+确认 2=评分达标
input int MinComboScore = 2; // ComboMode=2 时最少几分
input bool AllowRsiPullback = true;
input int RsiPeriod = 14;
input double RsiBuyZone = 42.0;
input double RsiSellZone = 58.0;
input bool AllowMacdMomentum = true;
input int MacdFast = 12;
input int MacdSlow = 26;
input int MacdSignal = 9;
input bool AllowInsideBarBreak = true;
input bool AllowAsianBreakout = true;
input int AsianStartHour = 0;
input int AsianEndHour = 8;
input double AsianBreakBufferPips = 1.0;
input bool UseH1TrendFilter = true;
input int H1EmaPeriod = 50;
input bool UseAdxFilter = false;
input int AdxPeriod = 14;
input double AdxMin = 20.0;
CTrade g_trade;
int g_fastHandle = INVALID_HANDLE;
int g_slowHandle = INVALID_HANDLE;
int g_atrHandle = INVALID_HANDLE;
int g_dailyEma = INVALID_HANDLE;
int g_rsiHandle = INVALID_HANDLE;
int g_macdHandle = INVALID_HANDLE;
int g_adxHandle = INVALID_HANDLE;
int g_h1EmaHandle = INVALID_HANDLE;
datetime g_lastBar = 0;
int g_lastEntryBar = -100000;
int g_bars = 0;
double g_trail = 0.0;
double PipSize()
{
double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
int d = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
return (d == 3 || d == 5) ? pt * 10.0 : pt;
}
int SpreadPips()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(ask <= 0 || bid <= 0) return 9999;
return (int)MathRound((ask - bid) / PipSize());
}
bool HourInRange(const int h, const int start, const int end)
{
if(start == end) return true;
if(start < end) return (h >= start && h < end);
return (h >= start || h < end);
}
bool InMomentumWindow()
{
MqlDateTime ts; TimeToStruct(TimeCurrent(), ts);
return HourInRange(ts.hour, MomentumStartHour, MomentumEndHour);
}
bool InNyChaos()
{
MqlDateTime ts; TimeToStruct(TimeCurrent(), ts);
return HourInRange(ts.hour, NyChaosStartHour, NyChaosEndHour);
}
bool SessionOk()
{
if(InNyChaos()) return false;
if(!UseMomentumWindow) return true;
return InMomentumWindow();
}
bool IsNewBar()
{
datetime t = iTime(_Symbol, Timeframe, 0);
if(t <= 0 || t == g_lastBar) return false;
g_lastBar = t;
g_bars++;
return true;
}
bool Copy1(const int h, const int sh, const int buf, double &v)
{
double b[1];
if(CopyBuffer(h, buf, sh, 1, b) <= 0) return false;
v = b[0]; return true;
}
int TfShift(const ENUM_TIMEFRAMES tf, const int ltf_sh)
{
datetime t = iTime(_Symbol, Timeframe, ltf_sh);
if(t <= 0) return -1;
return iBarShift(_Symbol, tf, t, true);
}
double BodyRatio(const ENUM_TIMEFRAMES tf, const int sh)
{
double o = iOpen(_Symbol, tf, sh);
double h = iHigh(_Symbol, tf, sh);
double l = iLow(_Symbol, tf, sh);
double c = iClose(_Symbol, tf, sh);
double rng = h - l;
if(rng <= 0) return 0;
return MathAbs(c - o) / rng;
}
bool HtfZoneAt(const int hsh, double &resistance, double &support)
{
if(hsh < 0) return false;
resistance = -1e100;
support = 1e100;
for(int i = hsh + 1; i <= hsh + HtfZoneBars; i++)
{
double hi = iHigh(_Symbol, HtfTimeframe, i);
double lo = iLow(_Symbol, HtfTimeframe, i);
if(hi > resistance) resistance = hi;
if(lo < support) support = lo;
}
return (resistance > -1e50 && support < 1e50);
}
int DailyBias(const int ltf_sh)
{
if(!UseDailyBias) return 0;
int dsh = TfShift(DailyTimeframe, ltf_sh);
if(dsh < 0) return 0;
double ema, close;
if(!Copy1(g_dailyEma, dsh, 0, ema)) return 0;
close = iClose(_Symbol, DailyTimeframe, dsh);
if(close > ema) return 1;
if(close < ema) return -1;
return 0;
}
bool HtfBullBreak(const int ltf_sh)
{
int hsh = TfShift(HtfTimeframe, ltf_sh);
if(hsh < 0) return false;
double res, sup;
if(!HtfZoneAt(hsh + 1, res, sup)) return false;
double c = iClose(_Symbol, HtfTimeframe, hsh);
return (c > res && BodyRatio(HtfTimeframe, hsh) >= MinBreakBodyRatio);
}
bool HtfBearBreak(const int ltf_sh)
{
int hsh = TfShift(HtfTimeframe, ltf_sh);
if(hsh < 0) return false;
double res, sup;
if(!HtfZoneAt(hsh + 1, res, sup)) return false;
double c = iClose(_Symbol, HtfTimeframe, hsh);
return (c < sup && BodyRatio(HtfTimeframe, hsh) >= MinBreakBodyRatio);
}
bool BullTrap(const int ltf_sh)
{
if(!UseDoubleTrap) return false;
int hsh = TfShift(HtfTimeframe, ltf_sh);
if(hsh < 0) return false;
double res, sup;
if(!HtfZoneAt(hsh + 2, res, sup)) return false;
double hi = iHigh(_Symbol, HtfTimeframe, hsh + 1);
double cl = iClose(_Symbol, HtfTimeframe, hsh + 1);
return (hi > res && cl < res);
}
bool BearTrap(const int ltf_sh)
{
if(!UseDoubleTrap) return false;
int hsh = TfShift(HtfTimeframe, ltf_sh);
if(hsh < 0) return false;
double res, sup;
if(!HtfZoneAt(hsh + 2, res, sup)) return false;
double lo = iLow(_Symbol, HtfTimeframe, hsh + 1);
double cl = iClose(_Symbol, HtfTimeframe, hsh + 1);
return (lo < sup && cl > sup);
}
bool CompressionOk(const int sh)
{
if(!UseCompressionFilter || !InNyChaos()) return true;
double atrNow, atrSum = 0;
if(!Copy1(g_atrHandle, sh, 0, atrNow)) return true;
int n = MathMin(CompressLookback, 200);
for(int i = sh; i < sh + n; i++)
{
double a;
if(!Copy1(g_atrHandle, i, 0, a)) continue;
atrSum += a;
}
double avg = atrSum / MathMax(n, 1);
if(avg <= 0) return true;
return (atrNow / avg >= CompressAtrRatio);
}
bool PullbackLong(const int sh)
{
double f, s, close, low;
if(!Copy1(g_fastHandle, sh, 0, f) || !Copy1(g_slowHandle, sh, 0, s)) return false;
close = iClose(_Symbol, Timeframe, sh);
low = iLow(_Symbol, Timeframe, sh);
return (f > s && low <= f && close > f);
}
bool PullbackShort(const int sh)
{
double f, s, close, high;
if(!Copy1(g_fastHandle, sh, 0, f) || !Copy1(g_slowHandle, sh, 0, s)) return false;
close = iClose(_Symbol, Timeframe, sh);
high = iHigh(_Symbol, Timeframe, sh);
return (f < s && high >= f && close < f);
}
bool ReclaimLong(const int sh)
{
int hsh = TfShift(HtfTimeframe, sh);
if(hsh < 0) return false;
double res, sup, f;
if(!HtfZoneAt(hsh + 1, res, sup)) return false;
if(!Copy1(g_fastHandle, sh, 0, f)) return false;
double c = iClose(_Symbol, Timeframe, sh);
return (BullTrap(sh) && c > res && c > f);
}
bool ReclaimShort(const int sh)
{
int hsh = TfShift(HtfTimeframe, sh);
if(hsh < 0) return false;
double res, sup, f;
if(!HtfZoneAt(hsh + 1, res, sup)) return false;
if(!Copy1(g_fastHandle, sh, 0, f)) return false;
double c = iClose(_Symbol, Timeframe, sh);
return (BearTrap(sh) && c < sup && c < f);
}
bool HasOurPosition()
{
return PositionSelect(_Symbol) && PositionGetInteger(POSITION_MAGIC) == MagicNumber;
}
void CloseOur(const string reason)
{
if(!HasOurPosition()) return;
if(g_trade.PositionClose((ulong)PositionGetInteger(POSITION_TICKET)))
Print("[USDCHF Playbook] close ", reason);
g_trail = 0;
}
bool TryOpen(const bool isLong)
{
if(HasOurPosition()) return false;
if(OneTradeBlocked()) return false;
g_trade.SetExpertMagicNumber(MagicNumber);
bool ok = isLong ? g_trade.Buy(LotSize, _Symbol) : g_trade.Sell(LotSize, _Symbol);
if(ok)
{
g_lastEntryBar = g_bars;
g_trail = 0;
Print("[USDCHF Playbook] ", isLong ? "BUY" : "SELL");
}
return ok;
}
bool OneTradeBlocked()
{
if(MaxSpreadPips > 0 && SpreadPips() > MaxSpreadPips) return true;
if(g_bars - g_lastEntryBar < CooldownBars) return true;
return false;
}
bool EmaGapOk(const int sh)
{
if(MinEmaGapPips <= 0) return true;
double f, s;
if(!Copy1(g_fastHandle, sh, 0, f) || !Copy1(g_slowHandle, sh, 0, s)) return false;
return (MathAbs(f - s) / PipSize() >= MinEmaGapPips);
}
bool TrendLong(const int sh)
{
double f, s;
if(!Copy1(g_fastHandle, sh, 0, f) || !Copy1(g_slowHandle, sh, 0, s)) return false;
return (f > s);
}
bool TrendShort(const int sh)
{
double f, s;
if(!Copy1(g_fastHandle, sh, 0, f) || !Copy1(g_slowHandle, sh, 0, s)) return false;
return (f < s);
}
bool EmaCrossLong(const int sh)
{
double f1, f2, s1, s2;
if(!Copy1(g_fastHandle, sh, 0, f1) || !Copy1(g_fastHandle, sh + 1, 0, f2)) return false;
if(!Copy1(g_slowHandle, sh, 0, s1) || !Copy1(g_slowHandle, sh + 1, 0, s2)) return false;
return (f2 <= s2 && f1 > s1);
}
bool EmaCrossShort(const int sh)
{
double f1, f2, s1, s2;
if(!Copy1(g_fastHandle, sh, 0, f1) || !Copy1(g_fastHandle, sh + 1, 0, f2)) return false;
if(!Copy1(g_slowHandle, sh, 0, s1) || !Copy1(g_slowHandle, sh + 1, 0, s2)) return false;
return (f2 >= s2 && f1 < s1);
}
bool LtfMomentumLong(const int sh)
{
if(!EmaGapOk(sh)) return false;
bool pb = AllowLtfPullback && PullbackLong(sh) && TrendLong(sh);
bool x = AllowEmaCross && EmaCrossLong(sh);
return (pb || x);
}
bool LtfMomentumShort(const int sh)
{
if(!EmaGapOk(sh)) return false;
bool pb = AllowLtfPullback && PullbackShort(sh) && TrendShort(sh);
bool x = AllowEmaCross && EmaCrossShort(sh);
return (pb || x);
}
bool H1TrendLong(const int sh)
{
if(!UseH1TrendFilter) return true;
int h1sh = TfShift(PERIOD_H1, sh);
if(h1sh < 0) return false;
double ema, close;
if(!Copy1(g_h1EmaHandle, h1sh, 0, ema)) return false;
close = iClose(_Symbol, PERIOD_H1, h1sh);
return (close > ema);
}
bool H1TrendShort(const int sh)
{
if(!UseH1TrendFilter) return true;
int h1sh = TfShift(PERIOD_H1, sh);
if(h1sh < 0) return false;
double ema, close;
if(!Copy1(g_h1EmaHandle, h1sh, 0, ema)) return false;
close = iClose(_Symbol, PERIOD_H1, h1sh);
return (close < ema);
}
bool AdxOk(const int sh)
{
if(!UseAdxFilter) return true;
double adx;
if(!Copy1(g_adxHandle, sh, 0, adx)) return false;
return (adx >= AdxMin);
}
bool RsiPullbackLong(const int sh)
{
if(!AllowRsiPullback) return false;
double r1, r2;
if(!Copy1(g_rsiHandle, sh, 0, r1) || !Copy1(g_rsiHandle, sh + 1, 0, r2)) return false;
return (r2 <= RsiBuyZone && r1 > RsiBuyZone && TrendLong(sh));
}
bool RsiPullbackShort(const int sh)
{
if(!AllowRsiPullback) return false;
double r1, r2;
if(!Copy1(g_rsiHandle, sh, 0, r1) || !Copy1(g_rsiHandle, sh + 1, 0, r2)) return false;
return (r2 >= RsiSellZone && r1 < RsiSellZone && TrendShort(sh));
}
bool MacdMomentumLong(const int sh)
{
if(!AllowMacdMomentum) return false;
double h1, h2;
if(!Copy1(g_macdHandle, sh, 2, h1) || !Copy1(g_macdHandle, sh + 1, 2, h2)) return false;
return (h1 > h2 && h1 > 0);
}
bool MacdMomentumShort(const int sh)
{
if(!AllowMacdMomentum) return false;
double h1, h2;
if(!Copy1(g_macdHandle, sh, 2, h1) || !Copy1(g_macdHandle, sh + 1, 2, h2)) return false;
return (h1 < h2 && h1 < 0);
}
bool InsideBarBreakLong(const int sh)
{
if(!AllowInsideBarBreak) return false;
double hiM = iHigh(_Symbol, Timeframe, sh + 1);
double loM = iLow(_Symbol, Timeframe, sh + 1);
double hiI = iHigh(_Symbol, Timeframe, sh);
double loI = iLow(_Symbol, Timeframe, sh);
double cI = iClose(_Symbol, Timeframe, sh);
if(hiI >= hiM || loI <= loM) return false;
return (cI > hiM && TrendLong(sh));
}
bool InsideBarBreakShort(const int sh)
{
if(!AllowInsideBarBreak) return false;
double hiM = iHigh(_Symbol, Timeframe, sh + 1);
double loM = iLow(_Symbol, Timeframe, sh + 1);
double hiI = iHigh(_Symbol, Timeframe, sh);
double loI = iLow(_Symbol, Timeframe, sh);
double cI = iClose(_Symbol, Timeframe, sh);
if(hiI >= hiM || loI <= loM) return false;
return (cI < loM && TrendShort(sh));
}
bool AsianRange(const int sh, double &aHigh, double &aLow)
{
aHigh = -1e100;
aLow = 1e100;
datetime barTime = iTime(_Symbol, Timeframe, sh);
if(barTime <= 0) return false;
MqlDateTime ts; TimeToStruct(barTime, ts);
if(!HourInRange(ts.hour, AsianEndHour, AsianEndHour + 12)) return false;
for(int i = sh; i < sh + 96; i++)
{
datetime t = iTime(_Symbol, Timeframe, i);
if(t <= 0) break;
MqlDateTime bt; TimeToStruct(t, bt);
if(!HourInRange(bt.hour, AsianStartHour, AsianEndHour)) continue;
double hi = iHigh(_Symbol, Timeframe, i);
double lo = iLow(_Symbol, Timeframe, i);
if(hi > aHigh) aHigh = hi;
if(lo < aLow) aLow = lo;
}
return (aHigh > -1e50 && aLow < 1e50 && aHigh > aLow);
}
bool AsianBreakoutLong(const int sh)
{
if(!AllowAsianBreakout) return false;
double aHi, aLo;
if(!AsianRange(sh, aHi, aLo)) return false;
double buf = AsianBreakBufferPips * PipSize();
double c = iClose(_Symbol, Timeframe, sh);
return (c > aHi + buf && TrendLong(sh));
}
bool AsianBreakoutShort(const int sh)
{
if(!AllowAsianBreakout) return false;
double aHi, aLo;
if(!AsianRange(sh, aHi, aLo)) return false;
double buf = AsianBreakBufferPips * PipSize();
double c = iClose(_Symbol, Timeframe, sh);
return (c < aLo - buf && TrendShort(sh));
}
bool PrimaryLong(const int sh)
{
if(EntryMode == 0) return HtfBullBreak(sh);
if(EntryMode == 2) return ReclaimLong(sh);
if(EntryMode == 3) return LtfMomentumLong(sh);
return (HtfBullBreak(sh) || ReclaimLong(sh) || LtfMomentumLong(sh) || PullbackLong(sh));
}
bool PrimaryShort(const int sh)
{
if(EntryMode == 0) return HtfBearBreak(sh);
if(EntryMode == 2) return ReclaimShort(sh);
if(EntryMode == 3) return LtfMomentumShort(sh);
return (HtfBearBreak(sh) || ReclaimShort(sh) || LtfMomentumShort(sh) || PullbackShort(sh));
}
bool ConfirmLong(const int sh)
{
return (RsiPullbackLong(sh) || MacdMomentumLong(sh) || InsideBarBreakLong(sh) || AsianBreakoutLong(sh));
}
bool ConfirmShort(const int sh)
{
return (RsiPullbackShort(sh) || MacdMomentumShort(sh) || InsideBarBreakShort(sh) || AsianBreakoutShort(sh));
}
int ScoreLong(const int sh)
{
int sc = 0;
if(HtfBullBreak(sh)) sc++;
if(ReclaimLong(sh)) sc++;
if(LtfMomentumLong(sh)) sc++;
if(PullbackLong(sh) && TrendLong(sh)) sc++;
if(RsiPullbackLong(sh)) sc++;
if(MacdMomentumLong(sh)) sc++;
if(InsideBarBreakLong(sh)) sc++;
if(AsianBreakoutLong(sh)) sc++;
return sc;
}
int ScoreShort(const int sh)
{
int sc = 0;
if(HtfBearBreak(sh)) sc++;
if(ReclaimShort(sh)) sc++;
if(LtfMomentumShort(sh)) sc++;
if(PullbackShort(sh) && TrendShort(sh)) sc++;
if(RsiPullbackShort(sh)) sc++;
if(MacdMomentumShort(sh)) sc++;
if(InsideBarBreakShort(sh)) sc++;
if(AsianBreakoutShort(sh)) sc++;
return sc;
}
bool ComboLong(const int sh)
{
if(ComboMode == 1) return (PrimaryLong(sh) && ConfirmLong(sh));
if(ComboMode == 2) return (ScoreLong(sh) >= MinComboScore);
return (PrimaryLong(sh) || ConfirmLong(sh) || LtfMomentumLong(sh));
}
bool ComboShort(const int sh)
{
if(ComboMode == 1) return (PrimaryShort(sh) && ConfirmShort(sh));
if(ComboMode == 2) return (ScoreShort(sh) >= MinComboScore);
return (PrimaryShort(sh) || ConfirmShort(sh) || LtfMomentumShort(sh));
}
bool BuySignal(const int sh)
{
if(!SessionOk() || !CompressionOk(sh)) return false;
if(!H1TrendLong(sh) || !AdxOk(sh)) return false;
int bias = DailyBias(sh);
if(UseDailyBias && bias < 0) return false;
return ComboLong(sh);
}
bool SellSignal(const int sh)
{
if(!SessionOk() || !CompressionOk(sh)) return false;
if(!H1TrendShort(sh) || !AdxOk(sh)) return false;
int bias = DailyBias(sh);
if(UseDailyBias && bias > 0) return false;
return ComboShort(sh);
}
void ManagePosition(const int sh)
{
if(!HasOurPosition()) return;
long type = PositionGetInteger(POSITION_TYPE);
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
int barsHeld = iBarShift(_Symbol, Timeframe, openTime);
int maxBars = MaxBarsInTrade;
if(ExtendHoldMomentum && InMomentumWindow()) maxBars = (int)(maxBars * 1.5);
if(maxBars > 0 && barsHeld >= maxBars)
{
CloseOur("max_bars");
return;
}
double atr;
if(!Copy1(g_atrHandle, sh, 0, atr) || atr <= 0) return;
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(type == POSITION_TYPE_BUY)
{
double sl = entry - atr * AtrSlMult;
double tp = entry + atr * AtrTpMult;
double effSl = sl;
if(UseTrailing)
{
double candidate = bid - atr * TrailAtrMult;
if(candidate > entry)
{
if(g_trail <= 0 || candidate > g_trail) g_trail = candidate;
effSl = MathMax(sl, g_trail);
}
}
if(bid <= effSl) { CloseOur("sl_trail"); return; }
if(bid >= tp) { CloseOur("tp"); return; }
}
else
{
double sl = entry + atr * AtrSlMult;
double tp = entry - atr * AtrTpMult;
double effSl = sl;
if(UseTrailing)
{
double candidate = ask + atr * TrailAtrMult;
if(candidate < entry)
{
if(g_trail <= 0 || candidate < g_trail) g_trail = candidate;
effSl = MathMin(sl, g_trail);
}
}
if(ask >= effSl) { CloseOur("sl_trail"); return; }
if(ask <= tp) { CloseOur("tp"); return; }
}
}
int OnInit()
{
g_fastHandle = iMA(_Symbol, Timeframe, LtfFastEma, 0, MODE_EMA, PRICE_CLOSE);
g_slowHandle = iMA(_Symbol, Timeframe, LtfSlowEma, 0, MODE_EMA, PRICE_CLOSE);
g_atrHandle = iATR(_Symbol, Timeframe, AtrPeriod);
g_dailyEma = iMA(_Symbol, DailyTimeframe, DailyEmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
g_rsiHandle = iRSI(_Symbol, Timeframe, RsiPeriod, PRICE_CLOSE);
g_macdHandle = iMACD(_Symbol, Timeframe, MacdFast, MacdSlow, MacdSignal, PRICE_CLOSE);
g_adxHandle = iADX(_Symbol, Timeframe, AdxPeriod);
g_h1EmaHandle = iMA(_Symbol, PERIOD_H1, H1EmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(g_fastHandle == INVALID_HANDLE || g_slowHandle == INVALID_HANDLE ||
g_atrHandle == INVALID_HANDLE || g_dailyEma == INVALID_HANDLE ||
g_rsiHandle == INVALID_HANDLE || g_macdHandle == INVALID_HANDLE ||
g_adxHandle == INVALID_HANDLE || g_h1EmaHandle == INVALID_HANDLE)
return INIT_FAILED;
g_trade.SetExpertMagicNumber(MagicNumber);
Print("[USDCHF Playbook v1.20] combo modules on ", _Symbol);
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(g_fastHandle != INVALID_HANDLE) IndicatorRelease(g_fastHandle);
if(g_slowHandle != INVALID_HANDLE) IndicatorRelease(g_slowHandle);
if(g_atrHandle != INVALID_HANDLE) IndicatorRelease(g_atrHandle);
if(g_dailyEma != INVALID_HANDLE) IndicatorRelease(g_dailyEma);
if(g_rsiHandle != INVALID_HANDLE) IndicatorRelease(g_rsiHandle);
if(g_macdHandle != INVALID_HANDLE) IndicatorRelease(g_macdHandle);
if(g_adxHandle != INVALID_HANDLE) IndicatorRelease(g_adxHandle);
if(g_h1EmaHandle != INVALID_HANDLE) IndicatorRelease(g_h1EmaHandle);
}
void OnTick()
{
if(!IsNewBar()) return;
const int sh = 1;
ManagePosition(sh);
if(HasOurPosition()) return;
if(BuySignal(sh)) TryOpen(true);
else if(SellSignal(sh)) TryOpen(false);
}