mql5/Indicators/WhaleBubbles.mq5

154 行
6.1 KiB
MQL5

2026-02-16 15:58:59 +03:00
//+------------------------------------------------------------------+
//| WhaleBubbles.mq5 |
//| Copyright 2026, Antigravity AI |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Antigravity AI"
#property link "https://www.mql5.com"
2026-02-17 11:11:01 +03:00
#property version "1.21"
2026-02-16 15:58:59 +03:00
#property indicator_chart_window
#property indicator_plots 0
//--- Input Parameters
2026-02-17 11:11:01 +03:00
input double InpMinRatio = 3.0; // Min Intensity Ratio (x Avg)
input int InpBurstWindowMs = 500; // Burst Window (ms)
input int InpAvgWindowSec = 20; // Average Window (sec)
2026-02-16 15:58:59 +03:00
input double InpAbsorptionPips = 1.5; // Max Pips for Absorption
input color InpBuyColor = clrAqua;
input color InpSellColor = clrMagenta;
input color InpAbsorbColor = clrGold;
2026-02-17 11:11:01 +03:00
input int InpMaxBubbles = 300;
2026-02-16 15:58:59 +03:00
//--- Global Variables
2026-02-17 11:11:01 +03:00
long m_tick_history_ms[];
int m_history_size = 5000;
int m_history_ptr = 0;
2026-02-16 15:58:59 +03:00
double m_prev_price = 0;
2026-02-17 11:11:01 +03:00
double m_prev_bid = 0;
double m_prev_ask = 0;
2026-02-16 15:58:59 +03:00
int m_bubble_count = 0;
2026-02-17 11:11:01 +03:00
long m_unique_id = 0;
2026-02-16 15:58:59 +03:00
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
2026-02-17 11:11:01 +03:00
ArrayResize(m_tick_history_ms, m_history_size);
ArrayInitialize(m_tick_history_ms, 0);
IndicatorSetString(INDICATOR_SHORTNAME, "WhaleBubbles v1.21");
2026-02-16 15:58:59 +03:00
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, "WB_");
Comment("");
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
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[])
{
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick)) return(rates_total);
2026-02-17 11:11:01 +03:00
// 1. Log intensity
long now_ms = GetMicrosecondCount() / 1000;
m_tick_history_ms[m_history_ptr] = now_ms;
m_history_ptr = (m_history_ptr + 1) % m_history_size;
2026-02-16 15:58:59 +03:00
2026-02-17 11:11:01 +03:00
// 2. Calculate Intensity Ratio
int burst_ticks = 0;
int long_ticks = 0;
for(int i=0; i<m_history_size; i++)
{
if(m_tick_history_ms[i] >= (long)(now_ms - InpBurstWindowMs)) burst_ticks++;
if(m_tick_history_ms[i] >= (long)(now_ms - InpAvgWindowSec * 1000)) long_ticks++;
}
2026-02-16 15:58:59 +03:00
2026-02-17 11:11:01 +03:00
double avg_per_burst = (double)long_ticks * (InpBurstWindowMs / (InpAvgWindowSec * 1000.0));
if(avg_per_burst < 0.5) avg_per_burst = 0.5;
double ratio = (double)burst_ticks / avg_per_burst;
2026-02-16 15:58:59 +03:00
2026-02-17 11:11:01 +03:00
// 3. Direction Detection
string direction = "UNKNOWN";
2026-02-16 15:58:59 +03:00
double price = (tick.last != 0) ? tick.last : ((tick.bid + tick.ask) / 2.0);
2026-02-17 11:11:01 +03:00
if(tick.flags & TICK_FLAG_BUY) direction = "BUY";
else if(tick.flags & TICK_FLAG_SELL) direction = "SELL";
else if(tick.last != 0)
{
if(tick.last >= tick.ask) direction = "BUY";
else if(tick.last <= tick.bid) direction = "SELL";
}
else
{
if(tick.ask > m_prev_ask && m_prev_ask > 0) direction = "BUY";
else if(tick.bid < m_prev_bid && m_prev_bid > 0) direction = "SELL";
else if(tick.flags & TICK_FLAG_ASK) direction = "BUY";
else if(tick.flags & TICK_FLAG_BID) direction = "SELL";
else direction = (price > m_prev_price) ? "BUY" : (price < m_prev_price ? "SELL" : "UNKNOWN");
}
// 4. Signal and Drawing
2026-02-16 15:58:59 +03:00
double delta = MathAbs(price - m_prev_price);
2026-02-17 11:11:01 +03:00
bool is_whale = (ratio >= InpMinRatio && burst_ticks > 2);
2026-02-16 15:58:59 +03:00
bool is_absorbed = (is_whale && m_prev_price > 0 && delta <= InpAbsorptionPips * _Point * 10);
2026-02-17 11:11:01 +03:00
if(is_whale && direction != "UNKNOWN")
2026-02-16 15:58:59 +03:00
{
2026-02-17 11:11:01 +03:00
DrawBubble(tick, ratio, price, is_absorbed, burst_ticks, direction);
ChartRedraw();
2026-02-16 15:58:59 +03:00
}
m_prev_price = price;
2026-02-17 11:11:01 +03:00
m_prev_bid = tick.bid;
m_prev_ask = tick.ask;
2026-02-16 15:58:59 +03:00
2026-02-17 11:11:01 +03:00
Comment(StringFormat("WhaleBubbles v1.21\nRatio: %.2fx | Burst: %d\nLast Dir: %s", ratio, burst_ticks, direction));
return(rates_total);
2026-02-16 15:58:59 +03:00
}
//+------------------------------------------------------------------+
//| Drawing the Bubble |
//+------------------------------------------------------------------+
2026-02-17 11:11:01 +03:00
void DrawBubble(MqlTick &tick, double ratio, double price, bool absorbed, int burst_count, string direction)
2026-02-16 15:58:59 +03:00
{
2026-02-17 11:11:01 +03:00
m_unique_id++;
string name = StringFormat("WB_%d_%d", TimeCurrent(), m_unique_id);
2026-02-16 15:58:59 +03:00
2026-02-17 11:11:01 +03:00
if(m_bubble_count >= InpMaxBubbles) { ObjectsDeleteAll(0, "WB_"); m_bubble_count = 0; }
2026-02-16 15:58:59 +03:00
color clr = clrGray;
if(absorbed) clr = InpAbsorbColor;
2026-02-17 11:11:01 +03:00
else if(direction == "BUY") clr = InpBuyColor;
else if(direction == "SELL") clr = InpSellColor;
2026-02-16 15:58:59 +03:00
2026-02-17 11:11:01 +03:00
int width = (int)MathMin(10, MathMax(2, ratio));
2026-02-16 15:58:59 +03:00
if(ObjectCreate(0, name, OBJ_ARROW, 0, tick.time, price))
{
2026-02-17 11:11:01 +03:00
ObjectSetInteger(0, name, OBJPROP_ARROWCODE, 159);
2026-02-16 15:58:59 +03:00
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
ObjectSetString(0, name, OBJPROP_TOOLTIP,
2026-02-17 11:11:01 +03:00
StringFormat("Dir: %s\nRatio: %.1fx\nBurst: %d ticks", direction, ratio, burst_count));
2026-02-16 15:58:59 +03:00
m_bubble_count++;
}
}
//+------------------------------------------------------------------+