From 0b4a70b84339b316ab34e4abe58dc54ad4a814a8 Mon Sep 17 00:00:00 2001 From: zhutoutoutousan Date: Sat, 25 Apr 2026 11:24:49 +0200 Subject: [PATCH] Update --- .../Strategies/SimpleTrendlineStrategy.mqh | 318 ++++++++++++++++++ frontline/cluster-0/_united-V2/main.mq5 | 59 +++- .../SimpleTrendlineBTCUSD/SimpleTrendline.mq5 | 284 ++++++++++++++++ .../SimpleTrendline_optimization.set | 14 + .../SimpleTrendlineGER40/SimpleTrendline.mq5 | 284 ++++++++++++++++ .../SimpleTrendline_optimization.set | 14 + .../SimpleTrendlineXAUUSD/SimpleTrendline.mq5 | 284 ++++++++++++++++ .../SimpleTrendline_optimization.set | 14 + lab/EAs/SimpleTrendline.mq5 | 0 9 files changed, 1270 insertions(+), 1 deletion(-) create mode 100644 frontline/cluster-0/_united-V2/Strategies/SimpleTrendlineStrategy.mqh create mode 100644 frontline/units/SimpleTrendlineBTCUSD/SimpleTrendline.mq5 create mode 100644 frontline/units/SimpleTrendlineBTCUSD/SimpleTrendline_optimization.set create mode 100644 frontline/units/SimpleTrendlineGER40/SimpleTrendline.mq5 create mode 100644 frontline/units/SimpleTrendlineGER40/SimpleTrendline_optimization.set create mode 100644 frontline/units/SimpleTrendlineXAUUSD/SimpleTrendline.mq5 create mode 100644 frontline/units/SimpleTrendlineXAUUSD/SimpleTrendline_optimization.set delete mode 100644 lab/EAs/SimpleTrendline.mq5 diff --git a/frontline/cluster-0/_united-V2/Strategies/SimpleTrendlineStrategy.mqh b/frontline/cluster-0/_united-V2/Strategies/SimpleTrendlineStrategy.mqh new file mode 100644 index 0000000..6341b88 --- /dev/null +++ b/frontline/cluster-0/_united-V2/Strategies/SimpleTrendlineStrategy.mqh @@ -0,0 +1,318 @@ +//+------------------------------------------------------------------+ +//| SimpleTrendlineStrategy.mqh | +//+------------------------------------------------------------------+ +#ifndef SIMPLE_TRENDLINE_STRATEGY_MQH +#define SIMPLE_TRENDLINE_STRATEGY_MQH + +struct SimpleTrendlineModel +{ + datetime t1; + datetime t2; + datetime t3; + double a; + double b; + bool valid; +}; + +struct SimpleTrendlineData +{ + string symbol; + bool isInitialized; + CTrade trade; + ENUM_TIMEFRAMES signalTF; + ENUM_TIMEFRAMES higherTF; + int maPeriod; + ENUM_MA_METHOD maMethod; + ENUM_APPLIED_PRICE appliedPrice; + int htfBarsToScan; + double touchTolerancePoints; + double breakBufferPoints; + ulong magic; + bool drawTrendline; + int maHandle; + datetime lastSignalBarTime; + string lineName; +}; + +double ST_NormalizeVolume(const string sym, double vol) +{ + double minLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN); + double maxLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX); + double step = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP); + if(step > 0.0) + vol = MathFloor(vol / step) * step; + if(vol < minLot) + vol = minLot; + if(vol > maxLot) + vol = maxLot; + return vol; +} + +bool ST_GetPosition(const string sym, const ulong magic, ENUM_POSITION_TYPE &type, double &volume) +{ + if(!PositionSelectByMagic(sym, magic)) + return false; + type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); + volume = PositionGetDouble(POSITION_VOLUME); + return true; +} + +int ST_FindRecentCrossPoints(SimpleTrendlineData &d, datetime ×[], double &prices[]) +{ + ArrayResize(times, 0); + ArrayResize(prices, 0); + if(d.maHandle == INVALID_HANDLE) + return 0; + + int needBars = MathMax(d.htfBarsToScan, d.maPeriod + 20); + MqlRates rates[]; + double maBuf[]; + ArraySetAsSeries(rates, true); + ArraySetAsSeries(maBuf, true); + + int copiedRates = CopyRates(d.symbol, d.higherTF, 0, needBars, rates); + int copiedMa = CopyBuffer(d.maHandle, 0, 0, needBars, maBuf); + if(copiedRates <= 5 || copiedMa <= 5) + return 0; + + int bars = MathMin(copiedRates, copiedMa); + for(int i = 2; i < bars - 1; i++) + { + double d0 = rates[i].close - maBuf[i]; + double d1 = rates[i + 1].close - maBuf[i + 1]; + if(d0 == 0.0 || d1 == 0.0 || (d0 * d1 < 0.0)) + { + int n = ArraySize(times); + ArrayResize(times, n + 1); + ArrayResize(prices, n + 1); + times[n] = rates[i].time; + prices[n] = rates[i].close; + if(ArraySize(times) >= 3) + break; + } + } + return ArraySize(times); +} + +bool ST_BuildTrendline(SimpleTrendlineData &d, SimpleTrendlineModel &m) +{ + m.valid = false; + datetime ts[]; + double ps[]; + if(ST_FindRecentCrossPoints(d, ts, ps) < 3) + return false; + + datetime tOld[3]; + double pOld[3]; + for(int i = 0; i < 3; i++) + { + tOld[i] = ts[2 - i]; + pOld[i] = ps[2 - i]; + } + + long t0 = (long)tOld[0]; + double x1 = 0.0; + double x2 = (double)((long)tOld[1] - t0); + double x3 = (double)((long)tOld[2] - t0); + double y1 = pOld[0]; + double y2 = pOld[1]; + double y3 = pOld[2]; + + double sx = x1 + x2 + x3; + double sy = y1 + y2 + y3; + double sxx = x1 * x1 + x2 * x2 + x3 * x3; + double sxy = x1 * y1 + x2 * y2 + x3 * y3; + double den = 3.0 * sxx - sx * sx; + if(MathAbs(den) < 1e-10) + return false; + + m.a = (3.0 * sxy - sx * sy) / den; + m.b = (sy - m.a * sx) / 3.0; + m.t1 = tOld[0]; + m.t2 = tOld[1]; + m.t3 = tOld[2]; + m.valid = true; + return true; +} + +double ST_LinePriceAt(const SimpleTrendlineModel &m, const datetime t) +{ + if(!m.valid) + return 0.0; + double x = (double)((long)t - (long)m.t1); + return m.a * x + m.b; +} + +void ST_DrawTrendline(SimpleTrendlineData &d, const SimpleTrendlineModel &m) +{ + if(!d.drawTrendline || !m.valid || d.symbol != _Symbol) + return; + + datetime tStart = m.t1; + datetime tEnd = iTime(d.symbol, d.signalTF, 0); + if(tEnd <= tStart) + tEnd = m.t3 + PeriodSeconds(d.signalTF) * 20; + + double pStart = ST_LinePriceAt(m, tStart); + double pEnd = ST_LinePriceAt(m, tEnd); + + if(ObjectFind(0, d.lineName) < 0) + ObjectCreate(0, d.lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd); + else + { + ObjectMove(0, d.lineName, 0, tStart, pStart); + ObjectMove(0, d.lineName, 1, tEnd, pEnd); + } + + ObjectSetInteger(0, d.lineName, OBJPROP_RAY_RIGHT, true); + ObjectSetInteger(0, d.lineName, OBJPROP_COLOR, clrGold); + ObjectSetInteger(0, d.lineName, OBJPROP_WIDTH, 2); +} + +void ST_TryExitOnBreak(SimpleTrendlineData &d, const SimpleTrendlineModel &m) +{ + ENUM_POSITION_TYPE posType; + double vol; + if(!ST_GetPosition(d.symbol, d.magic, posType, vol)) + return; + + double close1 = iClose(d.symbol, d.signalTF, 1); + datetime t1 = iTime(d.symbol, d.signalTF, 1); + double line1 = ST_LinePriceAt(m, t1); + double buf = d.breakBufferPoints * SymbolInfoDouble(d.symbol, SYMBOL_POINT); + + bool closePos = false; + if(posType == POSITION_TYPE_BUY && close1 < (line1 - buf)) + closePos = true; + if(posType == POSITION_TYPE_SELL && close1 > (line1 + buf)) + closePos = true; + + if(closePos) + ClosePositionByMagic(d.trade, d.symbol, d.magic); +} + +void ST_TryPullbackEntry(SimpleTrendlineData &d, const SimpleTrendlineModel &m, const double lots) +{ + if(PositionExistsByMagic(d.symbol, d.magic)) + return; + + MqlRates b1[], b2[]; + ArraySetAsSeries(b1, true); + ArraySetAsSeries(b2, true); + if(CopyRates(d.symbol, d.signalTF, 1, 1, b1) != 1) + return; + if(CopyRates(d.symbol, d.signalTF, 2, 1, b2) != 1) + return; + if(ArraySize(b1) < 1 || ArraySize(b2) < 1) + return; + + double line1 = ST_LinePriceAt(m, b1[0].time); + double tol = d.touchTolerancePoints * SymbolInfoDouble(d.symbol, SYMBOL_POINT); + bool upTrend = (m.a > 0.0); + bool downTrend = (m.a < 0.0); + double vol = ST_NormalizeVolume(d.symbol, lots); + + if(upTrend) + { + bool touched = (b1[0].low <= (line1 + tol)); + bool reclaim = (b1[0].close > line1); + bool bullish = (b1[0].close > b1[0].open); + bool stillHealthy = (b2[0].close >= ST_LinePriceAt(m, b2[0].time) - tol); + if(touched && reclaim && bullish && stillHealthy) + { + if(!d.trade.Buy(vol, d.symbol, 0.0, 0.0, 0.0, "SimpleTrendline BUY")) + Print("SimpleTrendline BUY failed [", d.symbol, "] retcode=", d.trade.ResultRetcode(), " ", d.trade.ResultRetcodeDescription()); + } + } + else if(downTrend) + { + bool touched = (b1[0].high >= (line1 - tol)); + bool reject = (b1[0].close < line1); + bool bearish = (b1[0].close < b1[0].open); + bool stillWeak = (b2[0].close <= ST_LinePriceAt(m, b2[0].time) + tol); + if(touched && reject && bearish && stillWeak) + { + if(!d.trade.Sell(vol, d.symbol, 0.0, 0.0, 0.0, "SimpleTrendline SELL")) + Print("SimpleTrendline SELL failed [", d.symbol, "] retcode=", d.trade.ResultRetcode(), " ", d.trade.ResultRetcodeDescription()); + } + } +} + +bool InitSimpleTrendline(SimpleTrendlineData &d, + const string symbol, + const ENUM_TIMEFRAMES signalTF, + const ENUM_TIMEFRAMES higherTF, + const int maPeriod, + const ENUM_MA_METHOD maMethod, + const ENUM_APPLIED_PRICE appliedPrice, + const int htfBarsToScan, + const double touchTolerancePoints, + const double breakBufferPoints, + const ulong magic, + const bool drawTrendline) +{ + d.isInitialized = false; + d.symbol = symbol; + StringTrimLeft(d.symbol); + StringTrimRight(d.symbol); + if(StringLen(d.symbol) == 0) + d.symbol = _Symbol; + + if(!SymbolSelect(d.symbol, true)) + return false; + + d.signalTF = signalTF; + d.higherTF = higherTF; + d.maPeriod = maPeriod; + d.maMethod = maMethod; + d.appliedPrice = appliedPrice; + d.htfBarsToScan = htfBarsToScan; + d.touchTolerancePoints = touchTolerancePoints; + d.breakBufferPoints = breakBufferPoints; + d.magic = magic; + d.drawTrendline = drawTrendline; + d.lastSignalBarTime = 0; + d.lineName = "SimpleTrendline_" + d.symbol + "_" + IntegerToString((int)d.magic); + + d.trade.SetExpertMagicNumber((long)d.magic); + d.trade.SetTypeFillingBySymbol(d.symbol); + d.trade.SetDeviationInPoints(20); + + d.maHandle = iMA(d.symbol, d.higherTF, d.maPeriod, 0, d.maMethod, d.appliedPrice); + if(d.maHandle == INVALID_HANDLE) + return false; + + d.isInitialized = true; + return true; +} + +void DeinitSimpleTrendline(SimpleTrendlineData &d) +{ + if(d.maHandle != INVALID_HANDLE) + IndicatorRelease(d.maHandle); + d.maHandle = INVALID_HANDLE; + if(ObjectFind(0, d.lineName) >= 0) + ObjectDelete(0, d.lineName); + d.isInitialized = false; +} + +void ProcessSimpleTrendline(SimpleTrendlineData &d, const double lots) +{ + if(!d.isInitialized) + return; + + datetime bar0 = iTime(d.symbol, d.signalTF, 0); + if(bar0 == 0 || bar0 == d.lastSignalBarTime) + return; + d.lastSignalBarTime = bar0; + + SimpleTrendlineModel m; + if(!ST_BuildTrendline(d, m)) + return; + + ST_DrawTrendline(d, m); + ST_TryExitOnBreak(d, m); + ST_TryPullbackEntry(d, m, lots); +} + +#endif // SIMPLE_TRENDLINE_STRATEGY_MQH diff --git a/frontline/cluster-0/_united-V2/main.mq5 b/frontline/cluster-0/_united-V2/main.mq5 index 35e7106..564b03f 100644 --- a/frontline/cluster-0/_united-V2/main.mq5 +++ b/frontline/cluster-0/_united-V2/main.mq5 @@ -22,6 +22,7 @@ #include "Strategies/SuperEMAStrategy.mqh" #include "Strategies/RSIReversalAsianStrategy.mqh" #include "Strategies/RSIConsolidationStrategy.mqh" +#include "Strategies/SimpleTrendlineStrategy.mqh" //+------------------------------------------------------------------+ //| Global Lot Size Variables (for dynamic lot sizing) | @@ -54,6 +55,8 @@ input bool EnableSuperEMA = true; input bool EnableRSIConsolidation = true; input bool EnableRSIReversalAsianEURUSD = true; input bool EnableRSIReversalAsianAUDUSD = true; +input bool EnableSimpleTrendlineBTCUSD = true; +input bool EnableSimpleTrendlineXAUUSD = true; input group "=== Centralized Lot Size (Granular Per Robot) ===" input double LOT_ES_EMASlopeDistance = 0.05; @@ -68,6 +71,8 @@ input double LOT_RRA_EURUSD = 0.01; input double LOT_RRA_AUDUSD = 0.10; input double LOT_SE_SuperEMA = 0.01; input double LOT_RCO_RSIConsolidation = 0.04; +input double LOT_ST_BTCUSD = 0.19; +input double LOT_ST_XAUUSD = 0.02; //+------------------------------------------------------------------+ //| Strategy 1: DarvasBoxXAUUSD | @@ -385,6 +390,32 @@ input ulong RCO_MagicNumber = 20250420; input int RCO_Slippage = 10; input int RCO_MaxSpreadPoints = 28; +input group "=== SimpleTrendline BTCUSD ===" +input string ST_BTC_Symbol = "BTCUSD"; +input ENUM_TIMEFRAMES ST_BTC_SignalTF = PERIOD_H1; +input ENUM_TIMEFRAMES ST_BTC_HigherTF = PERIOD_H4; +input int ST_BTC_MAPeriod = 150; +input ENUM_MA_METHOD ST_BTC_MAMethod = MODE_SMMA; +input ENUM_APPLIED_PRICE ST_BTC_AppliedPrice = PRICE_OPEN; +input int ST_BTC_HTFBarsToScan = 1200; +input double ST_BTC_LineTouchTolerance = 170.0; +input double ST_BTC_BreakBuffer = 90.0; +input ulong ST_BTC_MagicNumber = 26042501; +input bool ST_BTC_DrawTrendline = true; + +input group "=== SimpleTrendline XAUUSD ===" +input string ST_XAU_Symbol = "XAUUSD"; +input ENUM_TIMEFRAMES ST_XAU_SignalTF = PERIOD_H1; +input ENUM_TIMEFRAMES ST_XAU_HigherTF = PERIOD_M10; +input int ST_XAU_MAPeriod = 65; +input ENUM_MA_METHOD ST_XAU_MAMethod = MODE_EMA; +input ENUM_APPLIED_PRICE ST_XAU_AppliedPrice = PRICE_OPEN; +input int ST_XAU_HTFBarsToScan = 500; +input double ST_XAU_LineTouchTolerance = 220.0; +input double ST_XAU_BreakBuffer = 110.0; +input ulong ST_XAU_MagicNumber = 26042503; +input bool ST_XAU_DrawTrendline = true; + //+------------------------------------------------------------------+ //| Global Variables - DarvasBox | //+------------------------------------------------------------------+ @@ -483,6 +514,8 @@ RSIScalpingData rsTSLAData; RSIScalpingData rsXAUUSDData; SuperEMAData seData; RSIConsolidationData rcoData; +SimpleTrendlineData stBTCData; +SimpleTrendlineData stXAUData; //+------------------------------------------------------------------+ //| Global Variables - RSI Reversal Asian | @@ -570,6 +603,18 @@ int OnInit() RRA_AUDUSD_UseTakeProfit, RRA_AUDUSD_UseRSIExit, RRA_AUDUSD_RSIExitLevel, RRA_AUDUSD_CloseOutsideSession, RRA_AUDUSD_TimeFrame, RRA_AUDUSD_MagicNumber, RRA_AUDUSD_Slippage)) Print("Warning: RSIReversalAsianAUDUSD strategy failed to initialize for symbol '", RRA_AUDUSD_Symbol, "'"); + + if(EnableSimpleTrendlineBTCUSD) + if(!InitSimpleTrendline(stBTCData, ST_BTC_Symbol, ST_BTC_SignalTF, ST_BTC_HigherTF, ST_BTC_MAPeriod, + ST_BTC_MAMethod, ST_BTC_AppliedPrice, ST_BTC_HTFBarsToScan, + ST_BTC_LineTouchTolerance, ST_BTC_BreakBuffer, ST_BTC_MagicNumber, ST_BTC_DrawTrendline)) + Print("Warning: SimpleTrendlineBTCUSD failed to initialize for symbol '", ST_BTC_Symbol, "'"); + + if(EnableSimpleTrendlineXAUUSD) + if(!InitSimpleTrendline(stXAUData, ST_XAU_Symbol, ST_XAU_SignalTF, ST_XAU_HigherTF, ST_XAU_MAPeriod, + ST_XAU_MAMethod, ST_XAU_AppliedPrice, ST_XAU_HTFBarsToScan, + ST_XAU_LineTouchTolerance, ST_XAU_BreakBuffer, ST_XAU_MagicNumber, ST_XAU_DrawTrendline)) + Print("Warning: SimpleTrendlineXAUUSD failed to initialize for symbol '", ST_XAU_Symbol, "'"); Print("United EA initialized. Active strategies: ", (EnableDarvasBox ? "DarvasBox " : ""), @@ -584,7 +629,9 @@ int OnInit() (EnableSuperEMA ? "SuperEMA " : ""), (EnableRSIConsolidation ? "RSIConsolidation " : ""), (EnableRSIReversalAsianEURUSD ? "RSIReversalAsianEURUSD " : ""), - (EnableRSIReversalAsianAUDUSD ? "RSIReversalAsianAUDUSD " : "")); + (EnableRSIReversalAsianAUDUSD ? "RSIReversalAsianAUDUSD " : ""), + (EnableSimpleTrendlineBTCUSD ? "SimpleTrendlineBTCUSD " : ""), + (EnableSimpleTrendlineXAUUSD ? "SimpleTrendlineXAUUSD " : "")); return initResult; } @@ -632,6 +679,11 @@ void OnDeinit(const int reason) if(EnableRSIReversalAsianAUDUSD) DeinitRSIReversalAsian(rraAUDUSDData); + + if(EnableSimpleTrendlineBTCUSD) + DeinitSimpleTrendline(stBTCData); + if(EnableSimpleTrendlineXAUUSD) + DeinitSimpleTrendline(stXAUData); Print("United EA deinitialized. Reason: ", reason); } @@ -699,6 +751,11 @@ void OnTick() if(EnableRSIConsolidation) ProcessRSIConsolidation(rcoData, LOT_RCO_RSIConsolidation); + + if(EnableSimpleTrendlineBTCUSD) + ProcessSimpleTrendline(stBTCData, LOT_ST_BTCUSD); + if(EnableSimpleTrendlineXAUUSD) + ProcessSimpleTrendline(stXAUData, LOT_ST_XAUUSD); } //+------------------------------------------------------------------+ diff --git a/frontline/units/SimpleTrendlineBTCUSD/SimpleTrendline.mq5 b/frontline/units/SimpleTrendlineBTCUSD/SimpleTrendline.mq5 new file mode 100644 index 0000000..6476f77 --- /dev/null +++ b/frontline/units/SimpleTrendlineBTCUSD/SimpleTrendline.mq5 @@ -0,0 +1,284 @@ +#property strict +#property version "1.00" + +#include + +input ENUM_TIMEFRAMES InpHigherTF = PERIOD_H4; // Higher timeframe for MA/cross points +input int InpMAPeriod = 150; // MA period +input ENUM_MA_METHOD InpMAMethod = MODE_SMMA; // MA method +input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_OPEN; // MA applied price +input int InpHTFBarsToScan = 1200; // HTF bars to scan for crossings +input double InpLineTouchTolerance = 170; // Pullback touch tolerance (points) +input double InpBreakBuffer = 90; // Break confirmation buffer (points) +input double InpLots = 0.10; // Position size +input long InpMagic = 26042501; // Magic number +input bool InpDrawTrendline = true; // Draw detected trendline + +CTrade trade; + +int g_maHandle = INVALID_HANDLE; +datetime g_lastBarTime = 0; +string g_lineName = "SimpleTrendline_Basis"; + +struct TrendlineModel +{ + datetime t1; + datetime t2; + datetime t3; + double p1; + double p2; + double p3; + double a; + double b; + bool valid; +}; + +bool IsNewBar() +{ + datetime t = iTime(_Symbol, _Period, 0); + if(t == 0) + return false; + if(t != g_lastBarTime) + { + g_lastBarTime = t; + return true; + } + return false; +} + +int FindRecentCrossPoints(datetime ×[], double &prices[]) +{ + ArrayResize(times, 0); + ArrayResize(prices, 0); + + if(g_maHandle == INVALID_HANDLE) + return 0; + + int needBars = MathMax(InpHTFBarsToScan, InpMAPeriod + 20); + MqlRates rates[]; + double maBuf[]; + + int copiedRates = CopyRates(_Symbol, InpHigherTF, 0, needBars, rates); + int copiedMa = CopyBuffer(g_maHandle, 0, 0, needBars, maBuf); + if(copiedRates <= 5 || copiedMa <= 5) + return 0; + + int bars = MathMin(copiedRates, copiedMa); + ArraySetAsSeries(rates, true); + ArraySetAsSeries(maBuf, true); + + for(int i = 2; i < bars - 1; i++) + { + double d0 = rates[i].close - maBuf[i]; + double d1 = rates[i + 1].close - maBuf[i + 1]; + if(d0 == 0.0 || d1 == 0.0 || (d0 * d1 < 0.0)) + { + int n = ArraySize(times); + ArrayResize(times, n + 1); + ArrayResize(prices, n + 1); + times[n] = rates[i].time; + prices[n] = rates[i].close; + if(ArraySize(times) >= 3) + break; + } + } + + return ArraySize(times); +} + +bool BuildTrendlineFrom3Points(TrendlineModel &m) +{ + m.valid = false; + datetime ts[]; + double ps[]; + int n = FindRecentCrossPoints(ts, ps); + if(n < 3) + return false; + + // We collected from recent to older in series order. + // Re-map as oldest -> newest to stabilize slope direction. + datetime tOld[3]; + double pOld[3]; + for(int i = 0; i < 3; i++) + { + tOld[i] = ts[2 - i]; + pOld[i] = ps[2 - i]; + } + + long t0 = (long)tOld[0]; + double x1 = 0.0; + double x2 = (double)((long)tOld[1] - t0); + double x3 = (double)((long)tOld[2] - t0); + double y1 = pOld[0]; + double y2 = pOld[1]; + double y3 = pOld[2]; + + double sx = x1 + x2 + x3; + double sy = y1 + y2 + y3; + double sxx = x1 * x1 + x2 * x2 + x3 * x3; + double sxy = x1 * y1 + x2 * y2 + x3 * y3; + + double den = 3.0 * sxx - sx * sx; + if(MathAbs(den) < 1e-10) + return false; + + m.a = (3.0 * sxy - sx * sy) / den; + m.b = (sy - m.a * sx) / 3.0; + + m.t1 = tOld[0]; + m.t2 = tOld[1]; + m.t3 = tOld[2]; + m.p1 = pOld[0]; + m.p2 = pOld[1]; + m.p3 = pOld[2]; + m.valid = true; + return true; +} + +double TrendlinePriceAtTime(const TrendlineModel &m, datetime t) +{ + if(!m.valid) + return 0.0; + double x = (double)((long)t - (long)m.t1); + return m.a * x + m.b; +} + +void DrawTrendline(const TrendlineModel &m) +{ + if(!InpDrawTrendline || !m.valid) + return; + + datetime tStart = m.t1; + datetime tEnd = iTime(_Symbol, _Period, 0); + if(tEnd <= tStart) + tEnd = m.t3 + PeriodSeconds(_Period) * 20; + + double pStart = TrendlinePriceAtTime(m, tStart); + double pEnd = TrendlinePriceAtTime(m, tEnd); + + if(ObjectFind(0, g_lineName) < 0) + ObjectCreate(0, g_lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd); + else + { + ObjectMove(0, g_lineName, 0, tStart, pStart); + ObjectMove(0, g_lineName, 1, tEnd, pEnd); + } + + ObjectSetInteger(0, g_lineName, OBJPROP_RAY_RIGHT, true); + ObjectSetInteger(0, g_lineName, OBJPROP_COLOR, clrGold); + ObjectSetInteger(0, g_lineName, OBJPROP_WIDTH, 2); +} + +bool GetCurrentPosition(long &type, double &volume) +{ + if(!PositionSelect(_Symbol)) + return false; + if((long)PositionGetInteger(POSITION_MAGIC) != InpMagic) + return false; + type = PositionGetInteger(POSITION_TYPE); + volume = PositionGetDouble(POSITION_VOLUME); + return true; +} + +void TryExitOnBreak(const TrendlineModel &m) +{ + long posType; + double vol; + if(!GetCurrentPosition(posType, vol)) + return; + + double close1 = iClose(_Symbol, _Period, 1); + datetime t1 = iTime(_Symbol, _Period, 1); + double line1 = TrendlinePriceAtTime(m, t1); + double buf = InpBreakBuffer * _Point; + + bool closePos = false; + if(posType == POSITION_TYPE_BUY && close1 < (line1 - buf)) + closePos = true; + if(posType == POSITION_TYPE_SELL && close1 > (line1 + buf)) + closePos = true; + + if(closePos) + trade.PositionClose(_Symbol); +} + +void TryPullbackEntry(const TrendlineModel &m) +{ + long posType; + double vol; + if(GetCurrentPosition(posType, vol)) + return; + + MqlRates bars1[], bars2[]; + if(CopyRates(_Symbol, _Period, 1, 1, bars1) != 1) + return; + if(CopyRates(_Symbol, _Period, 2, 1, bars2) != 1) + return; + if(ArraySize(bars1) < 1 || ArraySize(bars2) < 1) + return; + + MqlRates b1 = bars1[0]; + MqlRates b2 = bars2[0]; + + double line1 = TrendlinePriceAtTime(m, b1.time); + double tol = InpLineTouchTolerance * _Point; + + bool upTrend = (m.a > 0.0); + bool downTrend = (m.a < 0.0); + + if(upTrend) + { + bool touched = (b1.low <= (line1 + tol)); + bool reclaim = (b1.close > line1); + bool bullish = (b1.close > b1.open); + bool stillHealthy = (b2.close >= TrendlinePriceAtTime(m, b2.time) - tol); + if(touched && reclaim && bullish && stillHealthy) + { + trade.Buy(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback buy"); + } + } + else if(downTrend) + { + bool touched = (b1.high >= (line1 - tol)); + bool reject = (b1.close < line1); + bool bearish = (b1.close < b1.open); + bool stillWeak = (b2.close <= TrendlinePriceAtTime(m, b2.time) + tol); + if(touched && reject && bearish && stillWeak) + { + trade.Sell(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback sell"); + } + } +} + +int OnInit() +{ + g_maHandle = iMA(_Symbol, InpHigherTF, InpMAPeriod, 0, InpMAMethod, InpAppliedPrice); + if(g_maHandle == INVALID_HANDLE) + return INIT_FAILED; + + trade.SetExpertMagicNumber(InpMagic); + g_lastBarTime = 0; + return INIT_SUCCEEDED; +} + +void OnDeinit(const int reason) +{ + if(g_maHandle != INVALID_HANDLE) + IndicatorRelease(g_maHandle); + if(ObjectFind(0, g_lineName) >= 0) + ObjectDelete(0, g_lineName); +} + +void OnTick() +{ + if(!IsNewBar()) + return; + + TrendlineModel m; + if(!BuildTrendlineFrom3Points(m)) + return; + + DrawTrendline(m); + TryExitOnBreak(m); + TryPullbackEntry(m); +} diff --git a/frontline/units/SimpleTrendlineBTCUSD/SimpleTrendline_optimization.set b/frontline/units/SimpleTrendlineBTCUSD/SimpleTrendline_optimization.set new file mode 100644 index 0000000..10ea2ee --- /dev/null +++ b/frontline/units/SimpleTrendlineBTCUSD/SimpleTrendline_optimization.set @@ -0,0 +1,14 @@ +; SimpleTrendline.mq5 optimization preset +; Strategy Tester -> Inputs -> Load +; Focus: trendline pullback entries + break exits (no broker SL/TP) +; +InpHigherTF=16385||16385||0||16388||Y +InpMAPeriod=50||20||5||200||Y +InpMAMethod=1||0||1||3||Y +InpAppliedPrice=0||0||1||6||Y +InpHTFBarsToScan=400||200||100||1200||Y +InpLineTouchTolerance=100.0||30.0||10.0||300.0||Y +InpBreakBuffer=30.0||5.0||5.0||120.0||Y +InpLots=0.10||0.10||0.01||0.10||N +InpMagic=26042501||26042501||1||26042501||N +InpDrawTrendline=false||false||0||true||N diff --git a/frontline/units/SimpleTrendlineGER40/SimpleTrendline.mq5 b/frontline/units/SimpleTrendlineGER40/SimpleTrendline.mq5 new file mode 100644 index 0000000..c87c3ef --- /dev/null +++ b/frontline/units/SimpleTrendlineGER40/SimpleTrendline.mq5 @@ -0,0 +1,284 @@ +#property strict +#property version "1.00" + +#include + +input ENUM_TIMEFRAMES InpHigherTF = PERIOD_M15; // Higher timeframe for MA/cross points +input int InpMAPeriod = 65; // MA period +input ENUM_MA_METHOD InpMAMethod = MODE_LWMA; // MA method +input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_OPEN; // MA applied price +input int InpHTFBarsToScan = 1200; // HTF bars to scan for crossings +input double InpLineTouchTolerance = 100; // Pullback touch tolerance (points) +input double InpBreakBuffer = 80; // Break confirmation buffer (points) +input double InpLots = 0.10; // Position size +input long InpMagic = 26042501; // Magic number +input bool InpDrawTrendline = true; // Draw detected trendline + +CTrade trade; + +int g_maHandle = INVALID_HANDLE; +datetime g_lastBarTime = 0; +string g_lineName = "SimpleTrendline_Basis"; + +struct TrendlineModel +{ + datetime t1; + datetime t2; + datetime t3; + double p1; + double p2; + double p3; + double a; + double b; + bool valid; +}; + +bool IsNewBar() +{ + datetime t = iTime(_Symbol, _Period, 0); + if(t == 0) + return false; + if(t != g_lastBarTime) + { + g_lastBarTime = t; + return true; + } + return false; +} + +int FindRecentCrossPoints(datetime ×[], double &prices[]) +{ + ArrayResize(times, 0); + ArrayResize(prices, 0); + + if(g_maHandle == INVALID_HANDLE) + return 0; + + int needBars = MathMax(InpHTFBarsToScan, InpMAPeriod + 20); + MqlRates rates[]; + double maBuf[]; + + int copiedRates = CopyRates(_Symbol, InpHigherTF, 0, needBars, rates); + int copiedMa = CopyBuffer(g_maHandle, 0, 0, needBars, maBuf); + if(copiedRates <= 5 || copiedMa <= 5) + return 0; + + int bars = MathMin(copiedRates, copiedMa); + ArraySetAsSeries(rates, true); + ArraySetAsSeries(maBuf, true); + + for(int i = 2; i < bars - 1; i++) + { + double d0 = rates[i].close - maBuf[i]; + double d1 = rates[i + 1].close - maBuf[i + 1]; + if(d0 == 0.0 || d1 == 0.0 || (d0 * d1 < 0.0)) + { + int n = ArraySize(times); + ArrayResize(times, n + 1); + ArrayResize(prices, n + 1); + times[n] = rates[i].time; + prices[n] = rates[i].close; + if(ArraySize(times) >= 3) + break; + } + } + + return ArraySize(times); +} + +bool BuildTrendlineFrom3Points(TrendlineModel &m) +{ + m.valid = false; + datetime ts[]; + double ps[]; + int n = FindRecentCrossPoints(ts, ps); + if(n < 3) + return false; + + // We collected from recent to older in series order. + // Re-map as oldest -> newest to stabilize slope direction. + datetime tOld[3]; + double pOld[3]; + for(int i = 0; i < 3; i++) + { + tOld[i] = ts[2 - i]; + pOld[i] = ps[2 - i]; + } + + long t0 = (long)tOld[0]; + double x1 = 0.0; + double x2 = (double)((long)tOld[1] - t0); + double x3 = (double)((long)tOld[2] - t0); + double y1 = pOld[0]; + double y2 = pOld[1]; + double y3 = pOld[2]; + + double sx = x1 + x2 + x3; + double sy = y1 + y2 + y3; + double sxx = x1 * x1 + x2 * x2 + x3 * x3; + double sxy = x1 * y1 + x2 * y2 + x3 * y3; + + double den = 3.0 * sxx - sx * sx; + if(MathAbs(den) < 1e-10) + return false; + + m.a = (3.0 * sxy - sx * sy) / den; + m.b = (sy - m.a * sx) / 3.0; + + m.t1 = tOld[0]; + m.t2 = tOld[1]; + m.t3 = tOld[2]; + m.p1 = pOld[0]; + m.p2 = pOld[1]; + m.p3 = pOld[2]; + m.valid = true; + return true; +} + +double TrendlinePriceAtTime(const TrendlineModel &m, datetime t) +{ + if(!m.valid) + return 0.0; + double x = (double)((long)t - (long)m.t1); + return m.a * x + m.b; +} + +void DrawTrendline(const TrendlineModel &m) +{ + if(!InpDrawTrendline || !m.valid) + return; + + datetime tStart = m.t1; + datetime tEnd = iTime(_Symbol, _Period, 0); + if(tEnd <= tStart) + tEnd = m.t3 + PeriodSeconds(_Period) * 20; + + double pStart = TrendlinePriceAtTime(m, tStart); + double pEnd = TrendlinePriceAtTime(m, tEnd); + + if(ObjectFind(0, g_lineName) < 0) + ObjectCreate(0, g_lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd); + else + { + ObjectMove(0, g_lineName, 0, tStart, pStart); + ObjectMove(0, g_lineName, 1, tEnd, pEnd); + } + + ObjectSetInteger(0, g_lineName, OBJPROP_RAY_RIGHT, true); + ObjectSetInteger(0, g_lineName, OBJPROP_COLOR, clrGold); + ObjectSetInteger(0, g_lineName, OBJPROP_WIDTH, 2); +} + +bool GetCurrentPosition(long &type, double &volume) +{ + if(!PositionSelect(_Symbol)) + return false; + if((long)PositionGetInteger(POSITION_MAGIC) != InpMagic) + return false; + type = PositionGetInteger(POSITION_TYPE); + volume = PositionGetDouble(POSITION_VOLUME); + return true; +} + +void TryExitOnBreak(const TrendlineModel &m) +{ + long posType; + double vol; + if(!GetCurrentPosition(posType, vol)) + return; + + double close1 = iClose(_Symbol, _Period, 1); + datetime t1 = iTime(_Symbol, _Period, 1); + double line1 = TrendlinePriceAtTime(m, t1); + double buf = InpBreakBuffer * _Point; + + bool closePos = false; + if(posType == POSITION_TYPE_BUY && close1 < (line1 - buf)) + closePos = true; + if(posType == POSITION_TYPE_SELL && close1 > (line1 + buf)) + closePos = true; + + if(closePos) + trade.PositionClose(_Symbol); +} + +void TryPullbackEntry(const TrendlineModel &m) +{ + long posType; + double vol; + if(GetCurrentPosition(posType, vol)) + return; + + MqlRates bars1[], bars2[]; + if(CopyRates(_Symbol, _Period, 1, 1, bars1) != 1) + return; + if(CopyRates(_Symbol, _Period, 2, 1, bars2) != 1) + return; + if(ArraySize(bars1) < 1 || ArraySize(bars2) < 1) + return; + + MqlRates b1 = bars1[0]; + MqlRates b2 = bars2[0]; + + double line1 = TrendlinePriceAtTime(m, b1.time); + double tol = InpLineTouchTolerance * _Point; + + bool upTrend = (m.a > 0.0); + bool downTrend = (m.a < 0.0); + + if(upTrend) + { + bool touched = (b1.low <= (line1 + tol)); + bool reclaim = (b1.close > line1); + bool bullish = (b1.close > b1.open); + bool stillHealthy = (b2.close >= TrendlinePriceAtTime(m, b2.time) - tol); + if(touched && reclaim && bullish && stillHealthy) + { + trade.Buy(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback buy"); + } + } + else if(downTrend) + { + bool touched = (b1.high >= (line1 - tol)); + bool reject = (b1.close < line1); + bool bearish = (b1.close < b1.open); + bool stillWeak = (b2.close <= TrendlinePriceAtTime(m, b2.time) + tol); + if(touched && reject && bearish && stillWeak) + { + trade.Sell(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback sell"); + } + } +} + +int OnInit() +{ + g_maHandle = iMA(_Symbol, InpHigherTF, InpMAPeriod, 0, InpMAMethod, InpAppliedPrice); + if(g_maHandle == INVALID_HANDLE) + return INIT_FAILED; + + trade.SetExpertMagicNumber(InpMagic); + g_lastBarTime = 0; + return INIT_SUCCEEDED; +} + +void OnDeinit(const int reason) +{ + if(g_maHandle != INVALID_HANDLE) + IndicatorRelease(g_maHandle); + if(ObjectFind(0, g_lineName) >= 0) + ObjectDelete(0, g_lineName); +} + +void OnTick() +{ + if(!IsNewBar()) + return; + + TrendlineModel m; + if(!BuildTrendlineFrom3Points(m)) + return; + + DrawTrendline(m); + TryExitOnBreak(m); + TryPullbackEntry(m); +} diff --git a/frontline/units/SimpleTrendlineGER40/SimpleTrendline_optimization.set b/frontline/units/SimpleTrendlineGER40/SimpleTrendline_optimization.set new file mode 100644 index 0000000..10ea2ee --- /dev/null +++ b/frontline/units/SimpleTrendlineGER40/SimpleTrendline_optimization.set @@ -0,0 +1,14 @@ +; SimpleTrendline.mq5 optimization preset +; Strategy Tester -> Inputs -> Load +; Focus: trendline pullback entries + break exits (no broker SL/TP) +; +InpHigherTF=16385||16385||0||16388||Y +InpMAPeriod=50||20||5||200||Y +InpMAMethod=1||0||1||3||Y +InpAppliedPrice=0||0||1||6||Y +InpHTFBarsToScan=400||200||100||1200||Y +InpLineTouchTolerance=100.0||30.0||10.0||300.0||Y +InpBreakBuffer=30.0||5.0||5.0||120.0||Y +InpLots=0.10||0.10||0.01||0.10||N +InpMagic=26042501||26042501||1||26042501||N +InpDrawTrendline=false||false||0||true||N diff --git a/frontline/units/SimpleTrendlineXAUUSD/SimpleTrendline.mq5 b/frontline/units/SimpleTrendlineXAUUSD/SimpleTrendline.mq5 new file mode 100644 index 0000000..c869eeb --- /dev/null +++ b/frontline/units/SimpleTrendlineXAUUSD/SimpleTrendline.mq5 @@ -0,0 +1,284 @@ +#property strict +#property version "1.00" + +#include + +input ENUM_TIMEFRAMES InpHigherTF = PERIOD_M10; // Higher timeframe for MA/cross points +input int InpMAPeriod = 65; // MA period +input ENUM_MA_METHOD InpMAMethod = MODE_EMA; // MA method +input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_OPEN; // MA applied price +input int InpHTFBarsToScan = 500; // HTF bars to scan for crossings +input double InpLineTouchTolerance = 220; // Pullback touch tolerance (points) +input double InpBreakBuffer = 110; // Break confirmation buffer (points) +input double InpLots = 0.10; // Position size +input long InpMagic = 26042501; // Magic number +input bool InpDrawTrendline = true; // Draw detected trendline + +CTrade trade; + +int g_maHandle = INVALID_HANDLE; +datetime g_lastBarTime = 0; +string g_lineName = "SimpleTrendline_Basis"; + +struct TrendlineModel +{ + datetime t1; + datetime t2; + datetime t3; + double p1; + double p2; + double p3; + double a; + double b; + bool valid; +}; + +bool IsNewBar() +{ + datetime t = iTime(_Symbol, _Period, 0); + if(t == 0) + return false; + if(t != g_lastBarTime) + { + g_lastBarTime = t; + return true; + } + return false; +} + +int FindRecentCrossPoints(datetime ×[], double &prices[]) +{ + ArrayResize(times, 0); + ArrayResize(prices, 0); + + if(g_maHandle == INVALID_HANDLE) + return 0; + + int needBars = MathMax(InpHTFBarsToScan, InpMAPeriod + 20); + MqlRates rates[]; + double maBuf[]; + + int copiedRates = CopyRates(_Symbol, InpHigherTF, 0, needBars, rates); + int copiedMa = CopyBuffer(g_maHandle, 0, 0, needBars, maBuf); + if(copiedRates <= 5 || copiedMa <= 5) + return 0; + + int bars = MathMin(copiedRates, copiedMa); + ArraySetAsSeries(rates, true); + ArraySetAsSeries(maBuf, true); + + for(int i = 2; i < bars - 1; i++) + { + double d0 = rates[i].close - maBuf[i]; + double d1 = rates[i + 1].close - maBuf[i + 1]; + if(d0 == 0.0 || d1 == 0.0 || (d0 * d1 < 0.0)) + { + int n = ArraySize(times); + ArrayResize(times, n + 1); + ArrayResize(prices, n + 1); + times[n] = rates[i].time; + prices[n] = rates[i].close; + if(ArraySize(times) >= 3) + break; + } + } + + return ArraySize(times); +} + +bool BuildTrendlineFrom3Points(TrendlineModel &m) +{ + m.valid = false; + datetime ts[]; + double ps[]; + int n = FindRecentCrossPoints(ts, ps); + if(n < 3) + return false; + + // We collected from recent to older in series order. + // Re-map as oldest -> newest to stabilize slope direction. + datetime tOld[3]; + double pOld[3]; + for(int i = 0; i < 3; i++) + { + tOld[i] = ts[2 - i]; + pOld[i] = ps[2 - i]; + } + + long t0 = (long)tOld[0]; + double x1 = 0.0; + double x2 = (double)((long)tOld[1] - t0); + double x3 = (double)((long)tOld[2] - t0); + double y1 = pOld[0]; + double y2 = pOld[1]; + double y3 = pOld[2]; + + double sx = x1 + x2 + x3; + double sy = y1 + y2 + y3; + double sxx = x1 * x1 + x2 * x2 + x3 * x3; + double sxy = x1 * y1 + x2 * y2 + x3 * y3; + + double den = 3.0 * sxx - sx * sx; + if(MathAbs(den) < 1e-10) + return false; + + m.a = (3.0 * sxy - sx * sy) / den; + m.b = (sy - m.a * sx) / 3.0; + + m.t1 = tOld[0]; + m.t2 = tOld[1]; + m.t3 = tOld[2]; + m.p1 = pOld[0]; + m.p2 = pOld[1]; + m.p3 = pOld[2]; + m.valid = true; + return true; +} + +double TrendlinePriceAtTime(const TrendlineModel &m, datetime t) +{ + if(!m.valid) + return 0.0; + double x = (double)((long)t - (long)m.t1); + return m.a * x + m.b; +} + +void DrawTrendline(const TrendlineModel &m) +{ + if(!InpDrawTrendline || !m.valid) + return; + + datetime tStart = m.t1; + datetime tEnd = iTime(_Symbol, _Period, 0); + if(tEnd <= tStart) + tEnd = m.t3 + PeriodSeconds(_Period) * 20; + + double pStart = TrendlinePriceAtTime(m, tStart); + double pEnd = TrendlinePriceAtTime(m, tEnd); + + if(ObjectFind(0, g_lineName) < 0) + ObjectCreate(0, g_lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd); + else + { + ObjectMove(0, g_lineName, 0, tStart, pStart); + ObjectMove(0, g_lineName, 1, tEnd, pEnd); + } + + ObjectSetInteger(0, g_lineName, OBJPROP_RAY_RIGHT, true); + ObjectSetInteger(0, g_lineName, OBJPROP_COLOR, clrGold); + ObjectSetInteger(0, g_lineName, OBJPROP_WIDTH, 2); +} + +bool GetCurrentPosition(long &type, double &volume) +{ + if(!PositionSelect(_Symbol)) + return false; + if((long)PositionGetInteger(POSITION_MAGIC) != InpMagic) + return false; + type = PositionGetInteger(POSITION_TYPE); + volume = PositionGetDouble(POSITION_VOLUME); + return true; +} + +void TryExitOnBreak(const TrendlineModel &m) +{ + long posType; + double vol; + if(!GetCurrentPosition(posType, vol)) + return; + + double close1 = iClose(_Symbol, _Period, 1); + datetime t1 = iTime(_Symbol, _Period, 1); + double line1 = TrendlinePriceAtTime(m, t1); + double buf = InpBreakBuffer * _Point; + + bool closePos = false; + if(posType == POSITION_TYPE_BUY && close1 < (line1 - buf)) + closePos = true; + if(posType == POSITION_TYPE_SELL && close1 > (line1 + buf)) + closePos = true; + + if(closePos) + trade.PositionClose(_Symbol); +} + +void TryPullbackEntry(const TrendlineModel &m) +{ + long posType; + double vol; + if(GetCurrentPosition(posType, vol)) + return; + + MqlRates bars1[], bars2[]; + if(CopyRates(_Symbol, _Period, 1, 1, bars1) != 1) + return; + if(CopyRates(_Symbol, _Period, 2, 1, bars2) != 1) + return; + if(ArraySize(bars1) < 1 || ArraySize(bars2) < 1) + return; + + MqlRates b1 = bars1[0]; + MqlRates b2 = bars2[0]; + + double line1 = TrendlinePriceAtTime(m, b1.time); + double tol = InpLineTouchTolerance * _Point; + + bool upTrend = (m.a > 0.0); + bool downTrend = (m.a < 0.0); + + if(upTrend) + { + bool touched = (b1.low <= (line1 + tol)); + bool reclaim = (b1.close > line1); + bool bullish = (b1.close > b1.open); + bool stillHealthy = (b2.close >= TrendlinePriceAtTime(m, b2.time) - tol); + if(touched && reclaim && bullish && stillHealthy) + { + trade.Buy(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback buy"); + } + } + else if(downTrend) + { + bool touched = (b1.high >= (line1 - tol)); + bool reject = (b1.close < line1); + bool bearish = (b1.close < b1.open); + bool stillWeak = (b2.close <= TrendlinePriceAtTime(m, b2.time) + tol); + if(touched && reject && bearish && stillWeak) + { + trade.Sell(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback sell"); + } + } +} + +int OnInit() +{ + g_maHandle = iMA(_Symbol, InpHigherTF, InpMAPeriod, 0, InpMAMethod, InpAppliedPrice); + if(g_maHandle == INVALID_HANDLE) + return INIT_FAILED; + + trade.SetExpertMagicNumber(InpMagic); + g_lastBarTime = 0; + return INIT_SUCCEEDED; +} + +void OnDeinit(const int reason) +{ + if(g_maHandle != INVALID_HANDLE) + IndicatorRelease(g_maHandle); + if(ObjectFind(0, g_lineName) >= 0) + ObjectDelete(0, g_lineName); +} + +void OnTick() +{ + if(!IsNewBar()) + return; + + TrendlineModel m; + if(!BuildTrendlineFrom3Points(m)) + return; + + DrawTrendline(m); + TryExitOnBreak(m); + TryPullbackEntry(m); +} diff --git a/frontline/units/SimpleTrendlineXAUUSD/SimpleTrendline_optimization.set b/frontline/units/SimpleTrendlineXAUUSD/SimpleTrendline_optimization.set new file mode 100644 index 0000000..10ea2ee --- /dev/null +++ b/frontline/units/SimpleTrendlineXAUUSD/SimpleTrendline_optimization.set @@ -0,0 +1,14 @@ +; SimpleTrendline.mq5 optimization preset +; Strategy Tester -> Inputs -> Load +; Focus: trendline pullback entries + break exits (no broker SL/TP) +; +InpHigherTF=16385||16385||0||16388||Y +InpMAPeriod=50||20||5||200||Y +InpMAMethod=1||0||1||3||Y +InpAppliedPrice=0||0||1||6||Y +InpHTFBarsToScan=400||200||100||1200||Y +InpLineTouchTolerance=100.0||30.0||10.0||300.0||Y +InpBreakBuffer=30.0||5.0||5.0||120.0||Y +InpLots=0.10||0.10||0.01||0.10||N +InpMagic=26042501||26042501||1||26042501||N +InpDrawTrendline=false||false||0||true||N diff --git a/lab/EAs/SimpleTrendline.mq5 b/lab/EAs/SimpleTrendline.mq5 deleted file mode 100644 index e69de29..0000000