- MultiSignal Confluence EA v1.11 (stop loss bug fixed) - Harmonic Pattern Finder v2 (optimized) - Candlestick Pattern EA (fixed) - Pivot Fade EA v4 (fixed) - Bot series (10001, 10002, EnhancedEA) Performance: ~19% return in 2 months on 00k account
372 lines
27 KiB
Plaintext
Executable File
372 lines
27 KiB
Plaintext
Executable File
//+------------------------------------------------------------------+
|
|
//| FadePivot2_v2.mq5 |
|
|
//| Garfield Heron / Abbey Road Tech |
|
|
//| https://abbeyroadtechnology.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "© 2024"
|
|
#property link "https://abbeyroadtechnology.com"
|
|
#property version "1.75"
|
|
#property strict
|
|
|
|
/***** User Inputs *****/
|
|
input double LotSize = 0.01; // Lot size for trades
|
|
input int Slippage = 3; // Max slippage in points
|
|
input int TP_OffsetPips = 0; // +/- offset from R1 or S1 (in pips)
|
|
input double StopLossFactor = 1.0; // Multiplier for (R2 - R1) or (S1 - S2)
|
|
input double TakeProfitFactor = 1.0; // Multiplier for take-profit
|
|
input bool OnlyOneSetPerDay = true; // If true, place only once per day
|
|
input bool AllowNewOrdersIfPreviousOpen = true; // Place new orders even if old remain
|
|
input bool CloseEndOfDay = true; // If true, do EoD cleanup
|
|
input bool CloseOnlyPendingEndOfDay = false; // If true => close only pending orders at EoD
|
|
input bool CloseOpenOrdersEndOfDay = false; // If true, close open positions at EoD (only if not close-only-pending)
|
|
input long MagicNumber = 98765; // Magic number for this EA
|
|
|
|
/***** New Day-of-Week Inputs *****/
|
|
input bool TradeMonday = true; // If true, trade on Mondays
|
|
input bool TradeTuesday = true; // If true, trade on Tuesdays
|
|
input bool TradeWednesday = true; // ...
|
|
input bool TradeThursday = true;
|
|
input bool TradeFriday = true;
|
|
input bool TradeSaturday = false; // Typically false
|
|
input bool TradeSunday = false; // Typically false
|
|
|
|
/***** Global Variables *****/
|
|
static datetime TradeDate = 0; // Tracks the current day
|
|
static bool OrdersPlaced = false;
|
|
|
|
/***** Pivots *****/
|
|
static double P, R1, R2, S1, S2;
|
|
|
|
/***** Prototypes *****/
|
|
void CalculateDailyPivots();
|
|
void PlaceFadeOrders();
|
|
bool HasOpenOrPendingWithMagic(long magic);
|
|
bool CloseAllByMagic(long magic);
|
|
void PlaceLimitOrder(ENUM_ORDER_TYPE orderType, double entryPrice, double slPrice, double tpPrice);
|
|
|
|
bool IsTradingDay(); // helper to decide if current day-of-week is allowed
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
Print("FadePivot2_v2 EA initializing (with day-of-week filter)...");
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
Print("FadePivot2_v2 EA deinitialized. reason=", reason);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| OnTick |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
// 0) Check if it's an allowed trading day
|
|
if(!IsTradingDay())
|
|
{
|
|
// If it's not a day to trade, we can skip logic or still do pivot calc
|
|
// For example, we skip everything:
|
|
return;
|
|
}
|
|
|
|
// 1) Check for a new day (the 0th D1 bar changed)
|
|
datetime today = iTime(_Symbol, PERIOD_D1, 0);
|
|
if(today != TradeDate)
|
|
{
|
|
// New day => recalc pivots, reset flags
|
|
CalculateDailyPivots();
|
|
TradeDate = today;
|
|
OrdersPlaced = false;
|
|
Print("New daily bar. Pivots recalculated. R2=", R2, ", S2=", S2);
|
|
}
|
|
|
|
// 2) If we haven't placed orders for this day, do so now
|
|
if(!OrdersPlaced)
|
|
{
|
|
if(!OnlyOneSetPerDay)
|
|
{
|
|
// If OnlyOneSetPerDay=false, place new orders daily
|
|
PlaceFadeOrders();
|
|
OrdersPlaced = true;
|
|
}
|
|
else
|
|
{
|
|
// If OnlyOneSetPerDay=true, skip if an open/pending trade
|
|
// unless AllowNewOrdersIfPreviousOpen==true
|
|
if(!HasOpenOrPendingWithMagic(MagicNumber) || AllowNewOrdersIfPreviousOpen)
|
|
{
|
|
PlaceFadeOrders();
|
|
OrdersPlaced = true;
|
|
}
|
|
else
|
|
{
|
|
Print("Skipping new orders because OnlyOneSetPerDay=true ",
|
|
"and we already have open/pending trades (Magic=", MagicNumber, ").");
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3) EoD close logic
|
|
if(CloseEndOfDay)
|
|
{
|
|
MqlDateTime dt;
|
|
TimeToStruct(TimeCurrent(), dt);
|
|
// Example: close everything at 23:59
|
|
if(dt.hour == 23 && dt.min == 59)
|
|
{
|
|
Print("FadePivot2_v2: End-of-Day => cleaning up trades/orders...");
|
|
CloseAllByMagic(MagicNumber);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Check if current day-of-week is allowed for trading |
|
|
//+------------------------------------------------------------------+
|
|
bool IsTradingDay()
|
|
{
|
|
MqlDateTime dt;
|
|
TimeToStruct(TimeCurrent(), dt);
|
|
int dayOfWeek = dt.day_of_week; // 0=Sunday, 1=Monday,...6=Saturday
|
|
switch(dayOfWeek)
|
|
{
|
|
case 0: return TradeSunday;
|
|
case 1: return TradeMonday;
|
|
case 2: return TradeTuesday;
|
|
case 3: return TradeWednesday;
|
|
case 4: return TradeThursday;
|
|
case 5: return TradeFriday;
|
|
case 6: return TradeSaturday;
|
|
}
|
|
// fallback
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| CalculateDailyPivots |
|
|
//+------------------------------------------------------------------+
|
|
void CalculateDailyPivots()
|
|
{
|
|
int shift = 1; // "yesterday"
|
|
double prevHigh = iHigh(_Symbol, PERIOD_D1, shift);
|
|
double prevLow = iLow(_Symbol, PERIOD_D1, shift);
|
|
double prevClose= iClose(_Symbol, PERIOD_D1, shift);
|
|
|
|
P = (prevHigh + prevLow + prevClose)/3.0;
|
|
R1 = 2.0*P - prevLow;
|
|
S1 = 2.0*P - prevHigh;
|
|
R2 = P + (prevHigh - prevLow);
|
|
S2 = P - (prevHigh - prevLow);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| PlaceFadeOrders |
|
|
//+------------------------------------------------------------------+
|
|
void PlaceFadeOrders()
|
|
{
|
|
int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
|
double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
|
|
|
// SELL LIMIT at R2
|
|
{
|
|
double entryPrice = NormalizeDouble(R2, digits);
|
|
double distanceR1R2 = MathAbs(R2 - R1);
|
|
double slPrice = R2 + (distanceR1R2 * StopLossFactor);
|
|
double tpPrice = (R1 * TakeProfitFactor) + (TP_OffsetPips * pt);
|
|
|
|
Print("Placing SELL LIMIT at R2= ", entryPrice,
|
|
", SL= ", slPrice, ", TP= ", tpPrice);
|
|
PlaceLimitOrder(ORDER_TYPE_SELL_LIMIT, entryPrice, slPrice, tpPrice);
|
|
}
|
|
|
|
// BUY LIMIT at S2
|
|
{
|
|
double entryPrice = NormalizeDouble(S2, digits);
|
|
double distanceS1S2 = MathAbs(S1 - S2);
|
|
double slPrice = S2 - (distanceS1S2 * StopLossFactor);
|
|
double tpPrice = (S1 * TakeProfitFactor) + (TP_OffsetPips * pt);
|
|
|
|
Print("Placing BUY LIMIT at S2= ", entryPrice,
|
|
", SL= ", slPrice, ", TP= ", tpPrice);
|
|
PlaceLimitOrder(ORDER_TYPE_BUY_LIMIT, entryPrice, slPrice, tpPrice);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| PlaceLimitOrder - Helper for pending orders |
|
|
//+------------------------------------------------------------------+
|
|
void PlaceLimitOrder(ENUM_ORDER_TYPE orderType,
|
|
double entryPrice,
|
|
double slPrice,
|
|
double tpPrice)
|
|
{
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
ZeroMemory(request);
|
|
ZeroMemory(result);
|
|
|
|
request.action = TRADE_ACTION_PENDING;
|
|
request.symbol = _Symbol;
|
|
request.magic = MagicNumber;
|
|
request.volume = LotSize;
|
|
request.deviation = Slippage;
|
|
request.type = orderType;
|
|
request.price = entryPrice;
|
|
request.sl = slPrice;
|
|
request.tp = tpPrice;
|
|
request.type_filling = ORDER_FILLING_FOK;
|
|
request.type_time = ORDER_TIME_GTC;
|
|
request.comment = "FadePivot2_v2";
|
|
|
|
// Additional checks (optional):
|
|
// e.g. check volume validity, check margin, check limit price validity
|
|
|
|
if(!OrderSend(request, result))
|
|
{
|
|
Print(__FUNCTION__, ": OrderSend failed. LastErr=", GetLastError());
|
|
return;
|
|
}
|
|
if(result.retcode == TRADE_RETCODE_PLACED || result.retcode == TRADE_RETCODE_DONE)
|
|
{
|
|
Print("Limit order placed: type=",
|
|
(orderType==ORDER_TYPE_SELL_LIMIT?"SellLimit":"BuyLimit"),
|
|
", ticket=", result.order);
|
|
}
|
|
else
|
|
{
|
|
Print("Limit order retcode=", result.retcode, ", lastErr=", GetLastError());
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| HasOpenOrPendingWithMagic |
|
|
//+------------------------------------------------------------------+
|
|
bool HasOpenOrPendingWithMagic(long magic)
|
|
{
|
|
// check open positions
|
|
if(PositionSelect(_Symbol))
|
|
{
|
|
if((long)PositionGetInteger(POSITION_MAGIC) == magic)
|
|
return(true);
|
|
}
|
|
|
|
// check pending orders
|
|
int totalOrders = (int)OrdersTotal();
|
|
for(int i=0; i<totalOrders; i++)
|
|
{
|
|
ulong ticket = OrderGetTicket(i);
|
|
if(OrderSelect(ticket))
|
|
{
|
|
long ordMagic = OrderGetInteger(ORDER_MAGIC);
|
|
string ordSymbol = OrderGetString(ORDER_SYMBOL);
|
|
if(ordMagic == magic && ordSymbol == _Symbol)
|
|
return(true);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| CloseAllByMagic - End-of-Day cleanup |
|
|
//+------------------------------------------------------------------+
|
|
bool CloseAllByMagic(long magic)
|
|
{
|
|
bool success = true;
|
|
|
|
// If user wants to close ONLY pending orders => skip open position logic
|
|
if(!CloseOnlyPendingEndOfDay)
|
|
{
|
|
// 1) Possibly close open position(s)
|
|
if(CloseOpenOrdersEndOfDay && PositionSelect(_Symbol))
|
|
{
|
|
if((long)PositionGetInteger(POSITION_MAGIC) == magic)
|
|
{
|
|
ulong ticket = PositionGetInteger(POSITION_TICKET);
|
|
double vol = PositionGetDouble(POSITION_VOLUME);
|
|
long pType = PositionGetInteger(POSITION_TYPE);
|
|
double cPx = (pType==POSITION_TYPE_BUY)
|
|
? SymbolInfoDouble(_Symbol, SYMBOL_BID)
|
|
: SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
|
|
|
MqlTradeRequest clReq;
|
|
MqlTradeResult clRes;
|
|
ZeroMemory(clReq);
|
|
ZeroMemory(clRes);
|
|
|
|
clReq.action = TRADE_ACTION_DEAL;
|
|
clReq.symbol = _Symbol;
|
|
clReq.volume = vol;
|
|
clReq.magic = magic;
|
|
clReq.position = ticket;
|
|
clReq.deviation = Slippage;
|
|
clReq.type = (pType==POSITION_TYPE_BUY)?ORDER_TYPE_SELL:ORDER_TYPE_BUY;
|
|
clReq.price = cPx;
|
|
clReq.comment = "FadePivot2_v2 EoD Close";
|
|
|
|
if(!OrderSend(clReq, clRes) || clRes.retcode != TRADE_RETCODE_DONE)
|
|
{
|
|
Print("CloseAllByMagic: close pos retcode=", clRes.retcode,
|
|
", lastErr=", GetLastError());
|
|
success = false;
|
|
}
|
|
else
|
|
{
|
|
Print("Closed position #", ticket, " EoD.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2) Delete pending orders either way
|
|
int totalOrders = (int)OrdersTotal();
|
|
for(int i=totalOrders-1; i>=0; i--)
|
|
{
|
|
ulong ticket = OrderGetTicket(i);
|
|
if(OrderSelect(ticket))
|
|
{
|
|
long omagic = OrderGetInteger(ORDER_MAGIC);
|
|
string sym = OrderGetString(ORDER_SYMBOL);
|
|
long otype = OrderGetInteger(ORDER_TYPE);
|
|
|
|
if(omagic == magic && sym == _Symbol)
|
|
{
|
|
// If it's a pending order, remove it
|
|
if(otype == ORDER_TYPE_BUY_LIMIT || otype == ORDER_TYPE_SELL_LIMIT ||
|
|
otype == ORDER_TYPE_BUY_STOP || otype == ORDER_TYPE_SELL_STOP)
|
|
{
|
|
MqlTradeRequest odReq;
|
|
MqlTradeResult odRes;
|
|
ZeroMemory(odReq);
|
|
ZeroMemory(odRes);
|
|
|
|
odReq.action = TRADE_ACTION_REMOVE;
|
|
odReq.order = ticket;
|
|
odReq.symbol = _Symbol;
|
|
odReq.magic = magic;
|
|
odReq.comment= "FadePivot2_v2 EoD Remove";
|
|
|
|
if(!OrderSend(odReq, odRes) || odRes.retcode != TRADE_RETCODE_DONE)
|
|
{
|
|
Print("CloseAllByMagic: remove order retcode=", odRes.retcode,
|
|
", lastErr=", GetLastError());
|
|
success = false;
|
|
}
|
|
else
|
|
{
|
|
Print("Removed pending order #", ticket, " EoD.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return success;
|
|
}
|