MQL5 uses the modern CTrade library, making order management significantly cleaner. Array access requires setting timeseries formatting (ArraySetAsSeries) to mirror the typical [0] = current bar logic.
Code: Select all
//+------------------------------------------------------------------+
//| SilverBullet_EA.mq5 |
//+------------------------------------------------------------------+
#property copyright "Execution Engine"
#property version "1.00"
#include <Trade\Trade.mqh>
input string StartTime = "17:00";
input string EndTime = "18:00";
input double RiskReward = 2.0;
input double RiskPercent = 2.0;
input int ExpirationBars = 10;
input int PivotLeft = 5;
input int PivotRight = 2;
input int MaxBarsToMSS = 20;
input int MaxBarsToFVG = 15;
input ulong MagicNumber = 101101;
CTrade trade;
int seq_state = 0;
datetime state_time = 0;
double mss_trigger_level = 0.0;
double sweep_extreme = 0.0;
double last_ph = 0.0;
double last_pl = 0.0;
datetime last_bar_time = 0;
int OnInit()
{
trade.SetExpertMagicNumber(MagicNumber);
return(INIT_SUCCEEDED);
}
void OnTick()
{
datetime time_array[];
CopyTime(_Symbol, _Period, 0, 1, time_array);
if(time_array[0] == last_bar_time) return;
last_bar_time = time_array[0];
// Load arrays
double High[], Low[], Close[], Open[];
ArraySetAsSeries(High, true); ArraySetAsSeries(Low, true);
ArraySetAsSeries(Close, true); ArraySetAsSeries(Open, true);
CopyHigh(_Symbol, _Period, 0, PivotLeft + PivotRight + 5, High);
CopyLow(_Symbol, _Period, 0, PivotLeft + PivotRight + 5, Low);
CopyClose(_Symbol, _Period, 0, 4, Close);
CopyOpen(_Symbol, _Period, 0, 4, Open);
int p_shift = PivotRight + 1;
bool is_ph = true;
for(int i = 1; i <= PivotLeft; i++) { if(High[p_shift+i] > High[p_shift]) is_ph = false; }
for(int i = 1; i <= PivotRight; i++) { if(High[p_shift-i] >= High[p_shift]) is_ph = false; }
if(is_ph) last_ph = High[p_shift];
bool is_pl = true;
for(int i = 1; i <= PivotLeft; i++) { if(Low[p_shift+i] < Low[p_shift]) is_pl = false; }
for(int i = 1; i <= PivotRight; i++) { if(Low[p_shift-i] <= Low[p_shift]) is_pl = false; }
if(is_pl) last_pl = Low[p_shift];
int bars_passed = iBarShift(_Symbol, _Period, state_time);
// Step 1: Detect Sweep
if(High[1] > last_ph && last_ph > 0 && seq_state != 1 && seq_state != 2)
{
seq_state = 1; state_time = time_array[0];
mss_trigger_level = last_pl; sweep_extreme = High[1];
}
else if(Low[1] < last_pl && last_pl > 0 && seq_state != -1 && seq_state != -2)
{
seq_state = -1; state_time = time_array[0];
mss_trigger_level = last_ph; sweep_extreme = Low[1];
}
if(seq_state == 1 && High[1] > sweep_extreme) sweep_extreme = High[1];
if(seq_state == -1 && Low[1] < sweep_extreme) sweep_extreme = Low[1];
// Step 2: Detect MSS
if(seq_state == 1 && Close[1] < mss_trigger_level)
{
if(bars_passed <= MaxBarsToMSS) { seq_state = 2; state_time = time_array[0]; }
else seq_state = 0;
}
else if(seq_state == -1 && Close[1] > mss_trigger_level)
{
if(bars_passed <= MaxBarsToMSS) { seq_state = -2; state_time = time_array[0]; }
else seq_state = 0;
}
// Step 3: FVG Execution
if(!IsWithinWindow()) return;
if(PositionsTotal() > 0 || OrdersTotal() > 0) return;
bool bull_fvg = (Low[1] > High[3] && Close[2] > Open[2]);
bool bear_fvg = (High[1] < Low[3] && Close[2] < Open[2]);
double lot_size = CalculateLotSize(MathAbs(Close[1] - sweep_extreme));
if(bull_fvg && seq_state == -2 && bars_passed <= MaxBarsToFVG)
{
double entry = High[3];
double sl = sweep_extreme;
double tp = entry + ((entry - sl) * RiskReward);
datetime exp = TimeCurrent() + (ExpirationBars * PeriodSeconds());
if(trade.BuyLimit(lot_size, entry, _Symbol, sl, tp, ORDER_TIME_SPECIFIED, exp, "SB Long")) seq_state = 0;
}
if(bear_fvg && seq_state == 2 && bars_passed <= MaxBarsToFVG)
{
double entry = Low[3];
double sl = sweep_extreme;
double tp = entry - ((sl - entry) * RiskReward);
datetime exp = TimeCurrent() + (ExpirationBars * PeriodSeconds());
if(trade.SellLimit(lot_size, entry, _Symbol, sl, tp, ORDER_TIME_SPECIFIED, exp, "SB Short")) seq_state = 0;
}
}
//+------------------------------------------------------------------+
bool IsWithinWindow()
{
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
string current_time = StringFormat("%02d:%02d", dt.hour, dt.min);
return (current_time >= StartTime && current_time <= EndTime);
}
double CalculateLotSize(double risk_points)
{
if(risk_points == 0) return 0.01;
double risk_amount = AccountInfoDouble(ACCOUNT_BALANCE) * (RiskPercent / 100.0);
double tick_value = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tick_size = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double lots = risk_amount / ((risk_points / tick_size) * tick_value);
return MathMax(SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN), NormalizeDouble(MathFloor(lots/step)*step, 2));
}