Files
mt5_news_eas/InstitutionalTickHybridNewsFilter.mq5
T
2026-05-23 20:54:03 +03:00

793 lines
21 KiB
Plaintext

//+------------------------------------------------------------------+
//| PhantomEdge_TickScalper.mq5 |
//| Ultra-Fast Tick Scalper for Cent Accounts|
//| |
//| PHILOSOPHY: |
//| - Works on TICKS not bars — reacts to every price movement |
//| - Mean reversion on tick RETURNS (not raw prices) |
//| - Micro-VWAP as dynamic fair value anchor |
//| - Ultra-tight risk: equity-based lots, circuit breaker, session |
//| - News-aware: blocks new entries but NEVER closes winning trades|
//| - Aggressive entries, surgical exits |
//+------------------------------------------------------------------+
#property copyright "PhantomEdge"
#property version "1.00"
#property strict
#include <Trade\Trade.mqh>
//+------------------------------------------------------------------+
//| INPUT PARAMETERS |
//+------------------------------------------------------------------+
//--- Core Strategy
input group "=== CORE STRATEGY ==="
input int TickBufferSize = 40;
input double ZScoreEntry = 1.20;
input double ZScoreExit = 0.20;
input int VWAPWindowTicks = 60;
input bool UseVWAPConfirm = true;
//--- Risk Management
input group "=== RISK MANAGEMENT ==="
input double RiskPercentPerTrade = 1.0;
input double MaxDailyDrawdownPct = 5.0;
input double MaxTotalDrawdownPct = 15.0;
input int MaxPositionsPerSymbol= 1;
input double MaxLotSize = 0.50;
input double MinLotSize = 0.01;
//--- Stop Loss & Take Profit
input group "=== SL/TP & TRAILING ==="
input double SL_ATR_Multiplier = 1.2;
input double TP_RR_Ratio = 1.5;
input double TrailingATR_Mult = 0.8;
input double BreakevenATR_Mult = 0.5;
input int ATR_Period = 14;
input ENUM_TIMEFRAMES ATR_Timeframe = PERIOD_M1;
//--- Spread & Volatility Filters
input group "=== FILTERS ==="
input int MaxSpreadPoints = 45;
input double MinATR_Filter = 0.0;
input bool UseSessionFilter = true;
input int SessionStartHour = 0;
input int SessionEndHour = 24;
//--- News Filter
input group "=== NEWS FILTER ==="
input bool UseNewsFilter = false;
input int NewsMinutesBefore = 30;
input int NewsMinutesAfter = 30;
input bool NewsClosePositions = false;
//--- Trade Settings
input group "=== TRADE SETTINGS ==="
input int MagicNumber = 777888;
input int TradeSlippage = 20;
input string TradeComment = "PhantomEdge";
input int CooldownSeconds = 1;
//+------------------------------------------------------------------+
//| GLOBAL VARIABLES |
//+------------------------------------------------------------------+
CTrade trade;
// Tick data buffers
double TickPrices[];
double TickReturns[];
double TickVolumes[];
int TickCount = 0;
bool BufferReady = false;
// VWAP data
double VWAPPrices[];
double VWAPVolumes[];
int VWAPCount = 0;
// State tracking
datetime LastTradeTime = 0;
double DayStartEquity = 0;
double PeakEquity = 0;
bool DailyCircuitBreaker = false;
bool TotalCircuitBreaker = false;
datetime LastDay = 0;
datetime LastTickTime = 0;
// ATR handle
int ATR_Handle = INVALID_HANDLE;
//+------------------------------------------------------------------+
//| Expert initialization |
//+------------------------------------------------------------------+
int OnInit()
{
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(TradeSlippage);
ENUM_SYMBOL_TRADE_EXECUTION exec =
(ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_EXEMODE);
if(exec == SYMBOL_TRADE_EXECUTION_MARKET)
trade.SetTypeFilling(ORDER_FILLING_FOK);
else
trade.SetTypeFilling(ORDER_FILLING_RETURN);
ArrayResize(TickPrices, TickBufferSize);
ArrayResize(TickReturns, TickBufferSize);
ArrayResize(TickVolumes, TickBufferSize);
ArrayInitialize(TickPrices, 0);
ArrayInitialize(TickReturns, 0);
ArrayInitialize(TickVolumes, 0);
ArrayResize(VWAPPrices, VWAPWindowTicks);
ArrayResize(VWAPVolumes, VWAPWindowTicks);
ArrayInitialize(VWAPPrices, 0);
ArrayInitialize(VWAPVolumes, 0);
ATR_Handle = iATR(_Symbol, ATR_Timeframe, ATR_Period);
if(ATR_Handle == INVALID_HANDLE)
{
Print("ERROR: Failed to create ATR indicator");
return INIT_FAILED;
}
DayStartEquity = AccountInfoDouble(ACCOUNT_EQUITY);
PeakEquity = DayStartEquity;
LastDay = TimeCurrent();
Print("=== PhantomEdge TickScalper Initialized ===");
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(ATR_Handle != INVALID_HANDLE)
IndicatorRelease(ATR_Handle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
MqlTick ticks[];
int copied = CopyTicks(_Symbol, ticks, COPY_TICKS_INFO, 0, 200);
if(copied <= 0)
return;
for(int i = 0; i < copied; i++)
{
if(ticks[i].time_msc <= LastTickTime && LastTickTime != 0)
continue;
if(ticks[i].ask <= 0 || ticks[i].bid <= 0)
continue;
LastTickTime = ticks[i].time_msc;
double midPrice = (ticks[i].ask + ticks[i].bid) / 2.0;
double tickVol = (double)ticks[i].volume;
if(tickVol < 1)
tickVol = 1;
ProcessTick(midPrice, tickVol);
}
CheckDailyReset();
UpdateCircuitBreakers();
if(DailyCircuitBreaker || TotalCircuitBreaker)
{
ManageOpenPositions();
return;
}
ManageOpenPositions();
if(!BufferReady)
return;
if(!PassesAllFilters())
return;
if(TimeCurrent() - LastTradeTime < CooldownSeconds)
return;
if(CountMyPositions() >= MaxPositionsPerSymbol)
return;
EvaluateEntry();
}
//+------------------------------------------------------------------+
//| Process Tick |
//+------------------------------------------------------------------+
void ProcessTick(double price, double volume)
{
for(int i = 0; i < TickBufferSize - 1; i++)
{
TickPrices[i] = TickPrices[i + 1];
TickReturns[i] = TickReturns[i + 1];
TickVolumes[i] = TickVolumes[i + 1];
}
TickPrices[TickBufferSize - 1] = price;
TickVolumes[TickBufferSize - 1] = volume;
if(TickPrices[TickBufferSize - 2] > 0)
TickReturns[TickBufferSize - 1] =
MathLog(price / TickPrices[TickBufferSize - 2]);
else
TickReturns[TickBufferSize - 1] = 0;
TickCount++;
if(TickCount >= TickBufferSize + 1)
BufferReady = true;
for(int i = 0; i < VWAPWindowTicks - 1; i++)
{
VWAPPrices[i] = VWAPPrices[i + 1];
VWAPVolumes[i] = VWAPVolumes[i + 1];
}
VWAPPrices[VWAPWindowTicks - 1] = price;
VWAPVolumes[VWAPWindowTicks - 1] = volume;
VWAPCount++;
}
//+------------------------------------------------------------------+
//| Calculate ZScore |
//+------------------------------------------------------------------+
double CalcZScore()
{
double sum = 0;
for(int i = 0; i < TickBufferSize; i++)
sum += TickReturns[i];
double mean = sum / TickBufferSize;
double sumSqDev = 0;
for(int i = 0; i < TickBufferSize; i++)
{
double dev = TickReturns[i] - mean;
sumSqDev += dev * dev;
}
double stdev = MathSqrt(sumSqDev / (TickBufferSize - 1));
if(stdev < 1e-12)
return 0;
return (TickReturns[TickBufferSize - 1] - mean) / stdev;
}
//+------------------------------------------------------------------+
//| Calculate VWAP |
//+------------------------------------------------------------------+
double CalcMicroVWAP()
{
if(VWAPCount < VWAPWindowTicks)
return 0;
double sumPV = 0;
double sumV = 0;
for(int i = 0; i < VWAPWindowTicks; i++)
{
if(VWAPPrices[i] <= 0)
return 0;
sumPV += VWAPPrices[i] * VWAPVolumes[i];
sumV += VWAPVolumes[i];
}
if(sumV < 1)
return 0;
return sumPV / sumV;
}
//+------------------------------------------------------------------+
//| Calculate cumulative ZScore |
//+------------------------------------------------------------------+
double CalcCumulativeZScore(int lookback)
{
if(lookback > TickBufferSize)
lookback = TickBufferSize;
double cumReturn = 0;
for(int i = TickBufferSize - lookback; i < TickBufferSize; i++)
cumReturn += TickReturns[i];
double sum = 0;
for(int i = 0; i < TickBufferSize; i++)
sum += TickReturns[i];
double mean = sum / TickBufferSize;
double sumSqDev = 0;
for(int i = 0; i < TickBufferSize; i++)
{
double dev = TickReturns[i] - mean;
sumSqDev += dev * dev;
}
double stdev = MathSqrt(sumSqDev / (TickBufferSize - 1));
if(stdev < 1e-12)
return 0;
double expectedStdev = stdev * MathSqrt((double)lookback);
return (cumReturn - mean * lookback) / expectedStdev;
}
//+------------------------------------------------------------------+
//| Entry Logic |
//+------------------------------------------------------------------+
void EvaluateEntry()
{
double zScore = CalcZScore();
double cumZ = CalcCumulativeZScore(10);
double vwap = CalcMicroVWAP();
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double mid = (ask + bid) / 2.0;
double atr = GetATR();
if(atr <= 0)
return;
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(point <= 0)
return;
bool buySignal =
(zScore <= -ZScoreEntry) &&
(cumZ <= -1.0);
if(UseVWAPConfirm && vwap > 0)
buySignal = buySignal && (mid < vwap);
bool sellSignal =
(zScore >= ZScoreEntry) &&
(cumZ >= 1.0);
if(UseVWAPConfirm && vwap > 0)
sellSignal = sellSignal && (mid > vwap);
if(buySignal)
{
double sl = NormalizeDouble(
ask - atr * SL_ATR_Multiplier,
_Digits
);
double tp = NormalizeDouble(
ask + atr * SL_ATR_Multiplier * TP_RR_Ratio,
_Digits
);
if(!ValidateStops(ORDER_TYPE_BUY, ask, sl, tp))
return;
double lots = CalcLotSize(MathAbs(ask - sl) / point);
if(trade.Buy(lots, _Symbol, ask, sl, tp, TradeComment))
{
LastTradeTime = TimeCurrent();
Print("BUY OPENED");
}
}
else if(sellSignal)
{
double sl = NormalizeDouble(
bid + atr * SL_ATR_Multiplier,
_Digits
);
double tp = NormalizeDouble(
bid - atr * SL_ATR_Multiplier * TP_RR_Ratio,
_Digits
);
if(!ValidateStops(ORDER_TYPE_SELL, bid, sl, tp))
return;
double lots = CalcLotSize(MathAbs(sl - bid) / point);
if(trade.Sell(lots, _Symbol, bid, sl, tp, TradeComment))
{
LastTradeTime = TimeCurrent();
Print("SELL OPENED");
}
}
}
//+------------------------------------------------------------------+
//| Manage Positions |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
double atr = GetATR();
if(atr <= 0)
return;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber)
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
double currentSL = PositionGetDouble(POSITION_SL);
double currentTP = PositionGetDouble(POSITION_TP);
long posType = PositionGetInteger(POSITION_TYPE);
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(BufferReady)
{
double zNow = CalcZScore();
double cumZNow = CalcCumulativeZScore(10);
if(posType == POSITION_TYPE_BUY &&
zNow >= ZScoreExit &&
cumZNow >= 0)
{
double profit =
PositionGetDouble(POSITION_PROFIT);
if(profit > 0)
{
trade.PositionClose(ticket);
continue;
}
}
if(posType == POSITION_TYPE_SELL &&
zNow <= -ZScoreExit &&
cumZNow <= 0)
{
double profit =
PositionGetDouble(POSITION_PROFIT);
if(profit > 0)
{
trade.PositionClose(ticket);
continue;
}
}
}
double breakevenDist =
atr * BreakevenATR_Mult;
if(posType == POSITION_TYPE_BUY)
{
double bid =
SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(bid >= openPrice + breakevenDist &&
currentSL < openPrice)
{
double newSL =
NormalizeDouble(openPrice + point * 2, _Digits);
if(newSL > currentSL)
trade.PositionModify(ticket, newSL, currentTP);
}
double trailLevel =
NormalizeDouble(
bid - atr * TrailingATR_Mult,
_Digits
);
if(trailLevel > currentSL &&
trailLevel > openPrice)
{
trade.PositionModify(ticket, trailLevel, currentTP);
}
}
else if(posType == POSITION_TYPE_SELL)
{
double ask =
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(ask <= openPrice - breakevenDist &&
(currentSL > openPrice || currentSL == 0))
{
double newSL =
NormalizeDouble(openPrice - point * 2, _Digits);
if(currentSL == 0 || newSL < currentSL)
trade.PositionModify(ticket, newSL, currentTP);
}
double trailLevel =
NormalizeDouble(
ask + atr * TrailingATR_Mult,
_Digits
);
if((trailLevel < currentSL || currentSL == 0) &&
trailLevel < openPrice)
{
trade.PositionModify(ticket, trailLevel, currentTP);
}
}
}
}
//+------------------------------------------------------------------+
//| Lot Size |
//+------------------------------------------------------------------+
double CalcLotSize(double slDistancePoints)
{
if(slDistancePoints <= 0)
return MinLotSize;
double equity =
AccountInfoDouble(ACCOUNT_EQUITY);
double riskAmount =
equity * (RiskPercentPerTrade / 100.0);
double tickValue =
SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize =
SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double point =
SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(tickValue <= 0 || tickSize <= 0 || point <= 0)
return MinLotSize;
double riskPerLot =
(slDistancePoints * point / tickSize) * tickValue;
if(riskPerLot <= 0)
return MinLotSize;
double lots = riskAmount / riskPerLot;
double lotStep =
SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double lotMin =
SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double lotMax =
SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
lots = MathFloor(lots / lotStep) * lotStep;
lots = MathMax(lots, lotMin);
lots = MathMin(lots, lotMax);
lots = MathMax(lots, MinLotSize);
lots = MathMin(lots, MaxLotSize);
return NormalizeDouble(lots, 2);
}
//+------------------------------------------------------------------+
//| Circuit Breakers |
//+------------------------------------------------------------------+
void UpdateCircuitBreakers()
{
double equity =
AccountInfoDouble(ACCOUNT_EQUITY);
if(equity > PeakEquity)
PeakEquity = equity;
if(DayStartEquity > 0)
{
double dailyDD =
((DayStartEquity - equity) / DayStartEquity) * 100.0;
if(dailyDD >= MaxDailyDrawdownPct)
DailyCircuitBreaker = true;
}
if(PeakEquity > 0)
{
double totalDD =
((PeakEquity - equity) / PeakEquity) * 100.0;
if(totalDD >= MaxTotalDrawdownPct)
TotalCircuitBreaker = true;
}
}
//+------------------------------------------------------------------+
//| Daily Reset |
//+------------------------------------------------------------------+
void CheckDailyReset()
{
MqlDateTime now;
TimeToStruct(TimeCurrent(), now);
MqlDateTime last;
TimeToStruct(LastDay, last);
if(now.day != last.day || now.mon != last.mon)
{
DayStartEquity =
AccountInfoDouble(ACCOUNT_EQUITY);
DailyCircuitBreaker = false;
LastDay = TimeCurrent();
}
}
//+------------------------------------------------------------------+
//| Filters |
//+------------------------------------------------------------------+
bool PassesAllFilters()
{
double spreadPoints =
SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
if(spreadPoints > MaxSpreadPoints)
return false;
double atr = GetATR();
double minATR = MinATR_Filter;
if(minATR <= 0)
{
double point =
SymbolInfoDouble(_Symbol, SYMBOL_POINT);
minATR = spreadPoints * point * 2;
}
if(atr < minATR)
return false;
if(UseSessionFilter)
{
MqlDateTime serverTime;
TimeToStruct(TimeCurrent(), serverTime);
if(SessionStartHour < SessionEndHour)
{
if(serverTime.hour < SessionStartHour ||
serverTime.hour >= SessionEndHour)
return false;
}
}
return true;
}
//+------------------------------------------------------------------+
//| Validate Stops |
//+------------------------------------------------------------------+
bool ValidateStops(
ENUM_ORDER_TYPE orderType,
double price,
double sl,
double tp
)
{
long stopsLevel =
SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
long freezeLevel =
SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL);
double point =
SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double minDist =
MathMax((double)stopsLevel, (double)freezeLevel) * point;
double spreadDist =
SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * point * 3;
minDist = MathMax(minDist, spreadDist);
if(orderType == ORDER_TYPE_BUY)
{
if(MathAbs(price - sl) < minDist)
return false;
if(MathAbs(tp - price) < minDist)
return false;
}
else
{
if(MathAbs(sl - price) < minDist)
return false;
if(MathAbs(price - tp) < minDist)
return false;
}
return true;
}
//+------------------------------------------------------------------+
//| ATR |
//+------------------------------------------------------------------+
double GetATR()
{
double atrBuffer[];
ArraySetAsSeries(atrBuffer, true);
if(CopyBuffer(ATR_Handle, 0, 0, 1, atrBuffer) <= 0)
return 0;
return atrBuffer[0];
}
//+------------------------------------------------------------------+
//| Count Positions |
//+------------------------------------------------------------------+
int CountMyPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber)
continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
count++;
}
return count;
}
//+------------------------------------------------------------------+