mirror of
https://github.com/dinethlive/dbasket-EA.git
synced 2026-08-23 23:58:10 +00:00
Initial commit: D-Basket EA v2.0 Pro
This commit is contained in:
@@ -0,0 +1,332 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DBasket_VolatilityBalancer.mqh |
|
||||
//| D-Basket Correlation Hedging EA |
|
||||
//| ATR-Based Position Sizing |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "D-Basket EA"
|
||||
#property version "2.00"
|
||||
#property strict
|
||||
|
||||
#ifndef DBASKET_VOLATILITYBALANCER_MQH
|
||||
#define DBASKET_VOLATILITYBALANCER_MQH
|
||||
|
||||
#include "DBasket_Defines.mqh"
|
||||
#include "DBasket_Structures.mqh"
|
||||
#include "DBasket_Logger.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Volatility Data Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct VolatilityData
|
||||
{
|
||||
double atr[NUM_SYMBOLS]; // ATR values for each symbol
|
||||
double weights[NUM_SYMBOLS]; // Inverse volatility weights
|
||||
double adjustedLots[NUM_SYMBOLS]; // Final lot sizes
|
||||
datetime lastUpdateTime; // Timestamp of last calculation
|
||||
bool isValid; // True if calculation succeeded
|
||||
|
||||
void Reset()
|
||||
{
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
{
|
||||
atr[i] = 0;
|
||||
weights[i] = 0.333333; // Default equal weight
|
||||
adjustedLots[i] = 0.01;
|
||||
}
|
||||
lastUpdateTime = 0;
|
||||
isValid = false;
|
||||
}
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Volatility Balancer Class |
|
||||
//| Risk Parity Position Sizing via ATR |
|
||||
//+------------------------------------------------------------------+
|
||||
class CVolatilityBalancer
|
||||
{
|
||||
private:
|
||||
// Configuration
|
||||
string m_symbols[NUM_SYMBOLS];
|
||||
int m_atrPeriod; // ATR lookback period
|
||||
int m_atrHandles[NUM_SYMBOLS]; // ATR indicator handles
|
||||
double m_minWeight; // Minimum weight per symbol
|
||||
double m_maxWeight; // Maximum weight per symbol
|
||||
bool m_enabled; // ATR sizing enabled
|
||||
|
||||
// State
|
||||
VolatilityData m_cache;
|
||||
int m_barsSinceUpdate;
|
||||
bool m_isInitialized;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Normalize lot size to broker requirements |
|
||||
//+------------------------------------------------------------------+
|
||||
double NormalizeLots(string symbol, double lots)
|
||||
{
|
||||
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
||||
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
||||
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
||||
|
||||
if(lotStep == 0) lotStep = 0.01;
|
||||
if(minLot == 0) minLot = 0.01;
|
||||
if(maxLot == 0) maxLot = 100.0;
|
||||
|
||||
// Round to lot step
|
||||
lots = MathFloor(lots / lotStep) * lotStep;
|
||||
|
||||
// Clamp to min/max
|
||||
lots = MathMax(minLot, MathMin(lots, maxLot));
|
||||
|
||||
return NormalizeDouble(lots, 2);
|
||||
}
|
||||
|
||||
public:
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CVolatilityBalancer()
|
||||
{
|
||||
m_atrPeriod = 14;
|
||||
m_minWeight = 0.15;
|
||||
m_maxWeight = 0.50;
|
||||
m_enabled = true;
|
||||
m_barsSinceUpdate = 999;
|
||||
m_isInitialized = false;
|
||||
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
{
|
||||
m_symbols[i] = "";
|
||||
m_atrHandles[i] = INVALID_HANDLE;
|
||||
}
|
||||
|
||||
m_cache.Reset();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor - Release indicator handles |
|
||||
//+------------------------------------------------------------------+
|
||||
~CVolatilityBalancer()
|
||||
{
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
{
|
||||
if(m_atrHandles[i] != INVALID_HANDLE)
|
||||
{
|
||||
IndicatorRelease(m_atrHandles[i]);
|
||||
m_atrHandles[i] = INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Initialize volatility balancer |
|
||||
//+------------------------------------------------------------------+
|
||||
bool Initialize(const string &symbols[], int atrPeriod,
|
||||
double minWeight, double maxWeight, bool enabled)
|
||||
{
|
||||
m_atrPeriod = atrPeriod;
|
||||
m_minWeight = minWeight;
|
||||
m_maxWeight = maxWeight;
|
||||
m_enabled = enabled;
|
||||
|
||||
// Copy symbols
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
m_symbols[i] = symbols[i];
|
||||
|
||||
// Create ATR indicator handles
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
{
|
||||
m_atrHandles[i] = iATR(m_symbols[i], PERIOD_CURRENT, m_atrPeriod);
|
||||
if(m_atrHandles[i] == INVALID_HANDLE)
|
||||
{
|
||||
Logger.Error("Failed to create ATR handle for " + m_symbols[i]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_isInitialized = true;
|
||||
m_cache.Reset();
|
||||
m_barsSinceUpdate = 999;
|
||||
|
||||
Logger.Info("Volatility Balancer initialized - ATR Period: " + IntegerToString(m_atrPeriod) +
|
||||
", Enabled: " + (m_enabled ? "Yes" : "No"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update ATR values and calculate weights |
|
||||
//+------------------------------------------------------------------+
|
||||
bool Update(bool forceUpdate = false)
|
||||
{
|
||||
if(!m_isInitialized)
|
||||
{
|
||||
Logger.Error("Volatility Balancer not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if update needed
|
||||
m_barsSinceUpdate++;
|
||||
if(!forceUpdate && m_barsSinceUpdate < 1 && m_cache.isValid)
|
||||
{
|
||||
return true; // Use cached values
|
||||
}
|
||||
|
||||
m_barsSinceUpdate = 0;
|
||||
|
||||
// Get ATR values for each symbol
|
||||
double totalATR = 0;
|
||||
double totalInvATR = 0;
|
||||
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
{
|
||||
double buffer[1];
|
||||
if(CopyBuffer(m_atrHandles[i], 0, 0, 1, buffer) != 1)
|
||||
{
|
||||
Logger.Warning("Failed to get ATR for " + m_symbols[i] + ", using cached value");
|
||||
if(m_cache.atr[i] <= 0)
|
||||
{
|
||||
m_cache.isValid = false;
|
||||
return false;
|
||||
}
|
||||
// Use cached ATR
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cache.atr[i] = buffer[0];
|
||||
}
|
||||
|
||||
if(m_cache.atr[i] <= 0)
|
||||
{
|
||||
Logger.Error("Invalid ATR value for " + m_symbols[i]);
|
||||
m_cache.isValid = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
totalATR += m_cache.atr[i];
|
||||
totalInvATR += 1.0 / m_cache.atr[i];
|
||||
}
|
||||
|
||||
// Calculate inverse volatility weights
|
||||
// Higher volatility = smaller weight
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
{
|
||||
double rawWeight = (1.0 / m_cache.atr[i]) / totalInvATR;
|
||||
|
||||
// Apply min/max constraints
|
||||
rawWeight = MathMax(m_minWeight, MathMin(rawWeight, m_maxWeight));
|
||||
|
||||
m_cache.weights[i] = rawWeight;
|
||||
}
|
||||
|
||||
// Renormalize weights to sum to 1.0
|
||||
double totalWeight = 0;
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
totalWeight += m_cache.weights[i];
|
||||
|
||||
if(totalWeight > 0)
|
||||
{
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
m_cache.weights[i] /= totalWeight;
|
||||
}
|
||||
|
||||
m_cache.lastUpdateTime = TimeCurrent();
|
||||
m_cache.isValid = true;
|
||||
|
||||
Logger.Debug("ATR Weights updated: AUDCAD=" + DoubleToString(m_cache.weights[SYMBOL_AUDCAD], 3) +
|
||||
", NZDCAD=" + DoubleToString(m_cache.weights[SYMBOL_NZDCAD], 3) +
|
||||
", AUDNZD=" + DoubleToString(m_cache.weights[SYMBOL_AUDNZD], 3));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate weighted lot sizes |
|
||||
//| baseLots: total lot budget |
|
||||
//| lots[]: output array with adjusted lot sizes |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CalculateWeightedLots(double baseLots, double &lots[])
|
||||
{
|
||||
if(ArraySize(lots) < NUM_SYMBOLS)
|
||||
ArrayResize(lots, NUM_SYMBOLS);
|
||||
|
||||
if(!m_enabled || !m_cache.isValid)
|
||||
{
|
||||
// Fallback to equal sizing
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
{
|
||||
lots[i] = NormalizeLots(m_symbols[i], baseLots);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Apply weights to base lots
|
||||
// Multiply by 3 because weights sum to 1.0 but we want 3 positions
|
||||
for(int i = 0; i < NUM_SYMBOLS; i++)
|
||||
{
|
||||
double rawLots = baseLots * m_cache.weights[i] * 3.0;
|
||||
lots[i] = NormalizeLots(m_symbols[i], rawLots);
|
||||
m_cache.adjustedLots[i] = lots[i];
|
||||
}
|
||||
|
||||
Logger.Debug("Weighted lots: AUDCAD=" + DoubleToString(lots[SYMBOL_AUDCAD], 2) +
|
||||
", NZDCAD=" + DoubleToString(lots[SYMBOL_NZDCAD], 2) +
|
||||
", AUDNZD=" + DoubleToString(lots[SYMBOL_AUDNZD], 2));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get weight for a specific symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetWeight(int symbolIndex)
|
||||
{
|
||||
if(symbolIndex < 0 || symbolIndex >= NUM_SYMBOLS)
|
||||
return 0.333333;
|
||||
return m_cache.weights[symbolIndex];
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get ATR for a specific symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetATR(int symbolIndex)
|
||||
{
|
||||
if(symbolIndex < 0 || symbolIndex >= NUM_SYMBOLS)
|
||||
return 0;
|
||||
return m_cache.atr[symbolIndex];
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get cached volatility data |
|
||||
//+------------------------------------------------------------------+
|
||||
void GetData(VolatilityData &data)
|
||||
{
|
||||
data = m_cache;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Is enabled |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsEnabled()
|
||||
{
|
||||
return m_enabled;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Is cache valid |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsValid()
|
||||
{
|
||||
return m_cache.isValid;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Enable/disable volatility balancing |
|
||||
//+------------------------------------------------------------------+
|
||||
void SetEnabled(bool enabled)
|
||||
{
|
||||
m_enabled = enabled;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // DBASKET_VOLATILITYBALANCER_MQH
|
||||
//+------------------------------------------------------------------+
|
||||
Reference in New Issue
Block a user