111 lines
4.8 KiB
Plaintext
111 lines
4.8 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| Risk/PortfolioManager.mqh |
|
|
//+------------------------------------------------------------------+
|
|
#ifndef __PORTFOLIO_MANAGER_MQH__
|
|
#define __PORTFOLIO_MANAGER_MQH__
|
|
|
|
#include "../Core/Config.mqh"
|
|
#include "../Core/State.mqh"
|
|
#include "../Core/Logger.mqh"
|
|
|
|
extern CLogger g_logger;
|
|
|
|
class CPortfolioManager
|
|
{
|
|
private:
|
|
int m_corrLookback;
|
|
ENUM_TIMEFRAMES m_mtf;
|
|
double m_maxTotalRiskPercent;
|
|
|
|
public:
|
|
bool Init(int lookback, ENUM_TIMEFRAMES mtf)
|
|
{
|
|
m_corrLookback = lookback; m_mtf = mtf; m_maxTotalRiskPercent = InpMaxTotalRisk;
|
|
Print("[PortfolioManager] Correlation lookback: ", lookback, " bars");
|
|
return true;
|
|
}
|
|
void UpdateState(EAState &state)
|
|
{
|
|
state.openPositions = 0; double totalRiskAmount = 0;
|
|
int posTotal = PositionsTotal();
|
|
for(int i = posTotal - 1; i >= 0; i--)
|
|
{
|
|
string sym = PositionGetSymbol(i);
|
|
if(sym != _Symbol) continue;
|
|
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
|
|
state.openPositions++;
|
|
double lots = PositionGetDouble(POSITION_VOLUME);
|
|
double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double sl = PositionGetDouble(POSITION_SL);
|
|
double slDist = MathAbs(entry - sl);
|
|
double tickValue = SymbolInfoDouble(sym, SYMBOL_TRADE_TICK_VALUE);
|
|
double tickSize = SymbolInfoDouble(sym, SYMBOL_TRADE_TICK_SIZE);
|
|
if(tickSize > 0) { double ticks = slDist / tickSize; totalRiskAmount += lots * ticks * tickValue; }
|
|
}
|
|
double equity = AccountInfoDouble(ACCOUNT_EQUITY);
|
|
if(equity > 0) state.totalOpenRisk = (totalRiskAmount / equity) * 100.0;
|
|
else state.totalOpenRisk = 0;
|
|
}
|
|
bool IsCorrelated(const SignalData &signal, const EAState &state)
|
|
{
|
|
if(!InpUseCorrelationFilter) return false;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
string posSymbol = PositionGetSymbol(i);
|
|
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
|
|
if(posSymbol == _Symbol) continue;
|
|
double corr = CalculateCorrelation(_Symbol, posSymbol);
|
|
if(MathAbs(corr) > 0.7)
|
|
{
|
|
g_logger.LogEvent("PORTFOLIO", StringFormat("REJECTED: Correlation %.2f with %s", corr, posSymbol));
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
bool CheckExposure(const TradeParams ¶ms, const EAState &state)
|
|
{
|
|
double projectedRisk = state.totalOpenRisk + params.riskPercent;
|
|
if(projectedRisk > m_maxTotalRiskPercent)
|
|
{
|
|
g_logger.LogEvent("PORTFOLIO", StringFormat("REJECTED: Risk %.2f%% > max %.2f%%", projectedRisk, m_maxTotalRiskPercent));
|
|
return false;
|
|
}
|
|
int forexCount = 0, metalCount = 0, indexCount = 0, cryptoCount = 0;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(PositionGetInteger(POSITION_MAGIC) != InpMagicNumber) continue;
|
|
string sym = PositionGetSymbol(i);
|
|
if(StringFind(sym, "XAU") >= 0 || StringFind(sym, "XAG") >= 0) metalCount++;
|
|
else if(StringFind(sym, "BTC") >= 0 || StringFind(sym, "ETH") >= 0) cryptoCount++;
|
|
else if(StringFind(sym, "US30") >= 0 || StringFind(sym, "NAS") >= 0 || StringFind(sym, "GER") >= 0) indexCount++;
|
|
else forexCount++;
|
|
}
|
|
ENUM_ASSET_CLASS cls = state.assetProfile.assetClass;
|
|
if((cls == ASSET_FOREX_MAJOR || cls == ASSET_FOREX_CROSS) && forexCount >= 2) { g_logger.LogEvent("PORTFOLIO", "REJECTED: Max 2 Forex"); return false; }
|
|
if(cls == ASSET_METAL && metalCount >= 1) { g_logger.LogEvent("PORTFOLIO", "REJECTED: Max 1 Metal"); return false; }
|
|
if(cls == ASSET_INDEX && indexCount >= 1) { g_logger.LogEvent("PORTFOLIO", "REJECTED: Max 1 Index"); return false; }
|
|
if(cls == ASSET_CRYPTO && cryptoCount >= 1) { g_logger.LogEvent("PORTFOLIO", "REJECTED: Max 1 Crypto"); return false; }
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
double CalculateCorrelation(string sym1, string sym2)
|
|
{
|
|
double c1[], c2[]; ArraySetAsSeries(c1, true); ArraySetAsSeries(c2, true);
|
|
if(CopyClose(sym1, m_mtf, 1, m_corrLookback, c1) < m_corrLookback) return 0;
|
|
if(CopyClose(sym2, m_mtf, 1, m_corrLookback, c2) < m_corrLookback) return 0;
|
|
double mean1 = 0, mean2 = 0;
|
|
for(int i = 0; i < m_corrLookback; i++) { mean1 += c1[i]; mean2 += c2[i]; }
|
|
mean1 /= m_corrLookback; mean2 /= m_corrLookback;
|
|
double cov = 0, var1 = 0, var2 = 0;
|
|
for(int i = 0; i < m_corrLookback; i++)
|
|
{ double d1 = c1[i] - mean1; double d2 = c2[i] - mean2; cov += d1 * d2; var1 += d1 * d1; var2 += d2 * d2; }
|
|
double std1 = MathSqrt(var1); double std2 = MathSqrt(var2);
|
|
if(std1 * std2 == 0) return 0;
|
|
return cov / (std1 * std2);
|
|
}
|
|
};
|
|
|
|
#endif // __PORTFOLIO_MANAGER_MQH__
|