264 lines
8.7 KiB
MQL5
264 lines
8.7 KiB
MQL5
|
|
//+------------------------------------------------------------------+
|
||
|
|
//| DeltaFusionLite.mq5 |
|
||
|
|
//| Copyright 2025, MetaQuotes Ltd. |
|
||
|
|
//| https://www.mql5.com |
|
||
|
|
//+------------------------------------------------------------------+
|
||
|
|
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||
|
|
#property link "https://www.mql5.com/en/users/francescosecchi"
|
||
|
|
#property version "1.0"
|
||
|
|
#property description "DeltaFusion Lite - Cumulative Delta & Net Delta"
|
||
|
|
#property indicator_separate_window
|
||
|
|
#property indicator_buffers 3
|
||
|
|
#property indicator_plots 3
|
||
|
|
#property indicator_level1 0.0
|
||
|
|
#define EPSILON 1e-12
|
||
|
|
#define DEFAULT_MIN_TICK 0.00001
|
||
|
|
#define DEFAULT_TF_SECONDS 60
|
||
|
|
#define COLOR_CUM_ASK clrGreen
|
||
|
|
#define COLOR_CUM_BID clrRed
|
||
|
|
#define COLOR_NET_DELTA clrBlack
|
||
|
|
double CumAsk[];
|
||
|
|
double CumBid[];
|
||
|
|
double NetDelta[];
|
||
|
|
input string ResetSession = "=== Session Reset ==="; // Section header label used to group session reset inputs
|
||
|
|
input int ResetHourCustom = 1; // Hour of daily session reset (0-23)
|
||
|
|
input int ResetMinuteCustom = 0; // Minute of daily session reset (0-59)
|
||
|
|
input string CumulativeDelta = "=== Cumulative Delta ==="; // Section header label used to group cumulative delta inputs
|
||
|
|
input int SmoothPeriod = 10; // EMA smoothing period for delta (>=1)
|
||
|
|
datetime g_lastSession = 0;
|
||
|
|
bool g_firstRun = true;
|
||
|
|
double Eps() { return EPSILON; }
|
||
|
|
double SafeDiv(double numerator, double denominator)
|
||
|
|
{
|
||
|
|
if(MathAbs(denominator) <= Eps())
|
||
|
|
{
|
||
|
|
return (numerator > Eps()) ? 1e9 : 0.0;
|
||
|
|
}
|
||
|
|
double result = numerator / denominator;
|
||
|
|
if(!MathIsValidNumber(result)) return 0.0;
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
double SafeNormalize(double price)
|
||
|
|
{
|
||
|
|
return NormalizeDouble(price, _Digits);
|
||
|
|
}
|
||
|
|
int GetTFSeconds()
|
||
|
|
{
|
||
|
|
int sec = PeriodSeconds();
|
||
|
|
return (sec <= 0) ? DEFAULT_TF_SECONDS : sec;
|
||
|
|
}
|
||
|
|
double GetMinTick()
|
||
|
|
{
|
||
|
|
double tick = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
|
||
|
|
return (tick <= 0.0) ? DEFAULT_MIN_TICK : tick;
|
||
|
|
}
|
||
|
|
datetime GetSessionStart()
|
||
|
|
{
|
||
|
|
int mm = (ResetMinuteCustom < 0) ? 0 : ((ResetMinuteCustom > 59) ? 59 : ResetMinuteCustom);
|
||
|
|
datetime now = TimeCurrent();
|
||
|
|
MqlDateTime md;
|
||
|
|
TimeToStruct(now, md);
|
||
|
|
md.hour = ResetHourCustom;
|
||
|
|
md.min = mm;
|
||
|
|
md.sec = 0;
|
||
|
|
datetime sessionToday = StructToTime(md);
|
||
|
|
if(now < sessionToday)
|
||
|
|
{
|
||
|
|
sessionToday -= 24 * 60 * 60;
|
||
|
|
}
|
||
|
|
return sessionToday;
|
||
|
|
}
|
||
|
|
bool IsNewSession()
|
||
|
|
{
|
||
|
|
datetime currentSession = GetSessionStart();
|
||
|
|
if(currentSession != g_lastSession)
|
||
|
|
{
|
||
|
|
g_lastSession = currentSession;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
void SmoothEMA(const double &src[], int count, int period, double &emaOut[])
|
||
|
|
{
|
||
|
|
if(ArraySize(emaOut) != count)
|
||
|
|
{
|
||
|
|
ArrayResize(emaOut, count);
|
||
|
|
ArraySetAsSeries(emaOut, true);
|
||
|
|
}
|
||
|
|
if(count <= 0) return;
|
||
|
|
if(period <= 1)
|
||
|
|
{
|
||
|
|
ArrayCopy(emaOut, src, 0, 0, WHOLE_ARRAY);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
double alpha = 2.0 / (period + 1.0);
|
||
|
|
double prev = src[count - 1];
|
||
|
|
emaOut[count - 1] = prev;
|
||
|
|
for(int i = count - 2; i >= 0; i--)
|
||
|
|
{
|
||
|
|
prev = alpha * src[i] + (1.0 - alpha) * prev;
|
||
|
|
emaOut[i] = prev;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
void GetTickDeltaAskBid(int i,
|
||
|
|
const double &open[],
|
||
|
|
const double &high[],
|
||
|
|
const double &low[],
|
||
|
|
const double &close[],
|
||
|
|
const long &tick_volume[],
|
||
|
|
double &deltaAsk,
|
||
|
|
double &deltaBid)
|
||
|
|
{
|
||
|
|
deltaAsk = 0.0;
|
||
|
|
deltaBid = 0.0;
|
||
|
|
if(i < 0) return;
|
||
|
|
double o = open[i];
|
||
|
|
double c = close[i];
|
||
|
|
double h = high[i];
|
||
|
|
double l = low[i];
|
||
|
|
long tv = tick_volume[i];
|
||
|
|
double vol = (tv > 0) ? (double)tv : 1.0;
|
||
|
|
double spread = h - l;
|
||
|
|
if(spread <= 0.0) return;
|
||
|
|
double upper = MathMax(h - MathMax(o, c), 0.0);
|
||
|
|
double lower = MathMax(MathMin(o, c) - l, 0.0);
|
||
|
|
double body = spread - (upper + lower);
|
||
|
|
double pu = upper / spread;
|
||
|
|
double pl = lower / spread;
|
||
|
|
double pb = body / spread;
|
||
|
|
if(c > o)
|
||
|
|
{
|
||
|
|
deltaAsk = (pb + (pu + pl) / 2.0) * vol;
|
||
|
|
deltaBid = ((pu + pl) / 2.0) * vol;
|
||
|
|
}
|
||
|
|
else if(c < o)
|
||
|
|
{
|
||
|
|
deltaBid = (pb + (pu + pl) / 2.0) * vol;
|
||
|
|
deltaAsk = ((pu + pl) / 2.0) * vol;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
deltaAsk = deltaBid = ((pu + pl) / 2.0) * vol;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
int OnInit()
|
||
|
|
{
|
||
|
|
SetIndexBuffer(0, CumAsk, INDICATOR_DATA);
|
||
|
|
SetIndexBuffer(1, CumBid, INDICATOR_DATA);
|
||
|
|
SetIndexBuffer(2, NetDelta, INDICATOR_DATA);
|
||
|
|
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_HISTOGRAM);
|
||
|
|
PlotIndexSetInteger(0, PLOT_LINE_STYLE, STYLE_SOLID);
|
||
|
|
PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2);
|
||
|
|
PlotIndexSetInteger(0, PLOT_LINE_COLOR, COLOR_CUM_ASK);
|
||
|
|
PlotIndexSetString(0, PLOT_LABEL, "Ask Volume");
|
||
|
|
PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_HISTOGRAM);
|
||
|
|
PlotIndexSetInteger(1, PLOT_LINE_STYLE, STYLE_SOLID);
|
||
|
|
PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 2);
|
||
|
|
PlotIndexSetInteger(1, PLOT_LINE_COLOR, COLOR_CUM_BID);
|
||
|
|
PlotIndexSetString(1, PLOT_LABEL, "Bid Volume");
|
||
|
|
PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_LINE);
|
||
|
|
PlotIndexSetInteger(2, PLOT_LINE_STYLE, STYLE_SOLID);
|
||
|
|
PlotIndexSetInteger(2, PLOT_LINE_WIDTH, 1);
|
||
|
|
PlotIndexSetInteger(2, PLOT_LINE_COLOR, COLOR_NET_DELTA);
|
||
|
|
PlotIndexSetString(2, PLOT_LABEL, "Net Delta");
|
||
|
|
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
||
|
|
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
||
|
|
PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
||
|
|
ArraySetAsSeries(CumAsk, true);
|
||
|
|
ArraySetAsSeries(CumBid, true);
|
||
|
|
ArraySetAsSeries(NetDelta, true);
|
||
|
|
IndicatorSetString(INDICATOR_SHORTNAME, "DeltaFusion Lite v1.0");
|
||
|
|
IndicatorSetInteger(INDICATOR_LEVELS, 1);
|
||
|
|
IndicatorSetDouble(INDICATOR_LEVELVALUE, 0, 0.0);
|
||
|
|
IndicatorSetInteger(INDICATOR_LEVELSTYLE, 0, STYLE_DASH);
|
||
|
|
IndicatorSetInteger(INDICATOR_LEVELCOLOR, 0, clrGray);
|
||
|
|
IndicatorSetInteger(INDICATOR_LEVELWIDTH, 0, 1);
|
||
|
|
Print("DeltaFusion Lite initialized successfully");
|
||
|
|
return INIT_SUCCEEDED;
|
||
|
|
}
|
||
|
|
void OnDeinit(const int reason)
|
||
|
|
{
|
||
|
|
string reason_text;
|
||
|
|
switch(reason)
|
||
|
|
{
|
||
|
|
case REASON_PROGRAM: reason_text = "Program terminated"; break;
|
||
|
|
case REASON_REMOVE: reason_text = "Indicator removed"; break;
|
||
|
|
case REASON_RECOMPILE: reason_text = "Recompiled"; break;
|
||
|
|
case REASON_CHARTCHANGE: reason_text = "Chart changed"; break;
|
||
|
|
case REASON_CHARTCLOSE: reason_text = "Chart closed"; break;
|
||
|
|
case REASON_PARAMETERS: reason_text = "Parameters changed"; break;
|
||
|
|
case REASON_ACCOUNT: reason_text = "Account changed"; break;
|
||
|
|
default: reason_text = "Unknown reason"; break;
|
||
|
|
}
|
||
|
|
Print("Indicator deinitialized: ", reason_text);
|
||
|
|
}
|
||
|
|
int OnCalculate(const int rates_total,
|
||
|
|
const int prev_calculated,
|
||
|
|
const datetime &time[],
|
||
|
|
const double &open[],
|
||
|
|
const double &high[],
|
||
|
|
const double &low[],
|
||
|
|
const double &close[],
|
||
|
|
const long &tick_volume[],
|
||
|
|
const long &volume[],
|
||
|
|
const int &spread[])
|
||
|
|
{
|
||
|
|
if(rates_total <= 0) return 0;
|
||
|
|
ArraySetAsSeries(time, true);
|
||
|
|
ArraySetAsSeries(open, true);
|
||
|
|
ArraySetAsSeries(high, true);
|
||
|
|
ArraySetAsSeries(low, true);
|
||
|
|
ArraySetAsSeries(close, true);
|
||
|
|
ArraySetAsSeries(tick_volume, true);
|
||
|
|
int start;
|
||
|
|
if(prev_calculated == 0 || g_firstRun)
|
||
|
|
{
|
||
|
|
start = rates_total - 1;
|
||
|
|
g_firstRun = false;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
start = rates_total - prev_calculated;
|
||
|
|
if(start > 0) start--;
|
||
|
|
}
|
||
|
|
if(start < 0) start = 0;
|
||
|
|
if(start >= rates_total) start = rates_total - 1;
|
||
|
|
static double dA[];
|
||
|
|
static double dB[];
|
||
|
|
if(ArraySize(dA) != rates_total)
|
||
|
|
{
|
||
|
|
ArrayResize(dA, rates_total);
|
||
|
|
ArraySetAsSeries(dA, true);
|
||
|
|
}
|
||
|
|
if(ArraySize(dB) != rates_total)
|
||
|
|
{
|
||
|
|
ArrayResize(dB, rates_total);
|
||
|
|
ArraySetAsSeries(dB, true);
|
||
|
|
}
|
||
|
|
for(int i = start; i >= 0; i--)
|
||
|
|
{
|
||
|
|
GetTickDeltaAskBid(i, open, high, low, close, tick_volume, dA[i], dB[i]);
|
||
|
|
}
|
||
|
|
static double emaA[];
|
||
|
|
static double emaB[];
|
||
|
|
SmoothEMA(dA, rates_total, SmoothPeriod, emaA);
|
||
|
|
SmoothEMA(dB, rates_total, SmoothPeriod, emaB);
|
||
|
|
for(int i2 = start; i2 >= 0; i2--)
|
||
|
|
{
|
||
|
|
CumAsk[i2] = (emaA[i2] > 0.0) ? emaA[i2] : EMPTY_VALUE;
|
||
|
|
CumBid[i2] = (emaB[i2] > 0.0) ? -emaB[i2] : EMPTY_VALUE;
|
||
|
|
NetDelta[i2] = emaA[i2] - emaB[i2];
|
||
|
|
}
|
||
|
|
datetime sessionStart = GetSessionStart();
|
||
|
|
double sumAsk = 0.0, sumBid = 0.0;
|
||
|
|
int firstIdx = -1, lastIdx = -1;
|
||
|
|
for(int i3 = 0; i3 < rates_total; i3++)
|
||
|
|
{
|
||
|
|
if(time[i3] < sessionStart) break;
|
||
|
|
if(firstIdx < 0) firstIdx = i3;
|
||
|
|
lastIdx = i3;
|
||
|
|
if(CumAsk[i3] != EMPTY_VALUE) sumAsk += MathAbs(CumAsk[i3]);
|
||
|
|
if(CumBid[i3] != EMPTY_VALUE) sumBid += MathAbs(CumBid[i3]);
|
||
|
|
}
|
||
|
|
return rates_total;
|
||
|
|
}
|