142 líneas
6,1 KiB
MQL5
142 líneas
6,1 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| WhaleDetector.mq5 |
|
|
//| Copyright 2026, Antigravity AI |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Copyright 2026, Antigravity AI"
|
|
#property link "https://www.mql5.com"
|
|
#property version "1.21"
|
|
#property strict
|
|
|
|
#include <Trade\Trade.mqh>
|
|
|
|
//--- Input Parameters
|
|
input double InpIntensityMultiplier = 4.0; // Intensity Spike Multiplier (x Avg)
|
|
input int InpIntensityWindowMs = 500; // Burst Window (Milliseconds)
|
|
input int InpAvgIntWindowSec = 10; // Average Window (Seconds)
|
|
input int InpAbsorptionPips = 2; // Absorption Move (Max Pips)
|
|
input bool InpAutoTrade = true; // Place trades on detections?
|
|
input double InpLotSize = 0.1; // Lot Size for auto-trading
|
|
input int InpMagicNumber = 987654;
|
|
|
|
//--- Global Variables
|
|
CTrade m_trade;
|
|
datetime m_tick_history_ms[]; // Timestamps in milliseconds
|
|
int m_tick_history_size = 5000;
|
|
int m_tick_ptr = 0;
|
|
double m_prev_price = 0;
|
|
double m_prev_bid = 0;
|
|
double m_prev_ask = 0;
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
m_trade.SetExpertMagicNumber(InpMagicNumber);
|
|
ArrayResize(m_tick_history_ms, m_tick_history_size);
|
|
ArrayInitialize(m_tick_history_ms, 0);
|
|
|
|
Print("WhaleDetector v1.21 (Fixed Direction) Initialized.");
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
MqlTick tick;
|
|
if(!SymbolInfoTick(_Symbol, tick)) return;
|
|
|
|
// 1. Log Timestamp
|
|
long now_ms = GetMicrosecondCount() / 1000;
|
|
m_tick_history_ms[m_tick_ptr] = (datetime)now_ms;
|
|
m_tick_ptr = (m_tick_ptr + 1) % m_tick_history_size;
|
|
|
|
// 2. Calculate Intensity
|
|
int current_intensity = 0;
|
|
for(int i=0; i<m_tick_history_size; i++)
|
|
{
|
|
if(m_tick_history_ms[i] >= (datetime)(now_ms - InpIntensityWindowMs)) current_intensity++;
|
|
}
|
|
|
|
int total_ticks_long = 0;
|
|
for(int i=0; i<m_tick_history_size; i++)
|
|
{
|
|
if(m_tick_history_ms[i] >= (datetime)(now_ms - InpAvgIntWindowSec * 1000)) total_ticks_long++;
|
|
}
|
|
double avg_intensity_per_window = (double)total_ticks_long * (InpIntensityWindowMs / (InpAvgIntWindowSec * 1000.0));
|
|
if(avg_intensity_per_window < 1.0) avg_intensity_per_window = 1.0;
|
|
|
|
// 3. Whale Detection
|
|
bool is_whale = (current_intensity >= avg_intensity_per_window * InpIntensityMultiplier);
|
|
|
|
double price = (tick.last != 0) ? tick.last : ((tick.bid + tick.ask) / 2.0);
|
|
double delta = MathAbs(price - m_prev_price);
|
|
bool is_absorbed = (is_whale && m_prev_price > 0 && delta < InpAbsorptionPips * _Point * 10);
|
|
|
|
if(is_whale)
|
|
{
|
|
string direction = "UNKNOWN";
|
|
|
|
// Robust Direction Detection
|
|
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 // Fallback for last == 0
|
|
{
|
|
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");
|
|
}
|
|
|
|
if(direction != "UNKNOWN")
|
|
HandleWhale(tick, current_intensity, avg_intensity_per_window, direction, is_absorbed);
|
|
}
|
|
|
|
// Dashboard update
|
|
Comment(StringFormat("WhaleDetector v1.21\n"
|
|
"-------------------\n"
|
|
"Intensity: %d / Avg: %.2f\n"
|
|
"Ratio: %.2fx\n"
|
|
"Bid: %.5f | Ask: %.5f\n"
|
|
"Status: %s",
|
|
current_intensity, avg_intensity_per_window,
|
|
(double)current_intensity / avg_intensity_per_window,
|
|
tick.bid, tick.ask,
|
|
is_whale ? "WHALE!" : "monitoring..."));
|
|
|
|
m_prev_price = price;
|
|
m_prev_bid = tick.bid;
|
|
m_prev_ask = tick.ask;
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Handle Whale Event |
|
|
//+------------------------------------------------------------------+
|
|
void HandleWhale(MqlTick &tick, int intensity, double avg, string direction, bool absorbed)
|
|
{
|
|
double price = (tick.last != 0) ? tick.last : (direction == "BUY" ? tick.ask : tick.bid);
|
|
|
|
string name = StringFormat("Whale_%d_%d", TimeCurrent(), GetMicrosecondCount());
|
|
ObjectCreate(0, name, OBJ_ARROW, 0, tick.time, price);
|
|
ObjectSetInteger(0, name, OBJPROP_ARROWCODE, (direction == "BUY") ? 233 : 234);
|
|
ObjectSetInteger(0, name, OBJPROP_COLOR, (direction == "BUY") ? clrAqua : clrMagenta);
|
|
ObjectSetInteger(0, name, OBJPROP_WIDTH, absorbed ? 5 : 3);
|
|
|
|
if(InpAutoTrade && PositionsTotal() == 0)
|
|
{
|
|
double sl = (direction == "BUY") ? price - 500*_Point : price + 500*_Point;
|
|
double tp = (direction == "BUY") ? price + 1000*_Point : price - 1000*_Point;
|
|
if(direction == "BUY") m_trade.Buy(InpLotSize, _Symbol, tick.ask, sl, tp, "Whale Intensity Buy");
|
|
else m_trade.Sell(InpLotSize, _Symbol, tick.bid, sl, tp, "Whale Intensity Sell");
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|