//+------------------------------------------------------------------+ //| CascadeGreepMaster | //| Copyright 2025 Cascade (AI) | //| Purpose: Publish per-symbol risk/SL/cooldown multipliers | //| based on News, Volatility, and Equity regimes. | //+------------------------------------------------------------------+ #property copyright "Cascade AI" #property link "https://windsurf.ai" #property version "1.0" #property strict #include //==================== Inputs ==================== input group "Symbols" input string InpSymbols = "EURUSD;AUDUSD;USDCAD;USDJPY;XAUUSD"; // semicolon-separated input string InpGVNamespace = "CGreep"; // base for global variables input int InpUpdateSeconds = 60; // update frequency input group "News Regime" input bool InpUseNews = true; input int InpNewsWindowBeforeMin = 60; // minutes before event input int InpNewsWindowAfterMin = 15; // minutes after event input double InpNewsWeightLow = 0.85; // min risk factor near low-impact input double InpNewsWeightMedium = 0.60; // min risk factor near medium-impact input double InpNewsWeightHigh = 0.30; // min risk factor near high-impact input group "Volatility Regime (ATR)" input bool InpUseVolatility = true; input ENUM_TIMEFRAMES InpVolTF = PERIOD_H1; input int InpATRPeriod = 14; input int InpATRAvgLookback = 100; // bars for average ATR baseline input double InpVolMinRiskFactor = 0.50; // floor when volatility is extreme input bool InpVolAllowUpsize = false; // allow >1.0 risk factor in very low vol input group "Equity Regime" input bool InpUseEquity = true; input double InpDailyLossSoftPercent = 1.0; // when daily DD >= this, reduce risk input double InpEquityMinRiskFactor = 0.50; // floor under soft daily DD input bool InpEquityAllowUpsize = false; // allow >1.0 on strong daily profit input double InpDailyWinBoostCap = 1.10; // max upsize if allowed input group "Composition & Publishing" input double InpMinRiskFactor = 0.20; // global floor input double InpMaxRiskFactor = 1.00; // global cap input double InpSLMultGainOnCut = 1.00; // sl_mult = 1 + gain*(1-risk_factor) input double InpCooldownGainOnCut = 2.00; // cooldown_mult = 1 + gain*(1-risk_factor) input group "XAU Overrides" input double InpXAU_NewsWeightLow = 0.80; // XAU: min risk factor near low-impact input double InpXAU_NewsWeightMedium = 0.50; // XAU: medium-impact input double InpXAU_NewsWeightHigh = 0.25; // XAU: high-impact input int InpXAU_NewsWindowBeforeMin = 90; // XAU: minutes before input int InpXAU_NewsWindowAfterMin = 30; // XAU: minutes after input double InpXAU_VolMinRiskFactor = 0.35; // XAU: stronger cut under high vol input bool InpXAU_VolAllowUpsize = false; // XAU: no upsize on low vol input double InpXAU_MinRiskFactor = 0.30; // XAU: global floor input double InpXAU_SLMultGainOnCut = 1.50; // XAU: widen SL more when cut input double InpXAU_CooldownGainOnCut = 3.00; // XAU: longer cooldown when cut //==================== Globals ==================== string g_symbols[]; int g_day_key=0; double g_day_equity_start=0.0; //==================== Utils ==================== void TrimString(string &s) { int start=0; while(start=0 && StringGetCharacter(s,end)<=32) end--; if(end0) { int n=ArraySize(out); ArrayResize(out,n+1); out[n]=token; } last=pos+dlen; } string tail=StringSubstr(src,last); TrimString(tail); if(StringLen(tail)>0) { int n=ArraySize(out); ArrayResize(out,n+1); out[n]=tail; } } int TodayKey() { MqlDateTime dt; TimeToStruct(TimeTradeServer(), dt); return dt.year*10000+dt.mon*100+dt.day; } void DailyResetIfNeeded() { int key=TodayKey(); if(key!=g_day_key) { g_day_key=key; g_day_equity_start=AccountInfoDouble(ACCOUNT_EQUITY); Print("[MASTER] New day baseline equity=", g_day_equity_start); } } string Ccy1(const string sym) { if(StringLen(sym)>=3) return StringSubstr(sym,0,3); return ""; } string Ccy2(const string sym) { if(StringLen(sym)>=6) return StringSubstr(sym,3,3); return ""; } // clamp helper double Clamp(const double v, const double lo, const double hi) { return MathMax(lo, MathMin(hi, v)); } bool SymbolIsXAU(const string sym) { return (StringFind(sym, "XAU", 0) >= 0 || StringFind(sym, "GOLD", 0) >= 0); } //==================== Calendar & News ==================== double ImpactWeightWeighted(const int importance, const double wLow, const double wMed, const double wHigh) { // 0=low,1=medium,2=high in most terminals; fallback to medium otherwise if(importance<=0) return wLow; if(importance==1) return wMed; return wHigh; } double NewsFactorForCurrencyWeighted(const string ccy, const int beforeMin, const int afterMin, const double wLow, const double wMed, const double wHigh) { if(!InpUseNews || StringLen(ccy)==0) return 1.0; datetime now = TimeTradeServer(); datetime from = now - afterMin*60; datetime to = now + beforeMin*60; MqlCalendarValue vals[]; ArrayFree(vals); ResetLastError(); int n = CalendarValueHistory(vals, from, to, NULL, ccy); if(n<=0) { int err=GetLastError(); if(err!=0) PrintFormat("[MASTER] CalendarValueHistory returned %d (err=%d) for %s", n, err, ccy); return 1.0; // neutral if not available } double factor=1.0; for(int i=0;i=0 && minutes<=beforeMin) prox = (double)(beforeMin - minutes)/beforeMin; else if(minutes<0 && -minutes<=afterMin) prox = (double)(afterMin - (-minutes))/afterMin; else continue; double event_factor = 1.0 - (1.0 - w) * prox; // ramps from 1 -> w as prox->1 factor = MathMin(factor, event_factor); } return factor; } //==================== Volatility (ATR) ==================== bool CopyOne(const int handle, const int buffer, const int shift, double &out) { double tmp[]; int r=CopyBuffer(handle,buffer,shift,1,tmp); if(r!=1) return false; out=tmp[0]; return true; } double VolatilityFactorForSymbolParams(const string sym, const double volMinRiskFactor, const bool allowUpsize) { if(!InpUseVolatility) return 1.0; int hATR = iATR(sym, InpVolTF, InpATRPeriod); if(hATR==INVALID_HANDLE) return 1.0; double currATR; if(!CopyOne(hATR,0,1,currATR)) { IndicatorRelease(hATR); return 1.0; } int n = InpATRAvgLookback; double arr[]; int got = CopyBuffer(hATR,0,1,n,arr); IndicatorRelease(hATR); if(got<=10) return 1.0; double sum=0; int cnt=0; for(int i=0;i0){ sum+=arr[i]; cnt++; }} if(cnt==0) return 1.0; double avg = sum/cnt; double ratio = (avg>0 ? currATR/avg : 1.0); // higher ATR -> reduce risk; lower ATR -> optionally allow slight upsize double f = 1.0/MathSqrt(MathMax(0.0001, ratio)); if(!allowUpsize) f = MathMin(1.0, f); f = Clamp(f, volMinRiskFactor, InpMaxRiskFactor); return f; } //==================== Equity Regime ==================== double EquityFactor() { if(!InpUseEquity) return 1.0; if(g_day_equity_start<=0) return 1.0; double eq = AccountInfoDouble(ACCOUNT_EQUITY); double ddpct = (g_day_equity_start - eq)/g_day_equity_start * 100.0; if(ddpct >= InpDailyLossSoftPercent) return Clamp(InpEquityMinRiskFactor, InpMinRiskFactor, InpMaxRiskFactor); // optional upsize on strong day double profitpct = (eq - g_day_equity_start)/g_day_equity_start * 100.0; if(InpEquityAllowUpsize && profitpct>=1.0) return Clamp(InpDailyWinBoostCap, InpMinRiskFactor, InpMaxRiskFactor); return 1.0; } //==================== Compose & Publish ==================== void PublishForSymbol(const string sym) { string c1=Ccy1(sym), c2=Ccy2(sym); bool isXAU = SymbolIsXAU(sym); double rf=1.0; if(InpUseNews) { if(isXAU) { // Use USD-side news primarily for XAUUSD double nf_usd = NewsFactorForCurrencyWeighted("USD", InpXAU_NewsWindowBeforeMin, InpXAU_NewsWindowAfterMin, InpXAU_NewsWeightLow, InpXAU_NewsWeightMedium, InpXAU_NewsWeightHigh); rf = MathMin(rf, nf_usd); } else { double nf1 = NewsFactorForCurrencyWeighted(c1, InpNewsWindowBeforeMin, InpNewsWindowAfterMin, InpNewsWeightLow, InpNewsWeightMedium, InpNewsWeightHigh); double nf2 = NewsFactorForCurrencyWeighted(c2, InpNewsWindowBeforeMin, InpNewsWindowAfterMin, InpNewsWeightLow, InpNewsWeightMedium, InpNewsWeightHigh); rf = MathMin(rf, nf1); rf = MathMin(rf, nf2); } } double vf = VolatilityFactorForSymbolParams(sym, isXAU ? InpXAU_VolMinRiskFactor : InpVolMinRiskFactor, isXAU ? InpXAU_VolAllowUpsize : InpVolAllowUpsize); rf = MathMin(rf, vf); double ef = EquityFactor(); rf = MathMin(rf, ef); double floor_rf = isXAU ? InpXAU_MinRiskFactor : InpMinRiskFactor; rf = Clamp(rf, floor_rf, InpMaxRiskFactor); double sl_gain = isXAU ? InpXAU_SLMultGainOnCut : InpSLMultGainOnCut; double cd_gain = isXAU ? InpXAU_CooldownGainOnCut : InpCooldownGainOnCut; double sl_mult = 1.0 + sl_gain * (1.0 - rf); double cd_mult = 1.0 + cd_gain * (1.0 - rf); string base = InpGVNamespace + "/"; GlobalVariableSet(base+"risk_mult/"+sym, rf); GlobalVariableSet(base+"sl_mult/"+sym, sl_mult); GlobalVariableSet(base+"cooldown_mult/"+sym, cd_mult); GlobalVariableSet(base+"updated_ts", (double)TimeTradeServer()); // optional: debug PrintFormat("[MASTER] %s rf=%.2f sl_mult=%.2f cd_mult=%.2f (news/vol/equity)%s", sym, rf, sl_mult, cd_mult, isXAU?" [XAU]":""); } void UpdateAll() { DailyResetIfNeeded(); for(int i=0;i= InpUpdateSeconds) { UpdateAll(); last=now; } } //+------------------------------------------------------------------+