984 lines
38 KiB
MQL5
984 lines
38 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| AdaptiveRegime_EA.mq5 |
|
||
|
|
//| Decision Layer: Mean Reversion vs Trend |
|
||
|
|
//| https://www.mql5.com |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property copyright "AK47"
|
||
|
|
#property link "https://www.mql5.com"
|
||
|
|
#property version "1.00"
|
||
|
|
#property strict
|
||
|
|
|
||
|
|
#include <Trade\Trade.mqh>
|
||
|
|
#include <Trade\OrderInfo.mqh>
|
||
|
|
#include <Trade\SymbolInfo.mqh>
|
||
|
|
#include <Trade\AccountInfo.mqh>
|
||
|
|
#include <Trade\PositionInfo.mqh>
|
||
|
|
|
||
|
|
//=== ENUMS ============================================================
|
||
|
|
enum ENUM_VWAP_PERIOD
|
||
|
|
{
|
||
|
|
VWAP_DAILY, // Daily
|
||
|
|
VWAP_SESSION, // Session (NY/Custom)
|
||
|
|
VWAP_WEEKLY, // Weekly
|
||
|
|
VWAP_MONTHLY // Monthly
|
||
|
|
};
|
||
|
|
|
||
|
|
enum ENUM_REGIME
|
||
|
|
{
|
||
|
|
REGIME_MEAN_REVERT, // Mean Reversion
|
||
|
|
REGIME_TRENDING, // Trending
|
||
|
|
REGIME_NEUTRAL // Neutral (No New Entries)
|
||
|
|
};
|
||
|
|
|
||
|
|
//=== INPUTS: REGIME DETECTION =========================================
|
||
|
|
input group "═══ REGIME DETECTION (Decision Layer) ═══"
|
||
|
|
input int InpADXPeriod = 14; // ADX Period
|
||
|
|
input int InpBBPeriodRegime = 20; // BB Period (for band width)
|
||
|
|
input double InpBBDevRegime = 2.0; // BB Deviation (for band width)
|
||
|
|
input int InpATRFast = 7; // ATR Fast Period
|
||
|
|
input int InpATRSlow = 21; // ATR Slow Period
|
||
|
|
input double InpWeightADX = 0.40; // Weight: ADX component
|
||
|
|
input double InpWeightBBW = 0.35; // Weight: BB Width component
|
||
|
|
input double InpWeightATR = 0.25; // Weight: ATR Ratio component
|
||
|
|
input double InpThresholdMR = 0.45; // Score Below = Mean Reversion
|
||
|
|
input double InpThresholdTR = 0.55; // Score Above = Trending
|
||
|
|
input double InpBBWLow = 0.005; // BB Width Low Reference
|
||
|
|
input double InpBBWHigh = 0.025; // BB Width High Reference
|
||
|
|
input ENUM_TIMEFRAMES InpRegimeTF = PERIOD_M5; // Regime Indicator Timeframe
|
||
|
|
|
||
|
|
//=== INPUTS: MEAN REVERSION (VWAP Close Predictive) ===================
|
||
|
|
input group "═══ MEAN REVERSION MODE (VWAP) ═══"
|
||
|
|
input double InpMR_BaseLot = 0.01; // MR: Base Lot Size
|
||
|
|
input double InpMR_Threshold = 0.005; // MR: Metric Threshold (e.g. 0.005)
|
||
|
|
input double InpMR_LotMultiplier = 2.0; // MR: Lot Scaling Multiplier
|
||
|
|
input int InpMR_MaxLevels = 10; // MR: Max Open Levels per side
|
||
|
|
input int InpMR_StopLoss = 0; // MR: Stop Loss (Points, 0=Off)
|
||
|
|
input double InpMR_TakeProfit = 0; // MR: Take Profit (Points, 0=Off)
|
||
|
|
input bool InpMR_UseTrailing = true; // MR: Use Trailing Stop
|
||
|
|
input double InpMR_ExitThreshold = 0.002; // MR: Metric Exit Threshold
|
||
|
|
input double InpMR_TrailPercent = 0.75; // MR: Trail Percent (0.75=3/4)
|
||
|
|
input int InpMR_TrailStep = 20; // MR: Trailing Step (Points)
|
||
|
|
input bool InpMR_CloseOnVWAP = true; // MR: Close on VWAP Cross
|
||
|
|
|
||
|
|
input group "═══ VWAP SETUP ═══"
|
||
|
|
input ENUM_VWAP_PERIOD InpVWAPPeriod = VWAP_SESSION; // VWAP Anchor Period
|
||
|
|
input int InpVWAPStartHour = 16; // VWAP Start Hour (Broker)
|
||
|
|
input int InpVWAPStartMinute = 30; // VWAP Start Minute (Broker)
|
||
|
|
input int InpVWAPStartDay = 1; // VWAP Start Day (1=Mon) Weekly
|
||
|
|
|
||
|
|
//=== INPUTS: TRENDING (Martin Effective) ==============================
|
||
|
|
input group "═══ TRENDING MODE (Martingale Grid) ═══"
|
||
|
|
input double InpTR_FixedLot = 0.01; // TR: Fixed Lot
|
||
|
|
input ushort InpTR_Grid = 250; // TR: Grid Distance (Points)
|
||
|
|
input double InpTR_GridMul = 1.2; // TR: Grid Distance Multiplier
|
||
|
|
input double InpTR_VolMul = 1.8; // TR: Volume Multiplier
|
||
|
|
input double InpTR_TPFactor = 2.0; // TR: Take Profit Factor
|
||
|
|
input int InpTR_PosThreshold = 2; // TR: Positions Threshold for TP
|
||
|
|
input int InpTR_MaxPositions = 10; // TR: Max Positions per side
|
||
|
|
input int InpTR_BBPeriod = 18; // TR: Bollinger Bands Period
|
||
|
|
input double InpTR_BBDev = 1.0; // TR: Bollinger Bands Deviation
|
||
|
|
input int InpTR_MALength = 12; // TR: EMA Length (High/Low/Close)
|
||
|
|
|
||
|
|
//=== INPUTS: NEWS FILTER ==============================================
|
||
|
|
input group "═══ NEWS FILTER ═══"
|
||
|
|
input bool InpUseNewsFilter = true; // Enable News Filter
|
||
|
|
input bool InpCloseBeforeNews = false; // Close Positions Before News
|
||
|
|
input int InpNewsBufferBefore = 30; // Minutes Before News
|
||
|
|
input int InpNewsBufferAfter = 30; // Minutes After News
|
||
|
|
|
||
|
|
//=== INPUTS: TIME FILTER ==============================================
|
||
|
|
input group "═══ TIME FILTER ═══"
|
||
|
|
input bool InpUseTimeFilter = false; // Enable Time Filter
|
||
|
|
input int InpStartHour = 8; // Trade Start Hour (Broker)
|
||
|
|
input int InpEndHour = 20; // Trade End Hour (Broker)
|
||
|
|
|
||
|
|
//=== INPUTS: VISUALS ==================================================
|
||
|
|
input group "═══ VISUALS ═══"
|
||
|
|
input bool InpShowComments = true; // Show On-Chart Comments
|
||
|
|
|
||
|
|
//=== MAGIC NUMBERS ====================================================
|
||
|
|
#define MAGIC_MR 777111 // Mean Reversion positions
|
||
|
|
#define MAGIC_TR 777222 // Trending positions
|
||
|
|
|
||
|
|
//=== GLOBAL OBJECTS ===================================================
|
||
|
|
CTrade tradeMR, tradeTR;
|
||
|
|
CPositionInfo posInfo;
|
||
|
|
CSymbolInfo symInfo;
|
||
|
|
CAccountInfo accInfo;
|
||
|
|
|
||
|
|
//=== REGIME STATE =====================================================
|
||
|
|
double regimeScore = 0.0;
|
||
|
|
ENUM_REGIME currentRegime = REGIME_NEUTRAL;
|
||
|
|
bool isNewsImminent = false;
|
||
|
|
string newsEventMsg = "";
|
||
|
|
|
||
|
|
//=== REGIME INDICATOR HANDLES =========================================
|
||
|
|
int hADX, hBBRegime, hATRFast, hATRSlow;
|
||
|
|
|
||
|
|
//=== MEAN REVERSION STATE =============================================
|
||
|
|
double currVWAP = 0;
|
||
|
|
double currMetric = 0;
|
||
|
|
|
||
|
|
//=== TRENDING STATE ===================================================
|
||
|
|
int hMAHigh, hMAClose, hMALow;
|
||
|
|
int hBBTrend;
|
||
|
|
int hATRTrend;
|
||
|
|
|
||
|
|
double nextUpBuy, nextDownBuy;
|
||
|
|
double nextUpSell, nextDownSell;
|
||
|
|
double currentUpBuy, currentDownBuy;
|
||
|
|
double currentUpSell, currentDownSell;
|
||
|
|
bool buyStopSwitch = false, sellStopSwitch = false;
|
||
|
|
bool buyLimitSwitch = false, sellLimitSwitch = false;
|
||
|
|
double Grid_step_buy, Grid_step_sell;
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Expert initialization |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
int OnInit()
|
||
|
|
{
|
||
|
|
if(!symInfo.Name(_Symbol)) return INIT_FAILED;
|
||
|
|
|
||
|
|
tradeMR.SetExpertMagicNumber(MAGIC_MR);
|
||
|
|
tradeTR.SetExpertMagicNumber(MAGIC_TR);
|
||
|
|
|
||
|
|
//--- Regime Detection Indicators
|
||
|
|
hADX = iADX(_Symbol, InpRegimeTF, InpADXPeriod);
|
||
|
|
hBBRegime = iBands(_Symbol, InpRegimeTF, InpBBPeriodRegime, 0, InpBBDevRegime, PRICE_TYPICAL);
|
||
|
|
hATRFast = iATR(_Symbol, InpRegimeTF, InpATRFast);
|
||
|
|
hATRSlow = iATR(_Symbol, InpRegimeTF, InpATRSlow);
|
||
|
|
|
||
|
|
if(hADX == INVALID_HANDLE || hBBRegime == INVALID_HANDLE ||
|
||
|
|
hATRFast == INVALID_HANDLE || hATRSlow == INVALID_HANDLE)
|
||
|
|
{
|
||
|
|
Print("ERROR: Failed to create regime detection indicators!");
|
||
|
|
return INIT_FAILED;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- Trending Mode Indicators
|
||
|
|
hMAHigh = iMA(_Symbol, InpRegimeTF, InpTR_MALength, 0, MODE_EMA, PRICE_HIGH);
|
||
|
|
hMAClose = iMA(_Symbol, InpRegimeTF, InpTR_MALength, 0, MODE_EMA, PRICE_CLOSE);
|
||
|
|
hMALow = iMA(_Symbol, InpRegimeTF, InpTR_MALength, 0, MODE_EMA, PRICE_LOW);
|
||
|
|
hBBTrend = iBands(_Symbol, InpRegimeTF, InpTR_BBPeriod, 0, InpTR_BBDev, PRICE_TYPICAL);
|
||
|
|
hATRTrend= iATR(_Symbol, InpRegimeTF, InpTR_MALength);
|
||
|
|
|
||
|
|
if(hMAHigh == INVALID_HANDLE || hMAClose == INVALID_HANDLE ||
|
||
|
|
hMALow == INVALID_HANDLE || hBBTrend == INVALID_HANDLE ||
|
||
|
|
hATRTrend == INVALID_HANDLE)
|
||
|
|
{
|
||
|
|
Print("ERROR: Failed to create trending mode indicators!");
|
||
|
|
return INIT_FAILED;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- Initialize grid levels
|
||
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||
|
|
nextUpBuy = ask + InpTR_Grid * _Point;
|
||
|
|
nextDownBuy = bid - InpTR_Grid * _Point;
|
||
|
|
nextUpSell = ask + InpTR_Grid * _Point;
|
||
|
|
nextDownSell = bid - InpTR_Grid * _Point;
|
||
|
|
Grid_step_buy = InpTR_Grid;
|
||
|
|
Grid_step_sell = InpTR_Grid;
|
||
|
|
|
||
|
|
Print("AdaptiveRegime EA Initialized | MR Magic: ", MAGIC_MR, " | TR Magic: ", MAGIC_TR);
|
||
|
|
|
||
|
|
return INIT_SUCCEEDED;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Expert deinitialization |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnDeinit(const int reason)
|
||
|
|
{
|
||
|
|
Comment("");
|
||
|
|
|
||
|
|
if(hADX != INVALID_HANDLE) IndicatorRelease(hADX);
|
||
|
|
if(hBBRegime != INVALID_HANDLE) IndicatorRelease(hBBRegime);
|
||
|
|
if(hATRFast != INVALID_HANDLE) IndicatorRelease(hATRFast);
|
||
|
|
if(hATRSlow != INVALID_HANDLE) IndicatorRelease(hATRSlow);
|
||
|
|
if(hMAHigh != INVALID_HANDLE) IndicatorRelease(hMAHigh);
|
||
|
|
if(hMAClose != INVALID_HANDLE) IndicatorRelease(hMAClose);
|
||
|
|
if(hMALow != INVALID_HANDLE) IndicatorRelease(hMALow);
|
||
|
|
if(hBBTrend != INVALID_HANDLE) IndicatorRelease(hBBTrend);
|
||
|
|
if(hATRTrend != INVALID_HANDLE) IndicatorRelease(hATRTrend);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Expert tick function |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void OnTick()
|
||
|
|
{
|
||
|
|
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||
|
|
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||
|
|
double midPrice = (bid + ask) / 2.0;
|
||
|
|
|
||
|
|
//====================================================================
|
||
|
|
// 1. DECISION LAYER — Calculate Regime Score
|
||
|
|
//====================================================================
|
||
|
|
regimeScore = CalculateRegimeScore();
|
||
|
|
currentRegime = DetermineRegime(regimeScore);
|
||
|
|
|
||
|
|
//====================================================================
|
||
|
|
// 2. NEWS FILTER
|
||
|
|
//====================================================================
|
||
|
|
isNewsImminent = false;
|
||
|
|
if(InpUseNewsFilter)
|
||
|
|
{
|
||
|
|
isNewsImminent = IsNewsUpcoming();
|
||
|
|
if(isNewsImminent && InpCloseBeforeNews)
|
||
|
|
{
|
||
|
|
CloseAllByMagic(MAGIC_MR);
|
||
|
|
CloseAllByMagic(MAGIC_TR);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//====================================================================
|
||
|
|
// 3. TIME FILTER
|
||
|
|
//====================================================================
|
||
|
|
bool inTradeWindow = true;
|
||
|
|
if(InpUseTimeFilter)
|
||
|
|
{
|
||
|
|
MqlDateTime dt;
|
||
|
|
TimeCurrent(dt);
|
||
|
|
if(InpStartHour < InpEndHour)
|
||
|
|
inTradeWindow = (dt.hour >= InpStartHour && dt.hour < InpEndHour);
|
||
|
|
else // overnight window
|
||
|
|
inTradeWindow = (dt.hour >= InpStartHour || dt.hour < InpEndHour);
|
||
|
|
}
|
||
|
|
|
||
|
|
//====================================================================
|
||
|
|
// 4. COUNT POSITIONS
|
||
|
|
//====================================================================
|
||
|
|
int mrBuys = 0, mrSells = 0;
|
||
|
|
int trBuys = 0, trSells = 0;
|
||
|
|
CountPositions(MAGIC_MR, mrBuys, mrSells);
|
||
|
|
CountPositions(MAGIC_TR, trBuys, trSells);
|
||
|
|
|
||
|
|
//====================================================================
|
||
|
|
// 5. ALWAYS MANAGE EXISTING POSITIONS (regardless of regime)
|
||
|
|
//====================================================================
|
||
|
|
|
||
|
|
// --- Manage Mean Reversion positions (exits, trailing) ---
|
||
|
|
if(mrBuys > 0 || mrSells > 0)
|
||
|
|
{
|
||
|
|
// Need VWAP for MR position management
|
||
|
|
if(UpdateVWAPData())
|
||
|
|
{
|
||
|
|
if(midPrice > 0)
|
||
|
|
currMetric = currVWAP / midPrice;
|
||
|
|
|
||
|
|
ManageMRPositions(bid, ask, midPrice);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- Manage Trending positions (SL updates) ---
|
||
|
|
if(trBuys > 0 || trSells > 0)
|
||
|
|
{
|
||
|
|
ManageTRPositions(bid, ask);
|
||
|
|
}
|
||
|
|
|
||
|
|
//====================================================================
|
||
|
|
// 6. NEW ENTRIES (only if allowed)
|
||
|
|
//====================================================================
|
||
|
|
bool canTrade = !isNewsImminent && inTradeWindow;
|
||
|
|
|
||
|
|
if(canTrade)
|
||
|
|
{
|
||
|
|
// --- Mean Reversion Entries ---
|
||
|
|
if(currentRegime == REGIME_MEAN_REVERT)
|
||
|
|
{
|
||
|
|
if(UpdateVWAPData())
|
||
|
|
{
|
||
|
|
if(midPrice > 0)
|
||
|
|
currMetric = currVWAP / midPrice;
|
||
|
|
|
||
|
|
ExecuteMREntries(bid, ask, mrBuys, mrSells);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- Trending Entries ---
|
||
|
|
if(currentRegime == REGIME_TRENDING)
|
||
|
|
{
|
||
|
|
ExecuteTREntries(bid, ask, trBuys, trSells);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//====================================================================
|
||
|
|
// 7. RESET GRID if no trending positions
|
||
|
|
//====================================================================
|
||
|
|
if(trBuys == 0 && trSells == 0)
|
||
|
|
{
|
||
|
|
Grid_step_buy = InpTR_Grid;
|
||
|
|
Grid_step_sell = InpTR_Grid;
|
||
|
|
}
|
||
|
|
|
||
|
|
//====================================================================
|
||
|
|
// 8. UPDATE COMMENTS
|
||
|
|
//====================================================================
|
||
|
|
UpdateComments(mrBuys, mrSells, trBuys, trSells, canTrade);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| DECISION LAYER: Calculate regime score [0.0 – 1.0] |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double CalculateRegimeScore()
|
||
|
|
{
|
||
|
|
//--- ADX Component
|
||
|
|
double adxVal[];
|
||
|
|
ArraySetAsSeries(adxVal, true);
|
||
|
|
if(CopyBuffer(hADX, 0, 0, 3, adxVal) < 3) return 0.5; // fallback neutral
|
||
|
|
|
||
|
|
double adxScore = MathMin(MathMax((adxVal[1] - 20.0) / 15.0, 0.0), 1.0);
|
||
|
|
|
||
|
|
//--- Bollinger Band Width Component
|
||
|
|
double bbMid[], bbUpper[], bbLower[];
|
||
|
|
ArraySetAsSeries(bbMid, true);
|
||
|
|
ArraySetAsSeries(bbUpper, true);
|
||
|
|
ArraySetAsSeries(bbLower, true);
|
||
|
|
if(CopyBuffer(hBBRegime, 0, 0, 3, bbMid) < 3) return 0.5;
|
||
|
|
if(CopyBuffer(hBBRegime, 1, 0, 3, bbUpper) < 3) return 0.5;
|
||
|
|
if(CopyBuffer(hBBRegime, 2, 0, 3, bbLower) < 3) return 0.5;
|
||
|
|
|
||
|
|
double bbw = 0;
|
||
|
|
if(bbMid[1] > 0)
|
||
|
|
bbw = (bbUpper[1] - bbLower[1]) / bbMid[1];
|
||
|
|
|
||
|
|
double bbwScore = MathMin(MathMax((bbw - InpBBWLow) / (InpBBWHigh - InpBBWLow), 0.0), 1.0);
|
||
|
|
|
||
|
|
//--- ATR Ratio Component
|
||
|
|
double atrFastVal[], atrSlowVal[];
|
||
|
|
ArraySetAsSeries(atrFastVal, true);
|
||
|
|
ArraySetAsSeries(atrSlowVal, true);
|
||
|
|
if(CopyBuffer(hATRFast, 0, 0, 3, atrFastVal) < 3) return 0.5;
|
||
|
|
if(CopyBuffer(hATRSlow, 0, 0, 3, atrSlowVal) < 3) return 0.5;
|
||
|
|
|
||
|
|
double atrRatio = 1.0;
|
||
|
|
if(atrSlowVal[1] > 0)
|
||
|
|
atrRatio = atrFastVal[1] / atrSlowVal[1];
|
||
|
|
|
||
|
|
double atrScore = MathMin(MathMax((atrRatio - 0.8) / 0.6, 0.0), 1.0);
|
||
|
|
|
||
|
|
//--- Weighted Composite Score
|
||
|
|
double totalWeight = InpWeightADX + InpWeightBBW + InpWeightATR;
|
||
|
|
if(totalWeight <= 0) totalWeight = 1.0;
|
||
|
|
|
||
|
|
double score = (InpWeightADX * adxScore + InpWeightBBW * bbwScore + InpWeightATR * atrScore) / totalWeight;
|
||
|
|
|
||
|
|
return MathMin(MathMax(score, 0.0), 1.0);
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Convert score to regime enum with hysteresis |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
ENUM_REGIME DetermineRegime(double score)
|
||
|
|
{
|
||
|
|
// Use hysteresis: once in a regime, stay until crossing the opposite threshold
|
||
|
|
static ENUM_REGIME lastRegime = REGIME_NEUTRAL;
|
||
|
|
|
||
|
|
if(score <= InpThresholdMR)
|
||
|
|
lastRegime = REGIME_MEAN_REVERT;
|
||
|
|
else if(score >= InpThresholdTR)
|
||
|
|
lastRegime = REGIME_TRENDING;
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// In the neutral zone — keep previous regime but mark as neutral
|
||
|
|
// This prevents whipsawing. Keep managing existing positions.
|
||
|
|
lastRegime = REGIME_NEUTRAL;
|
||
|
|
}
|
||
|
|
|
||
|
|
return lastRegime;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Count positions by magic number |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void CountPositions(int magic, int &buys, int &sells)
|
||
|
|
{
|
||
|
|
buys = 0;
|
||
|
|
sells = 0;
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
if(posInfo.SelectByIndex(i) && posInfo.Symbol() == _Symbol && posInfo.Magic() == magic)
|
||
|
|
{
|
||
|
|
if(posInfo.PositionType() == POSITION_TYPE_BUY) buys++;
|
||
|
|
if(posInfo.PositionType() == POSITION_TYPE_SELL) sells++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Close all positions by magic number |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void CloseAllByMagic(int magic)
|
||
|
|
{
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
if(posInfo.SelectByIndex(i) && posInfo.Symbol() == _Symbol && posInfo.Magic() == magic)
|
||
|
|
{
|
||
|
|
if(magic == MAGIC_MR)
|
||
|
|
tradeMR.PositionClose(posInfo.Ticket());
|
||
|
|
else
|
||
|
|
tradeTR.PositionClose(posInfo.Ticket());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//======================================================================
|
||
|
|
// MEAN REVERSION ENGINE (from VWAP Close Predictive EA)
|
||
|
|
//======================================================================
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| MR: Place new entries |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ExecuteMREntries(double bid, double ask, int buys, int sells)
|
||
|
|
{
|
||
|
|
double nextBuyThreshold = 1.0 + (InpMR_Threshold * (buys + 1));
|
||
|
|
double nextSellThreshold = 1.0 - (InpMR_Threshold * (sells + 1));
|
||
|
|
|
||
|
|
// BUY: VWAP > Close (metric > 1 + threshold) means price is below VWAP
|
||
|
|
if(buys < InpMR_MaxLevels && currMetric >= nextBuyThreshold)
|
||
|
|
{
|
||
|
|
double sl = (InpMR_StopLoss > 0) ? ask - (InpMR_StopLoss * _Point) : 0;
|
||
|
|
double tp = (InpMR_TakeProfit > 0) ? ask + (InpMR_TakeProfit * _Point) : 0;
|
||
|
|
double lot = NormalizeDouble(InpMR_BaseLot * MathPow(InpMR_LotMultiplier, buys), 2);
|
||
|
|
|
||
|
|
if(tradeMR.Buy(lot, _Symbol, ask, NormalizeDouble(sl, _Digits), NormalizeDouble(tp, _Digits),
|
||
|
|
"MR_Buy_L" + IntegerToString(buys + 1)))
|
||
|
|
{
|
||
|
|
PrintFormat("[MR] BUY L%d | Metric=%.5f | Thresh=%.5f | Lot=%.2f", buys+1, currMetric, nextBuyThreshold, lot);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// SELL: VWAP < Close (metric < 1 - threshold) means price is above VWAP
|
||
|
|
if(sells < InpMR_MaxLevels && currMetric <= nextSellThreshold)
|
||
|
|
{
|
||
|
|
double sl = (InpMR_StopLoss > 0) ? bid + (InpMR_StopLoss * _Point) : 0;
|
||
|
|
double tp = (InpMR_TakeProfit > 0) ? bid - (InpMR_TakeProfit * _Point) : 0;
|
||
|
|
double lot = NormalizeDouble(InpMR_BaseLot * MathPow(InpMR_LotMultiplier, sells), 2);
|
||
|
|
|
||
|
|
if(tradeMR.Sell(lot, _Symbol, bid, NormalizeDouble(sl, _Digits), NormalizeDouble(tp, _Digits),
|
||
|
|
"MR_Sell_L" + IntegerToString(sells + 1)))
|
||
|
|
{
|
||
|
|
PrintFormat("[MR] SELL L%d | Metric=%.5f | Thresh=%.5f | Lot=%.2f", sells+1, currMetric, nextSellThreshold, lot);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| MR: Manage existing positions (trailing + exit on VWAP) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ManageMRPositions(double bid, double ask, double midPrice)
|
||
|
|
{
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
if(!posInfo.SelectByIndex(i)) continue;
|
||
|
|
if(posInfo.Symbol() != _Symbol || posInfo.Magic() != MAGIC_MR) continue;
|
||
|
|
|
||
|
|
ulong ticket = posInfo.Ticket();
|
||
|
|
double posSl = posInfo.StopLoss();
|
||
|
|
|
||
|
|
//--- Trailing Stop Logic (metric-based)
|
||
|
|
if(InpMR_UseTrailing && currVWAP > 0)
|
||
|
|
{
|
||
|
|
double dist = MathAbs(midPrice - currVWAP);
|
||
|
|
|
||
|
|
if(posInfo.PositionType() == POSITION_TYPE_BUY && currMetric <= 1.0 - InpMR_ExitThreshold)
|
||
|
|
{
|
||
|
|
double newSl = NormalizeDouble(currVWAP + (dist * InpMR_TrailPercent), _Digits);
|
||
|
|
if(posSl == 0 || newSl > posSl + (InpMR_TrailStep * _Point))
|
||
|
|
{
|
||
|
|
if(newSl > posSl)
|
||
|
|
tradeMR.PositionModify(ticket, newSl, posInfo.TakeProfit());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if(posInfo.PositionType() == POSITION_TYPE_SELL && currMetric >= 1.0 + InpMR_ExitThreshold)
|
||
|
|
{
|
||
|
|
double newSl = NormalizeDouble(currVWAP - (dist * InpMR_TrailPercent), _Digits);
|
||
|
|
if(posSl == 0 || newSl < posSl - (InpMR_TrailStep * _Point))
|
||
|
|
{
|
||
|
|
if(posSl == 0 || newSl < posSl)
|
||
|
|
tradeMR.PositionModify(ticket, newSl, posInfo.TakeProfit());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- Mean Reversion Exit (close on VWAP cross)
|
||
|
|
if(InpMR_CloseOnVWAP && currVWAP > 0)
|
||
|
|
{
|
||
|
|
if(posInfo.PositionType() == POSITION_TYPE_BUY && bid >= currVWAP)
|
||
|
|
{
|
||
|
|
tradeMR.PositionClose(ticket);
|
||
|
|
PrintFormat("[MR] BUY Closed | Reverted to VWAP at %.5f", bid);
|
||
|
|
}
|
||
|
|
else if(posInfo.PositionType() == POSITION_TYPE_SELL && ask <= currVWAP)
|
||
|
|
{
|
||
|
|
tradeMR.PositionClose(ticket);
|
||
|
|
PrintFormat("[MR] SELL Closed | Reverted to VWAP at %.5f", ask);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Calculate VWAP for selected period (from VWAP EA) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool UpdateVWAPData()
|
||
|
|
{
|
||
|
|
static datetime lastCalcBar = 0;
|
||
|
|
datetime currentBar = iTime(_Symbol, PERIOD_M1, 0);
|
||
|
|
if(currentBar == lastCalcBar && currVWAP > 0) return true;
|
||
|
|
lastCalcBar = currentBar;
|
||
|
|
|
||
|
|
MqlDateTime dt;
|
||
|
|
datetime currentTime = TimeCurrent();
|
||
|
|
TimeToStruct(currentTime, dt);
|
||
|
|
|
||
|
|
datetime anchorTime = 0;
|
||
|
|
|
||
|
|
if(InpVWAPPeriod == VWAP_DAILY || InpVWAPPeriod == VWAP_SESSION)
|
||
|
|
{
|
||
|
|
dt.hour = InpVWAPStartHour;
|
||
|
|
dt.min = InpVWAPStartMinute;
|
||
|
|
dt.sec = 0;
|
||
|
|
anchorTime = StructToTime(dt);
|
||
|
|
if(anchorTime > currentTime)
|
||
|
|
anchorTime -= 86400;
|
||
|
|
}
|
||
|
|
else if(InpVWAPPeriod == VWAP_WEEKLY)
|
||
|
|
{
|
||
|
|
dt.hour = InpVWAPStartHour;
|
||
|
|
dt.min = InpVWAPStartMinute;
|
||
|
|
dt.sec = 0;
|
||
|
|
datetime startOfDay = StructToTime(dt);
|
||
|
|
int daysToSubtract = dt.day_of_week - InpVWAPStartDay;
|
||
|
|
if(daysToSubtract < 0) daysToSubtract += 7;
|
||
|
|
anchorTime = startOfDay - (daysToSubtract * 86400);
|
||
|
|
}
|
||
|
|
else if(InpVWAPPeriod == VWAP_MONTHLY)
|
||
|
|
{
|
||
|
|
dt.day = 1;
|
||
|
|
dt.hour = InpVWAPStartHour;
|
||
|
|
dt.min = InpVWAPStartMinute;
|
||
|
|
dt.sec = 0;
|
||
|
|
anchorTime = StructToTime(dt);
|
||
|
|
}
|
||
|
|
|
||
|
|
int bars = Bars(_Symbol, PERIOD_M1, anchorTime, currentTime);
|
||
|
|
if(bars <= 0) return false;
|
||
|
|
|
||
|
|
MqlRates rates[];
|
||
|
|
if(CopyRates(_Symbol, PERIOD_M1, 0, bars, rates) <= 0) return false;
|
||
|
|
|
||
|
|
double sumPriceVol = 0;
|
||
|
|
double sumVol = 0;
|
||
|
|
|
||
|
|
for(int i = 0; i < bars; i++)
|
||
|
|
{
|
||
|
|
double typicalPrice = (rates[i].high + rates[i].low + rates[i].close) / 3.0;
|
||
|
|
sumPriceVol += typicalPrice * (double)rates[i].tick_volume;
|
||
|
|
sumVol += (double)rates[i].tick_volume;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(sumVol == 0) return false;
|
||
|
|
currVWAP = sumPriceVol / sumVol;
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
//======================================================================
|
||
|
|
// TRENDING ENGINE (from Martin Effective EA)
|
||
|
|
//======================================================================
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| TR: Place new entries (breakout grid) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ExecuteTREntries(double bid, double ask, int trBuys, int trSells)
|
||
|
|
{
|
||
|
|
//--- Copy indicator buffers
|
||
|
|
double MAHigh[], MALow[], MAClose[];
|
||
|
|
double bbMid[], bbUpper[], bbLower[];
|
||
|
|
MqlRates bars[];
|
||
|
|
|
||
|
|
ArraySetAsSeries(MAHigh, true);
|
||
|
|
ArraySetAsSeries(MALow, true);
|
||
|
|
ArraySetAsSeries(MAClose, true);
|
||
|
|
ArraySetAsSeries(bbMid, true);
|
||
|
|
ArraySetAsSeries(bbUpper, true);
|
||
|
|
ArraySetAsSeries(bbLower, true);
|
||
|
|
ArraySetAsSeries(bars, true);
|
||
|
|
|
||
|
|
if(CopyBuffer(hMAHigh, 0, 0, 4, MAHigh) < 4) return;
|
||
|
|
if(CopyBuffer(hMALow, 0, 0, 4, MALow) < 4) return;
|
||
|
|
if(CopyBuffer(hMAClose, 0, 0, 4, MAClose) < 4) return;
|
||
|
|
if(CopyBuffer(hBBTrend, 0, 0, 4, bbMid) < 4) return;
|
||
|
|
if(CopyBuffer(hBBTrend, 1, 0, 4, bbUpper) < 4) return;
|
||
|
|
if(CopyBuffer(hBBTrend, 2, 0, 4, bbLower) < 4) return;
|
||
|
|
if(CopyRates(_Symbol, InpRegimeTF, 0, 10, bars) < 10) return;
|
||
|
|
|
||
|
|
//--- Calculate dynamic grid step and volume
|
||
|
|
Grid_step_buy = InpTR_Grid * MathPow(InpTR_GridMul, trBuys);
|
||
|
|
Grid_step_sell = InpTR_Grid * MathPow(InpTR_GridMul, trSells);
|
||
|
|
|
||
|
|
double buyVolFactor = MathPow(InpTR_VolMul, trBuys);
|
||
|
|
double sellVolFactor = MathPow(InpTR_VolMul, trSells);
|
||
|
|
double buyVolume = NormalizeDouble(buyVolFactor * InpTR_FixedLot, 2);
|
||
|
|
double sellVolume = NormalizeDouble(sellVolFactor * InpTR_FixedLot, 2);
|
||
|
|
|
||
|
|
double slBuy = bbUpper[1];
|
||
|
|
double slSell = bbLower[1];
|
||
|
|
|
||
|
|
//--- BuyStop Logic: price breaks up through nextUpBuy
|
||
|
|
if(ask > nextUpBuy)
|
||
|
|
{
|
||
|
|
buyStopSwitch = true;
|
||
|
|
currentUpBuy = nextUpBuy;
|
||
|
|
nextUpBuy = ask + Grid_step_buy * _Point;
|
||
|
|
nextDownBuy = ask - Grid_step_buy * _Point;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(buyStopSwitch && trBuys < InpTR_MaxPositions &&
|
||
|
|
!OtherBuyPosition(MAGIC_TR, Grid_step_buy) &&
|
||
|
|
ask > bbUpper[1] &&
|
||
|
|
bars[1].high > bars[2].high &&
|
||
|
|
(ask > MeanEntryPriceBuy(MAGIC_TR) || trBuys == 0))
|
||
|
|
{
|
||
|
|
if(tradeTR.Buy(buyVolume, _Symbol, 0, slBuy, 0, "TR_BuyStop_L" + IntegerToString(trBuys + 1)))
|
||
|
|
{
|
||
|
|
PrintFormat("[TR] BUY L%d | Grid=%.0f | Vol=%.2f", trBuys+1, Grid_step_buy, buyVolume);
|
||
|
|
buyStopSwitch = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- BuyLimit Logic: price drops through nextDownBuy
|
||
|
|
if(ask < nextDownBuy)
|
||
|
|
{
|
||
|
|
buyLimitSwitch = true;
|
||
|
|
currentDownBuy = nextDownBuy;
|
||
|
|
nextUpBuy = ask + Grid_step_buy * _Point;
|
||
|
|
nextDownBuy = bid - Grid_step_buy * _Point;
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- SellStop Logic: price breaks down through nextDownSell
|
||
|
|
if(bid < nextDownSell)
|
||
|
|
{
|
||
|
|
sellStopSwitch = true;
|
||
|
|
currentDownSell = nextDownSell;
|
||
|
|
nextUpSell = bid + Grid_step_sell * _Point;
|
||
|
|
nextDownSell = bid - Grid_step_sell * _Point;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(sellStopSwitch && trSells < InpTR_MaxPositions &&
|
||
|
|
!OtherSellPosition(MAGIC_TR, Grid_step_sell) &&
|
||
|
|
bid < bbLower[1] &&
|
||
|
|
bars[1].low < bars[2].low &&
|
||
|
|
(bid < MeanEntryPriceSell(MAGIC_TR) || trSells == 0))
|
||
|
|
{
|
||
|
|
if(tradeTR.Sell(sellVolume, _Symbol, 0, slSell, 0, "TR_SellStop_L" + IntegerToString(trSells + 1)))
|
||
|
|
{
|
||
|
|
PrintFormat("[TR] SELL L%d | Grid=%.0f | Vol=%.2f", trSells+1, Grid_step_sell, sellVolume);
|
||
|
|
sellStopSwitch = false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- SellLimit Logic: price rises through nextUpSell
|
||
|
|
if(bid > nextUpSell)
|
||
|
|
{
|
||
|
|
sellLimitSwitch = true;
|
||
|
|
currentUpSell = nextUpSell;
|
||
|
|
nextUpSell = bid + Grid_step_sell * _Point;
|
||
|
|
nextDownSell = bid - Grid_step_sell * _Point;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| TR: Manage existing positions (SL updates, TP checks) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ManageTRPositions(double bid, double ask)
|
||
|
|
{
|
||
|
|
double bbUpper[], bbLower[];
|
||
|
|
ArraySetAsSeries(bbUpper, true);
|
||
|
|
ArraySetAsSeries(bbLower, true);
|
||
|
|
if(CopyBuffer(hBBTrend, 1, 0, 4, bbUpper) < 4) return;
|
||
|
|
if(CopyBuffer(hBBTrend, 2, 0, 4, bbLower) < 4) return;
|
||
|
|
|
||
|
|
int buyCount = 0, sellCount = 0;
|
||
|
|
CountPositions(MAGIC_TR, buyCount, sellCount);
|
||
|
|
|
||
|
|
double meanBuy = MeanEntryPriceBuy(MAGIC_TR);
|
||
|
|
double meanSell = MeanEntryPriceSell(MAGIC_TR);
|
||
|
|
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
if(!posInfo.SelectByIndex(i)) continue;
|
||
|
|
if(posInfo.Symbol() != _Symbol || posInfo.Magic() != MAGIC_TR) continue;
|
||
|
|
|
||
|
|
ulong ticket = posInfo.Ticket();
|
||
|
|
long tradeType = posInfo.PositionType();
|
||
|
|
double currentSL = posInfo.StopLoss();
|
||
|
|
double newStopLoss = 0;
|
||
|
|
|
||
|
|
// BUY positions SL management
|
||
|
|
if(tradeType == POSITION_TYPE_BUY)
|
||
|
|
{
|
||
|
|
if(ask < meanBuy + InpTR_TPFactor * InpTR_Grid * _Point)
|
||
|
|
newStopLoss = bbLower[1];
|
||
|
|
else
|
||
|
|
newStopLoss = NormalizeDouble((((ask + meanBuy) / 2.0) + ask) / 2.0, _Digits);
|
||
|
|
|
||
|
|
if(currentSL < newStopLoss)
|
||
|
|
tradeTR.PositionModify(ticket, newStopLoss, posInfo.TakeProfit());
|
||
|
|
|
||
|
|
// Close all buys if price exceeds mean + grid (profit target)
|
||
|
|
if(buyCount > InpTR_PosThreshold && ask > meanBuy + InpTR_TPFactor * InpTR_Grid * _Point)
|
||
|
|
{
|
||
|
|
ClosePositionsByType(MAGIC_TR, POSITION_TYPE_BUY);
|
||
|
|
PrintFormat("[TR] All BUYs Closed | Price=%.5f > MeanEntry+TP=%.5f", ask, meanBuy + InpTR_TPFactor * InpTR_Grid * _Point);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// SELL positions SL management
|
||
|
|
if(tradeType == POSITION_TYPE_SELL)
|
||
|
|
{
|
||
|
|
if(bid > meanSell - InpTR_TPFactor * InpTR_Grid * _Point)
|
||
|
|
newStopLoss = bbUpper[1];
|
||
|
|
else
|
||
|
|
newStopLoss = NormalizeDouble((((bid + meanSell) / 2.0) + bid) / 2.0, _Digits);
|
||
|
|
|
||
|
|
if(currentSL > newStopLoss || currentSL == 0)
|
||
|
|
tradeTR.PositionModify(ticket, newStopLoss, posInfo.TakeProfit());
|
||
|
|
|
||
|
|
// Close all sells if price drops below mean - grid (profit target)
|
||
|
|
if(sellCount > InpTR_PosThreshold && bid < meanSell - InpTR_TPFactor * InpTR_Grid * _Point)
|
||
|
|
{
|
||
|
|
ClosePositionsByType(MAGIC_TR, POSITION_TYPE_SELL);
|
||
|
|
PrintFormat("[TR] All SELLs Closed | Price=%.5f < MeanEntry-TP=%.5f", bid, meanSell - InpTR_TPFactor * InpTR_Grid * _Point);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| TR: Close all positions of a specific type by magic |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void ClosePositionsByType(int magic, ENUM_POSITION_TYPE pType)
|
||
|
|
{
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
if(posInfo.SelectByIndex(i) && posInfo.Symbol() == _Symbol &&
|
||
|
|
posInfo.Magic() == magic && posInfo.PositionType() == pType)
|
||
|
|
{
|
||
|
|
if(magic == MAGIC_MR)
|
||
|
|
tradeMR.PositionClose(posInfo.Ticket());
|
||
|
|
else
|
||
|
|
tradeTR.PositionClose(posInfo.Ticket());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| TR: Check if another buy position is too close (grid filter) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool OtherBuyPosition(int magic, double gridStep)
|
||
|
|
{
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
if(posInfo.SelectByIndex(i) && posInfo.Symbol() == _Symbol &&
|
||
|
|
posInfo.Magic() == magic && posInfo.PositionType() == POSITION_TYPE_BUY)
|
||
|
|
{
|
||
|
|
if(MathAbs(posInfo.PriceCurrent() - posInfo.PriceOpen()) < gridStep * _Point)
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| TR: Check if another sell position is too close (grid filter) |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool OtherSellPosition(int magic, double gridStep)
|
||
|
|
{
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
if(posInfo.SelectByIndex(i) && posInfo.Symbol() == _Symbol &&
|
||
|
|
posInfo.Magic() == magic && posInfo.PositionType() == POSITION_TYPE_SELL)
|
||
|
|
{
|
||
|
|
if(MathAbs(posInfo.PriceCurrent() - posInfo.PriceOpen()) < gridStep * _Point)
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| TR: Volume-weighted mean entry price for buys |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double MeanEntryPriceBuy(int magic)
|
||
|
|
{
|
||
|
|
double priceVol = 0;
|
||
|
|
double totalVol = 0;
|
||
|
|
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
if(posInfo.SelectByIndex(i) && posInfo.Symbol() == _Symbol &&
|
||
|
|
posInfo.Magic() == magic && posInfo.PositionType() == POSITION_TYPE_BUY)
|
||
|
|
{
|
||
|
|
priceVol += posInfo.Volume() * posInfo.PriceOpen();
|
||
|
|
totalVol += posInfo.Volume();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(totalVol == 0) return 0;
|
||
|
|
return priceVol / totalVol;
|
||
|
|
}
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| TR: Volume-weighted mean entry price for sells |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
double MeanEntryPriceSell(int magic)
|
||
|
|
{
|
||
|
|
double priceVol = 0;
|
||
|
|
double totalVol = 0;
|
||
|
|
|
||
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
|
{
|
||
|
|
if(posInfo.SelectByIndex(i) && posInfo.Symbol() == _Symbol &&
|
||
|
|
posInfo.Magic() == magic && posInfo.PositionType() == POSITION_TYPE_SELL)
|
||
|
|
{
|
||
|
|
priceVol += posInfo.Volume() * posInfo.PriceOpen();
|
||
|
|
totalVol += posInfo.Volume();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(totalVol == 0) return 0;
|
||
|
|
return priceVol / totalVol;
|
||
|
|
}
|
||
|
|
|
||
|
|
//======================================================================
|
||
|
|
// NEWS FILTER (from VWAP EA)
|
||
|
|
//======================================================================
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Check for upcoming high-impact news matching symbol currencies |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
bool IsNewsUpcoming()
|
||
|
|
{
|
||
|
|
MqlCalendarValue values[];
|
||
|
|
datetime startTime = TimeCurrent();
|
||
|
|
datetime endTime = startTime + (InpNewsBufferBefore * 60);
|
||
|
|
|
||
|
|
if(CalendarValueHistory(values, startTime, endTime) > 0)
|
||
|
|
{
|
||
|
|
for(int i = 0; i < ArraySize(values); i++)
|
||
|
|
{
|
||
|
|
MqlCalendarEvent event;
|
||
|
|
CalendarEventById(values[i].event_id, event);
|
||
|
|
|
||
|
|
if(event.importance == CALENDAR_IMPORTANCE_HIGH)
|
||
|
|
{
|
||
|
|
MqlCalendarCountry country;
|
||
|
|
CalendarCountryById(event.country_id, country);
|
||
|
|
|
||
|
|
if(StringFind(_Symbol, country.currency) >= 0)
|
||
|
|
{
|
||
|
|
newsEventMsg = event.name + " (" + country.currency + ")";
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if within 'After' buffer of recent news
|
||
|
|
startTime = TimeCurrent() - (InpNewsBufferAfter * 60);
|
||
|
|
endTime = TimeCurrent();
|
||
|
|
if(CalendarValueHistory(values, startTime, endTime) > 0)
|
||
|
|
{
|
||
|
|
for(int i = 0; i < ArraySize(values); i++)
|
||
|
|
{
|
||
|
|
MqlCalendarEvent event;
|
||
|
|
CalendarEventById(values[i].event_id, event);
|
||
|
|
|
||
|
|
if(event.importance == CALENDAR_IMPORTANCE_HIGH)
|
||
|
|
{
|
||
|
|
MqlCalendarCountry country;
|
||
|
|
CalendarCountryById(event.country_id, country);
|
||
|
|
|
||
|
|
if(StringFind(_Symbol, country.currency) >= 0)
|
||
|
|
{
|
||
|
|
newsEventMsg = event.name + " (Recent)";
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//======================================================================
|
||
|
|
// CHART COMMENTS / HUD
|
||
|
|
//======================================================================
|
||
|
|
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| Update chart commentary with full status |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
void UpdateComments(int mrBuys, int mrSells, int trBuys, int trSells, bool canTrade)
|
||
|
|
{
|
||
|
|
if(!InpShowComments)
|
||
|
|
{
|
||
|
|
Comment("");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
string regimeStr = "";
|
||
|
|
string regimeIcon = "";
|
||
|
|
switch(currentRegime)
|
||
|
|
{
|
||
|
|
case REGIME_MEAN_REVERT: regimeStr = "MEAN REVERSION"; regimeIcon = "◆"; break;
|
||
|
|
case REGIME_TRENDING: regimeStr = "TRENDING"; regimeIcon = "▲"; break;
|
||
|
|
case REGIME_NEUTRAL: regimeStr = "NEUTRAL"; regimeIcon = "●"; break;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Build score bar visualization [|||| ]
|
||
|
|
string scoreBar = "[";
|
||
|
|
int filled = (int)MathRound(regimeScore * 20);
|
||
|
|
for(int i = 0; i < 20; i++)
|
||
|
|
{
|
||
|
|
if(i < filled) scoreBar += "|";
|
||
|
|
else scoreBar += ".";
|
||
|
|
}
|
||
|
|
scoreBar += "]";
|
||
|
|
|
||
|
|
string comment = StringFormat(
|
||
|
|
"══════════════════════════════════════\n"
|
||
|
|
" %s ADAPTIVE REGIME EA %s\n"
|
||
|
|
"══════════════════════════════════════\n"
|
||
|
|
"REGIME: %s %s\n"
|
||
|
|
"Score: %.3f %s\n"
|
||
|
|
" MR ◄━━━━━━━━━━━━━━━━━━━━► TR\n"
|
||
|
|
"──────────────────────────────────────\n"
|
||
|
|
" MEAN REVERSION (VWAP)\n"
|
||
|
|
" VWAP: %.5f | Metric: %.5f\n"
|
||
|
|
" Buys: %d | Sells: %d\n"
|
||
|
|
"──────────────────────────────────────\n"
|
||
|
|
" TRENDING (Grid)\n"
|
||
|
|
" Grid Buy: %.0f | Grid Sell: %.0f\n"
|
||
|
|
" Buys: %d | Sells: %d\n"
|
||
|
|
"──────────────────────────────────────\n"
|
||
|
|
" Trading: %s\n"
|
||
|
|
" News: %s\n"
|
||
|
|
"══════════════════════════════════════",
|
||
|
|
regimeIcon, regimeIcon,
|
||
|
|
regimeStr, (currentRegime == REGIME_MEAN_REVERT ? " ↔" : (currentRegime == REGIME_TRENDING ? " ↗" : " —")),
|
||
|
|
regimeScore, scoreBar,
|
||
|
|
currVWAP, currMetric,
|
||
|
|
mrBuys, mrSells,
|
||
|
|
Grid_step_buy, Grid_step_sell,
|
||
|
|
trBuys, trSells,
|
||
|
|
(canTrade ? "ACTIVE" : "SUSPENDED"),
|
||
|
|
(isNewsImminent ? "⚠ " + newsEventMsg : "✓ Clear"));
|
||
|
|
|
||
|
|
Comment(comment);
|
||
|
|
}
|
||
|
|
//+------------------------------------------------------------------+
|