Update
This commit is contained in:
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user