2025-09-20 02:27:35 -04:00
|
|
|
#include "..\IStrategy.mqh"
|
|
|
|
|
#include "..\KnowledgeBase.mqh"
|
|
|
|
|
#include "..\TradeManager.mqh"
|
|
|
|
|
|
|
|
|
|
class CIndicesEnergiesStrategy : public IStrategy
|
|
|
|
|
{
|
|
|
|
|
private:
|
|
|
|
|
// Multi-timeframe analysis
|
|
|
|
|
int m_fastTF, m_mediumTF, m_slowTF;
|
|
|
|
|
|
|
|
|
|
// Indicators for indices/energies
|
|
|
|
|
int m_vwapPeriod;
|
|
|
|
|
int m_fractalPeriod;
|
|
|
|
|
int m_envelopePeriod;
|
|
|
|
|
double m_envelopeDeviation;
|
|
|
|
|
|
|
|
|
|
// Cached values
|
|
|
|
|
double m_vwap, m_fractal_up, m_fractal_down;
|
|
|
|
|
double m_envelope_upper, m_envelope_lower;
|
|
|
|
|
double m_close0, m_close1;
|
|
|
|
|
|
|
|
|
|
public:
|
2026-02-05 23:31:20 -05:00
|
|
|
CIndicesEnergiesStrategy(const string symbol, const ENUM_TIMEFRAMES tf) : IStrategy("IndicesEnergiesStrategy", symbol, tf)
|
2025-09-20 02:27:35 -04:00
|
|
|
{
|
|
|
|
|
m_vwapPeriod = 24; // Daily VWAP for intraday
|
|
|
|
|
m_fractalPeriod = 5;
|
|
|
|
|
m_envelopePeriod = 20;
|
|
|
|
|
m_envelopeDeviation = 0.1; // 10% envelope
|
|
|
|
|
|
|
|
|
|
ResetValues();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual string Name() { return "IndicesEnergiesStrategy"; }
|
|
|
|
|
|
|
|
|
|
void ResetValues()
|
|
|
|
|
{
|
|
|
|
|
m_vwap = m_fractal_up = m_fractal_down = 0;
|
|
|
|
|
m_envelope_upper = m_envelope_lower = 0;
|
|
|
|
|
m_close0 = m_close1 = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void Refresh()
|
|
|
|
|
{
|
|
|
|
|
ResetValues();
|
2026-02-05 23:31:20 -05:00
|
|
|
m_close0 = iClose(m_symbol, m_timeframe, 0);
|
|
|
|
|
m_close1 = iClose(m_symbol, m_timeframe, 1);
|
2025-09-20 02:27:35 -04:00
|
|
|
|
|
|
|
|
// VWAP calculation (simplified)
|
2026-02-05 23:31:20 -05:00
|
|
|
double typical_price = (iHigh(m_symbol, m_timeframe, 0) + iLow(m_symbol, m_timeframe, 0) + iClose(m_symbol, m_timeframe, 0)) / 3;
|
|
|
|
|
double volume = (double)iVolume(m_symbol, m_timeframe, 0);
|
2025-09-20 02:27:35 -04:00
|
|
|
|
|
|
|
|
// For now, use a moving average as VWAP proxy
|
|
|
|
|
double vwap_buffer[];
|
2026-02-05 23:31:20 -05:00
|
|
|
int vwap_handle = iMA(m_symbol, m_timeframe, m_vwapPeriod, 0, MODE_SMA, PRICE_TYPICAL);
|
2025-09-20 02:27:35 -04:00
|
|
|
if(vwap_handle > 0) CopyBuffer(vwap_handle, 0, 0, 1, vwap_buffer);
|
|
|
|
|
m_vwap = vwap_buffer[0];
|
|
|
|
|
|
|
|
|
|
// Fractals
|
|
|
|
|
double fractal_up_buffer[], fractal_down_buffer[];
|
2026-02-05 23:31:20 -05:00
|
|
|
int fractal_handle = iFractals(m_symbol, m_timeframe);
|
2025-09-20 02:27:35 -04:00
|
|
|
if(fractal_handle > 0)
|
|
|
|
|
{
|
|
|
|
|
CopyBuffer(fractal_handle, 0, 0, 1, fractal_up_buffer);
|
|
|
|
|
CopyBuffer(fractal_handle, 1, 0, 1, fractal_down_buffer);
|
|
|
|
|
m_fractal_up = fractal_up_buffer[0];
|
|
|
|
|
m_fractal_down = fractal_down_buffer[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Envelope
|
|
|
|
|
double envelope_upper[], envelope_lower[];
|
2026-02-05 23:31:20 -05:00
|
|
|
int envelope_handle = iEnvelopes(m_symbol, m_timeframe, m_envelopePeriod, 0, MODE_SMA, PRICE_CLOSE, m_envelopeDeviation);
|
2025-09-20 02:27:35 -04:00
|
|
|
if(envelope_handle > 0)
|
|
|
|
|
{
|
|
|
|
|
CopyBuffer(envelope_handle, 0, 0, 1, envelope_upper);
|
|
|
|
|
CopyBuffer(envelope_handle, 1, 0, 1, envelope_lower);
|
|
|
|
|
m_envelope_upper = envelope_upper[0];
|
|
|
|
|
m_envelope_lower = envelope_lower[0];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual TradeOrder CheckSignal()
|
|
|
|
|
{
|
|
|
|
|
TradeOrder ord; ord.strategy_name = Name();
|
|
|
|
|
|
|
|
|
|
// Skip if insufficient data
|
|
|
|
|
if(m_vwap <= 0 || m_envelope_upper <= 0) return ord;
|
|
|
|
|
|
|
|
|
|
// VWAP-based strategies
|
|
|
|
|
double vwap_distance = (m_close0 - m_vwap) / m_vwap * 100;
|
|
|
|
|
|
|
|
|
|
// Fractal breakout strategy
|
|
|
|
|
bool fractal_breakout_up = m_close0 > m_fractal_up && m_fractal_up > 0;
|
|
|
|
|
bool fractal_breakout_down = m_close0 < m_fractal_down && m_fractal_down > 0;
|
|
|
|
|
|
|
|
|
|
// Envelope strategies
|
|
|
|
|
bool near_upper_envelope = m_close0 >= m_envelope_upper * 0.98;
|
|
|
|
|
bool near_lower_envelope = m_close0 <= m_envelope_lower * 1.02;
|
|
|
|
|
|
|
|
|
|
// Volume spike confirmation
|
2026-02-05 23:31:20 -05:00
|
|
|
double volume = (double)iVolume(m_symbol, m_timeframe, 0);
|
2025-09-20 02:27:35 -04:00
|
|
|
double volume_ma = 0;
|
|
|
|
|
double volume_ma_buffer[];
|
2026-02-05 23:31:20 -05:00
|
|
|
int volume_ma_handle = iMA(m_symbol, m_timeframe, 20, 0, MODE_SMA, VOLUME_TICK);
|
2025-09-20 02:27:35 -04:00
|
|
|
if(volume_ma_handle > 0) CopyBuffer(volume_ma_handle, 0, 0, 1, volume_ma_buffer);
|
|
|
|
|
volume_ma = volume_ma_buffer[0];
|
|
|
|
|
|
|
|
|
|
bool volume_spike = volume > volume_ma * 1.5;
|
|
|
|
|
|
|
|
|
|
// Long setup: VWAP reversion + fractal breakout
|
|
|
|
|
if(vwap_distance < -2.0 && near_lower_envelope && volume_spike)
|
|
|
|
|
{
|
|
|
|
|
ord.action = ACTION_BUY;
|
|
|
|
|
ord.order_type = ORDER_TYPE_BUY;
|
|
|
|
|
ord.stop_loss = m_envelope_lower * 0.99;
|
|
|
|
|
ord.take_profit = m_vwap;
|
|
|
|
|
ord.trailing_enabled = true;
|
|
|
|
|
ord.trailing_type = TRAIL_FIXED_POINTS;
|
|
|
|
|
ord.trail_activation_points = 10;
|
|
|
|
|
ord.trail_distance_points = 8;
|
|
|
|
|
ord.trail_step_points = 3;
|
|
|
|
|
return ord;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Short setup: VWAP reversion + fractal breakout
|
|
|
|
|
else if(vwap_distance > 2.0 && near_upper_envelope && volume_spike)
|
|
|
|
|
{
|
|
|
|
|
ord.action = ACTION_SELL;
|
|
|
|
|
ord.order_type = ORDER_TYPE_SELL;
|
|
|
|
|
ord.stop_loss = m_envelope_upper * 1.01;
|
|
|
|
|
ord.take_profit = m_vwap;
|
|
|
|
|
ord.trailing_enabled = true;
|
|
|
|
|
ord.trailing_type = TRAIL_FIXED_POINTS;
|
|
|
|
|
ord.trail_activation_points = 10;
|
|
|
|
|
ord.trail_distance_points = 8;
|
|
|
|
|
ord.trail_step_points = 3;
|
|
|
|
|
return ord;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fractal breakout strategy
|
|
|
|
|
else if(fractal_breakout_up && volume_spike)
|
|
|
|
|
{
|
|
|
|
|
ord.action = ACTION_BUY;
|
|
|
|
|
ord.order_type = ORDER_TYPE_BUY;
|
|
|
|
|
ord.stop_loss = m_fractal_up * 0.995;
|
|
|
|
|
ord.take_profit = m_fractal_up + (m_fractal_up - m_fractal_down) * 0.5;
|
|
|
|
|
return ord;
|
|
|
|
|
}
|
|
|
|
|
else if(fractal_breakout_down && volume_spike)
|
|
|
|
|
{
|
|
|
|
|
ord.action = ACTION_SELL;
|
|
|
|
|
ord.order_type = ORDER_TYPE_SELL;
|
|
|
|
|
ord.stop_loss = m_fractal_down * 1.005;
|
|
|
|
|
ord.take_profit = m_fractal_down - (m_fractal_up - m_fractal_down) * 0.5;
|
|
|
|
|
return ord;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ord;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
virtual void ExportFeatures(CFeaturesKB* kb, const datetime ts)
|
|
|
|
|
{
|
|
|
|
|
if(CheckPointer(kb)==POINTER_INVALID) return;
|
|
|
|
|
|
|
|
|
|
double vwap_distance = (m_close0 - m_vwap) / m_vwap * 100;
|
|
|
|
|
|
|
|
|
|
(*kb).WriteKV(ts, m_symbol, Name(), "vwap_distance_pct", vwap_distance);
|
|
|
|
|
(*kb).WriteKV(ts, m_symbol, Name(), "envelope_position",
|
|
|
|
|
(m_close0 - m_envelope_lower) / (m_envelope_upper - m_envelope_lower));
|
|
|
|
|
(*kb).WriteKV(ts, m_symbol, Name(), "fractal_range",
|
|
|
|
|
MathAbs(m_fractal_up - m_fractal_down));
|
|
|
|
|
}
|
|
|
|
|
};
|