This commit is contained in:
zhutoutoutousan
2026-04-15 23:38:45 +02:00
parent b50b430d1a
commit de5263de32
40 changed files with 6578 additions and 54 deletions
+378
View File
@@ -0,0 +1,378 @@
#property strict
#property version "1.00"
#include <Trade/Trade.mqh>
input group "=== Common ==="
input string InpSymbol = "BTCUSD";
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M15;
input int InpSlippagePoints = 30;
input int InpPivotLookbackBars = 120;
input int InpMinSwingPoints = 500;
input group "=== Robot 1: Fibonacci Retracement ==="
input bool FR_Enabled = true;
input int FR_Magic = 920101;
input double FR_Lots = 0.01;
input bool FR_BuyAt618 = true;
input bool FR_BuyAt500 = false;
input bool FR_UseHardSLTP = true;
input double FR_SL_BufferPoints = 400;
input double FR_TP_BufferPoints = 400;
input int FR_MaxHoldingBars = 96; // time-stop safety
input bool FR_CloseOnStructureBreak = true; // close if recent swing low breaks
input group "=== Robot 2: Fibonacci Trend Extension ==="
input bool FE_Enabled = true;
input int FE_Magic = 920202;
input double FE_Lots = 0.01;
input bool FE_UseHardSLTP = true;
input double FE_SL_BufferPoints = 400;
input double FE_ExtensionLevel = 1.272; // Common values: 1.272 / 1.618
input int FE_MinBarsBetweenTrades = 6;
input double FE_MinStopPoints = 3000;
input int FE_AtrPeriod = 14;
input double FE_MinStopAtrMult = 1.2;
input double FE_MinRR = 1.5;
CTrade trade;
datetime g_lastBarTime = 0;
datetime g_lastFEEntryTime = 0;
datetime g_lastFREntryTime = 0;
bool IsNewBar(const string symbol, ENUM_TIMEFRAMES tf)
{
datetime t = iTime(symbol, tf, 0);
if(t <= 0 || t == g_lastBarTime)
return false;
g_lastBarTime = t;
return true;
}
bool GetLowestLow(const string symbol, ENUM_TIMEFRAMES tf, const int bars, int &idx, double &price)
{
idx = iLowest(symbol, tf, MODE_LOW, bars, 1);
if(idx < 0)
return false;
price = iLow(symbol, tf, idx);
return (price > 0.0);
}
bool GetHighestHigh(const string symbol, ENUM_TIMEFRAMES tf, const int bars, int &idx, double &price)
{
idx = iHighest(symbol, tf, MODE_HIGH, bars, 1);
if(idx < 0)
return false;
price = iHigh(symbol, tf, idx);
return (price > 0.0);
}
double GetAtrPrice(const string symbol, ENUM_TIMEFRAMES tf, const int period)
{
int hAtr = iATR(symbol, tf, period);
if(hAtr == INVALID_HANDLE)
return 0.0;
double b[1];
if(CopyBuffer(hAtr, 0, 1, 1, b) <= 0)
{
IndicatorRelease(hAtr);
return 0.0;
}
IndicatorRelease(hAtr);
return b[0];
}
bool PositionExistsByMagic(const string symbol, const int magic)
{
for(int i = PositionsTotal() - 1; i >= 0; --i)
{
ulong t = PositionGetTicket(i);
if(t == 0)
continue;
if(PositionGetString(POSITION_SYMBOL) == symbol &&
(int)PositionGetInteger(POSITION_MAGIC) == magic)
return true;
}
return false;
}
bool GetPositionByMagic(const string symbol, const int magic, ulong &ticket, ENUM_POSITION_TYPE &posType, datetime &openTime)
{
for(int i = PositionsTotal() - 1; i >= 0; --i)
{
ulong t = PositionGetTicket(i);
if(t == 0)
continue;
if(PositionGetString(POSITION_SYMBOL) == symbol &&
(int)PositionGetInteger(POSITION_MAGIC) == magic)
{
ticket = t;
posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
openTime = (datetime)PositionGetInteger(POSITION_TIME);
return true;
}
}
return false;
}
double NormalizePrice(const string symbol, const double price)
{
int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
return NormalizeDouble(price, digits);
}
bool ValidateAndAdjustStops(const bool isBuy, double &sl, double &tp)
{
if(sl == 0.0 && tp == 0.0)
return true;
MqlTick tick;
if(!SymbolInfoTick(InpSymbol, tick))
return false;
int stopsLevelPts = (int)SymbolInfoInteger(InpSymbol, SYMBOL_TRADE_STOPS_LEVEL);
int freezeLevelPts = (int)SymbolInfoInteger(InpSymbol, SYMBOL_TRADE_FREEZE_LEVEL);
double minDist = (double)MathMax(stopsLevelPts, freezeLevelPts) * _Point + 2.0 * _Point;
if(isBuy)
{
if(sl > 0.0 && sl >= tick.bid - minDist)
sl = tick.bid - minDist;
if(tp > 0.0 && tp <= tick.ask + minDist)
tp = tick.ask + minDist;
if(sl > 0.0 && sl >= tick.bid)
return false;
if(tp > 0.0 && tp <= tick.ask)
return false;
}
else
{
if(sl > 0.0 && sl <= tick.ask + minDist)
sl = tick.ask + minDist;
if(tp > 0.0 && tp >= tick.bid - minDist)
tp = tick.bid - minDist;
if(sl > 0.0 && sl <= tick.ask)
return false;
if(tp > 0.0 && tp >= tick.bid)
return false;
}
if(sl > 0.0)
sl = NormalizePrice(InpSymbol, sl);
if(tp > 0.0)
tp = NormalizePrice(InpSymbol, tp);
return true;
}
bool OpenBuy(const int magic, const double lots, const string comment, const double sl, const double tp)
{
MqlTick tick;
if(!SymbolInfoTick(InpSymbol, tick))
return false;
double useSL = sl, useTP = tp;
if(!ValidateAndAdjustStops(true, useSL, useTP))
return false;
trade.SetExpertMagicNumber(magic);
bool ok = trade.Buy(lots, InpSymbol, tick.ask, useSL, useTP, comment);
if(ok && magic == FR_Magic)
g_lastFREntryTime = iTime(InpSymbol, InpTimeframe, 0);
if(ok && magic == FE_Magic)
g_lastFEEntryTime = iTime(InpSymbol, InpTimeframe, 0);
return ok;
}
bool OpenSell(const int magic, const double lots, const string comment, const double sl, const double tp)
{
MqlTick tick;
if(!SymbolInfoTick(InpSymbol, tick))
return false;
double useSL = sl, useTP = tp;
if(!ValidateAndAdjustStops(false, useSL, useTP))
return false;
trade.SetExpertMagicNumber(magic);
bool ok = trade.Sell(lots, InpSymbol, tick.bid, useSL, useTP, comment);
if(ok && magic == FE_Magic)
g_lastFEEntryTime = iTime(InpSymbol, InpTimeframe, 0);
return ok;
}
void RunFibonacciRetracement()
{
if(!FR_Enabled)
return;
if(PositionExistsByMagic(InpSymbol, FR_Magic))
return;
int idxLow = -1, idxHigh = -1;
double swingLow = 0.0, swingHigh = 0.0;
if(!GetLowestLow(InpSymbol, InpTimeframe, InpPivotLookbackBars, idxLow, swingLow))
return;
if(!GetHighestHigh(InpSymbol, InpTimeframe, InpPivotLookbackBars, idxHigh, swingHigh))
return;
double rangePts = (swingHigh - swingLow) / _Point;
if(rangePts < InpMinSwingPoints)
return;
// Uptrend retracement model: low appears before high.
bool upSwing = (idxLow > idxHigh);
if(!upSwing)
return;
double fib50 = swingHigh - (swingHigh - swingLow) * 0.500;
double fib61 = swingHigh - (swingHigh - swingLow) * 0.618;
MqlTick tick;
if(!SymbolInfoTick(InpSymbol, tick))
return;
double sl = 0.0, tp = 0.0;
if(FR_UseHardSLTP)
{
// Positional levels: SL below swing low, TP near prior swing high breakout.
sl = swingLow - FR_SL_BufferPoints * _Point;
tp = swingHigh + FR_TP_BufferPoints * _Point;
}
if(FR_BuyAt618 && tick.ask <= fib61)
OpenBuy(FR_Magic, FR_Lots, "FiboRetrace-61.8 Buy", sl, tp);
else if(FR_BuyAt500 && tick.ask <= fib50)
OpenBuy(FR_Magic, FR_Lots, "FiboRetrace-50.0 Buy", sl, tp);
}
void ManageFibonacciRetracementExit()
{
if(!FR_Enabled)
return;
ulong ticket = 0;
ENUM_POSITION_TYPE posType = WRONG_VALUE;
datetime openTime = 0;
if(!GetPositionByMagic(InpSymbol, FR_Magic, ticket, posType, openTime))
return;
int tfSec = PeriodSeconds(InpTimeframe);
if(tfSec <= 0)
tfSec = 60;
int barsHeld = (int)((iTime(InpSymbol, InpTimeframe, 0) - openTime) / tfSec);
// 1) Time stop: force close stale retracement trades.
if(FR_MaxHoldingBars > 0 && barsHeld >= FR_MaxHoldingBars)
{
trade.PositionClose(ticket);
return;
}
// 2) Structure invalidation: if latest swing violates the trade idea, exit.
if(FR_CloseOnStructureBreak)
{
int idxLow = -1, idxHigh = -1;
double swingLow = 0.0, swingHigh = 0.0;
if(GetLowestLow(InpSymbol, InpTimeframe, InpPivotLookbackBars, idxLow, swingLow) &&
GetHighestHigh(InpSymbol, InpTimeframe, InpPivotLookbackBars, idxHigh, swingHigh))
{
MqlTick tick;
if(SymbolInfoTick(InpSymbol, tick))
{
double invalidateBuffer = FR_SL_BufferPoints * _Point;
if(posType == POSITION_TYPE_BUY && tick.bid < (swingLow - invalidateBuffer))
trade.PositionClose(ticket);
else if(posType == POSITION_TYPE_SELL && tick.ask > (swingHigh + invalidateBuffer))
trade.PositionClose(ticket);
}
}
}
}
void RunFibonacciExtension()
{
if(!FE_Enabled)
return;
if(PositionExistsByMagic(InpSymbol, FE_Magic))
return;
if(g_lastFEEntryTime > 0)
{
int tfSec = PeriodSeconds(InpTimeframe);
if(tfSec > 0)
{
int barsSince = (int)((iTime(InpSymbol, InpTimeframe, 0) - g_lastFEEntryTime) / tfSec);
if(barsSince < FE_MinBarsBetweenTrades)
return;
}
}
int idxLow = -1, idxHigh = -1;
double swingLow = 0.0, swingHigh = 0.0;
if(!GetLowestLow(InpSymbol, InpTimeframe, InpPivotLookbackBars, idxLow, swingLow))
return;
if(!GetHighestHigh(InpSymbol, InpTimeframe, InpPivotLookbackBars, idxHigh, swingHigh))
return;
double rangePts = (swingHigh - swingLow) / _Point;
if(rangePts < InpMinSwingPoints)
return;
MqlTick tick;
if(!SymbolInfoTick(InpSymbol, tick))
return;
// Continuation breakout model:
// - If up swing (low before high), buy above swing high and target extension.
// - If down swing (high before low), sell below swing low and target extension.
bool upSwing = (idxLow > idxHigh);
if(upSwing && tick.ask > swingHigh)
{
double sl = 0.0, tp = 0.0;
if(FE_UseHardSLTP)
{
sl = swingHigh - FE_SL_BufferPoints * _Point;
double extTP = swingLow + (swingHigh - swingLow) * FE_ExtensionLevel;
double atr = GetAtrPrice(InpSymbol, InpTimeframe, FE_AtrPeriod);
double minRisk = MathMax(FE_MinStopPoints * _Point, atr * FE_MinStopAtrMult);
double risk = tick.ask - sl;
if(risk < minRisk)
return; // Skip fragile entries with overly tight stop.
double rrTP = tick.ask + risk * FE_MinRR;
tp = MathMax(extTP, rrTP);
}
OpenBuy(FE_Magic, FE_Lots, "FiboExtension Buy", sl, tp);
}
else if(!upSwing && tick.bid < swingLow)
{
double sl = 0.0, tp = 0.0;
if(FE_UseHardSLTP)
{
sl = swingLow + FE_SL_BufferPoints * _Point;
double extTP = swingHigh - (swingHigh - swingLow) * FE_ExtensionLevel;
double atr = GetAtrPrice(InpSymbol, InpTimeframe, FE_AtrPeriod);
double minRisk = MathMax(FE_MinStopPoints * _Point, atr * FE_MinStopAtrMult);
double risk = sl - tick.bid;
if(risk < minRisk)
return; // Skip fragile entries with overly tight stop.
double rrTP = tick.bid - risk * FE_MinRR;
tp = MathMin(extTP, rrTP);
}
OpenSell(FE_Magic, FE_Lots, "FiboExtension Sell", sl, tp);
}
}
int OnInit()
{
if(!SymbolSelect(InpSymbol, true))
return(INIT_FAILED);
trade.SetDeviationInPoints(InpSlippagePoints);
return(INIT_SUCCEEDED);
}
void OnTick()
{
if(_Symbol != InpSymbol)
return;
if(!IsNewBar(InpSymbol, InpTimeframe))
return;
ManageFibonacciRetracementExit();
RunFibonacciRetracement();
RunFibonacciExtension();
}