331 lignes
13 Kio
MQL5
331 lignes
13 Kio
MQL5
//+------------------------------------------------------------------+
| |||
//| EP_Demo.mq5 |
| |||
//| MMQ — Muhammad Minhas Qamar |
| |||
//| www.mql5.com/en/articles/24757 |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "MMQ — Muhammad Minhas Qamar"
| |||
#property link "https://www.mql5.com/en/articles/24757"
| |||
#property version "1.00"
| |||
#property strict
| |||
#property script_show_inputs
| |||
#property description "Builds one basket's joint history, compares the probability vectors"
| |||
#property description "a risk number can stand on, conditions on today's volatility, adds"
| |||
#property description "three views, and reports what each step does to the book's risk."
| |||
| |||
#include <EntropyPooling\Scenarios.mqh>
| |||
#include <EntropyPooling\Probabilities.mqh>
| |||
#include <EntropyPooling\MinRelEntropy.mqh>
| |||
#include <EntropyPooling\Views.mqh>
| |||
#include <EntropyPooling\Posterior.mqh>
| |||
| |||
input group "Data"
| |||
input string InpSymbols = "EURUSD,GBPUSD,AUDUSD,USDJPY,USDCHF"; // basket
| |||
input string InpWeights = "0.2,0.2,0.2,-0.2,-0.2"; // book notional per symbol, same order
| |||
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_D1; // bar size of one scenario
| |||
input int InpBars = 5000; // bars requested per symbol
| |||
input datetime InpAsOf = 0; // replay as of this date, 0 for today
| |||
input group "Prior"
| |||
input int InpWindow = 250; // rolling window it is compared with
| |||
input double InpHalfLife = 250.0; // exponential decay half-life, bars
| |||
input group "State"
| |||
input int InpStateWindow = 20; // trailing volatility of the book, bars
| |||
input double InpMinEns = 250; // fewest effective scenarios conditioning may leave
| |||
input double InpKernelBand = 0.5; // kernel bandwidth, share of the state's std
| |||
input group "Views"
| |||
input string InpVolSymbol = "USDJPY"; // volatility view on
| |||
input double InpVolScale = 1.5; // times its conditioned volatility
| |||
input string InpTailSymbol = "USDCHF"; // tail view on
| |||
input double InpTailMove = -0.02; // daily log return at or below
| |||
input double InpTailProb = 0.01; // has at least this probability
| |||
input string InpCorrA = "EURUSD"; // correlation view between
| |||
input string InpCorrB = "GBPUSD"; // and
| |||
input double InpCorr = 0.40; // at most this correlation
| |||
input bool InpCorrPinVol = true; // hold both vols, so only the correlation moves
| |||
input double InpConfidence = 1.0; // weight on the views, 0..1
| |||
input group "Risk"
| |||
input double InpLevel = 0.025; // tail level for VaR and ES
| |||
| |||
//--- one probability vector and its label
| |||
struct SStage
| |||
{
| |||
string name;
| |||
string code; // short label for wide tables
| |||
vector p;
| |||
};
| |||
| |||
SStage g_stage[];
| |||
| |||
//+------------------------------------------------------------------+
| |||
void AddStage(const string name,const string code,const vector &p)
| |||
{
| |||
const int n=ArraySize(g_stage);
| |||
ArrayResize(g_stage,n+1);
| |||
g_stage[n].name=name;
| |||
g_stage[n].code=code;
| |||
g_stage[n].p =p;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Split a comma list, trimming spaces. |
| |||
//+------------------------------------------------------------------+
| |||
int SplitList(const string text,string &out[])
| |||
{
| |||
const int n=StringSplit(text,',',out);
| |||
for(int i=0;i<n;i++)
| |||
{
| |||
StringTrimLeft(out[i]);
| |||
StringTrimRight(out[i]);
| |||
}
| |||
return n;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| History may still be downloading when a script starts. |
| |||
//+------------------------------------------------------------------+
| |||
bool LoadScenarios(CEpScenarios &scen,const string &symbols[])
| |||
{
| |||
for(int attempt=0;attempt<40;attempt++)
| |||
{
| |||
bool ready=true;
| |||
for(int i=0;i<ArraySize(symbols);i++)
| |||
{
| |||
SymbolSelect(symbols[i],true);
| |||
if(!SeriesInfoInteger(symbols[i],InpTimeframe,SERIES_SYNCHRONIZED))
| |||
ready=false;
| |||
}
| |||
if(ready && scen.LoadReturns(symbols,InpTimeframe,InpBars))
| |||
return true;
| |||
Sleep(500);
| |||
}
| |||
return scen.LoadReturns(symbols,InpTimeframe,InpBars);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Per stage: ENS, KL to the decay prior, and the book's risk. |
| |||
//+------------------------------------------------------------------+
| |||
void ReportStages(const vector &book,const int decay)
| |||
{
| |||
const double ann=MathSqrt(252.0);
| |||
Print("--- the book under each probability vector ---------------------");
| |||
PrintFormat(" %-24s %7s %8s %9s %9s %9s %8s","vector","ENS","KL","vol ann","VaR","ES","size");
| |||
double es_ref=0.0;
| |||
for(int s=0;s<ArraySize(g_stage);s++)
| |||
{
| |||
SEpMoments m;
| |||
EpDescribe(book,g_stage[s].p,InpLevel,m);
| |||
if(s==1)
| |||
es_ref=m.es;
| |||
const double kl=EpRelativeEntropy(g_stage[s].p,g_stage[decay].p);
| |||
PrintFormat(" %-24s %7.0f %8s %8.2f%% %8.3f%% %8.3f%% %8s",g_stage[s].name,
| |||
EpEffectiveScenarios(g_stage[s].p),
| |||
(kl==EMPTY_VALUE ? "inf" : StringFormat("%.4f",kl)),100.0*m.vol*ann,
| |||
100.0*m.var,100.0*m.es,(s>=1 && m.es<0.0 ? StringFormat("%.2fx",es_ref/m.es) : ""));
| |||
}
| |||
PrintFormat(" size: position multiple that keeps the %.1f%% ES of the rolling window",
| |||
100.0*InpLevel);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
void ReportSymbols(const CEpScenarios &scen,const int n)
| |||
{
| |||
const double ann=MathSqrt(252.0);
| |||
Print("--- annualised volatility per symbol ----------------------------");
| |||
string head=StringFormat(" %-24s",""),line;
| |||
for(int k=0;k<n;k++)
| |||
head+=StringFormat(" %8s",scen.Name(k));
| |||
Print(head);
| |||
vector x;
| |||
for(int s=0;s<ArraySize(g_stage);s++)
| |||
{
| |||
line=StringFormat(" %-24s",g_stage[s].name);
| |||
for(int k=0;k<n;k++)
| |||
{
| |||
scen.Column(k,x);
| |||
line+=StringFormat(" %7.2f%%",100.0*ann*EpVolatility(x,g_stage[s].p));
| |||
}
| |||
Print(line);
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| The book's worst days and each vector's weight on them, times T. |
| |||
//+------------------------------------------------------------------+
| |||
void ReportWorstDays(const CEpScenarios &scen,const vector &book,const int count)
| |||
{
| |||
const int T=scen.Rows();
| |||
double a[][2];
| |||
ArrayResize(a,T);
| |||
for(int t=0;t<T;t++)
| |||
{
| |||
a[t][0]=book[t];
| |||
a[t][1]=t;
| |||
}
| |||
ArraySort(a);
| |||
Print("--- the book's worst days: weight as a multiple of 1/T ----------");
| |||
string head=StringFormat(" %-10s %8s","date","return");
| |||
for(int s=0;s<ArraySize(g_stage);s++)
| |||
head+=StringFormat(" %8s",g_stage[s].code);
| |||
Print(head);
| |||
for(int i=0;i<count && i<T;i++)
| |||
{
| |||
const int t=(int)a[i][1];
| |||
string line=StringFormat(" %-10s %7.2f%%",TimeToString(scen.Time(t),TIME_DATE),100.0*a[i][0]);
| |||
for(int s=0;s<ArraySize(g_stage);s++)
| |||
line+=StringFormat(" %8.3f",g_stage[s].p[t]*T);
| |||
Print(line);
| |||
}
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
void ReportSolve(const string what,const SEpSolve &r,const CEpViews &v)
| |||
{
| |||
PrintFormat(" %-24s %s: %d pass(es), %d Newton steps, worst miss %.1e, KL %.4f, ENS %.0f, %u ms",
| |||
what,EnumToString(r.status),v.Passes(),r.iterations,r.violation,r.entropy,r.ens,r.ms);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
void OnStart(void)
| |||
{
| |||
string symbols[],wtext[];
| |||
const int n=SplitList(InpSymbols,symbols);
| |||
if(n<2 || SplitList(InpWeights,wtext)!=n)
| |||
{
| |||
Print("EP_Demo: give at least two symbols and one weight per symbol");
| |||
return;
| |||
}
| |||
int cols[];
| |||
double weights[];
| |||
ArrayResize(cols,n);
| |||
ArrayResize(weights,n);
| |||
for(int i=0;i<n;i++)
| |||
{
| |||
cols[i] =i;
| |||
weights[i]=StringToDouble(wtext[i]);
| |||
}
| |||
| |||
Print("================================================================");
| |||
PrintFormat("Entropy pooling on %s, %s%s",InpSymbols,EnumToString(InpTimeframe),
| |||
(InpAsOf>0 ? ", as of "+TimeToString(InpAsOf,TIME_DATE) : ""));
| |||
Print("================================================================");
| |||
| |||
CEpScenarios scen;
| |||
if(!LoadScenarios(scen,symbols))
| |||
return;
| |||
if(InpAsOf>0 && !scen.KeepUntil(InpAsOf))
| |||
{
| |||
Print("EP_Demo: too little history before the replay date");
| |||
return;
| |||
}
| |||
| |||
//--- the book as a column, then its trailing volatility as the state
| |||
vector book;
| |||
EpPortfolio(scen,cols,weights,book);
| |||
double values[];
| |||
ArrayResize(values,scen.Rows());
| |||
for(int t=0;t<scen.Rows();t++)
| |||
values[t]=book[t];
| |||
const int book_col =scen.AddColumn("Book",EP_COLUMN_RETURN,values,EMPTY_VALUE);
| |||
const int state_col=scen.AddTrailingVolatility(book_col,InpStateWindow);
| |||
if(state_col<0)
| |||
return;
| |||
scen.Column(book_col,book);
| |||
const int T=scen.Rows();
| |||
| |||
vector z;
| |||
scen.Column(state_col,z);
| |||
const double z_today=scen.Next(state_col);
| |||
PrintFormat(" scenarios : %d joint rows, %s to %s",T,
| |||
TimeToString(scen.Time(0),TIME_DATE),TimeToString(scen.Time(T-1),TIME_DATE));
| |||
PrintFormat(" book : %s",InpWeights);
| |||
PrintFormat(" state : book's %d-bar volatility, today %.3f%% (ann. %.2f%%)",
| |||
InpStateWindow,100.0*z_today,100.0*z_today*MathSqrt(252.0));
| |||
| |||
//--- priors: what a lookback silently assumes, and two explicit choices
| |||
vector p_full,p_roll,p_decay,p_kern;
| |||
EpUniform(T,p_full);
| |||
EpRollingWindow(T,InpWindow,p_roll);
| |||
EpExponentialDecay(T,InpHalfLife,p_decay);
| |||
AddStage("full history","full",p_full);
| |||
AddStage(StringFormat("rolling %d",InpWindow),"rolling",p_roll);
| |||
AddStage(StringFormat("decay hl %.0f",InpHalfLife),"decay",p_decay);
| |||
const int decay=2;
| |||
| |||
const double z_sd=EpVolatility(z,p_decay);
| |||
EpKernel(z,z_today,InpKernelBand*z_sd,p_decay,p_kern);
| |||
AddStage("decay x kernel","kernel",p_kern);
| |||
| |||
CEpSolver solver;
| |||
Print("--- solves -----------------------------------------------------");
| |||
| |||
//--- condition on the state: tilt the decay prior to today's level, within budget
| |||
vector p_state;
| |||
SEpSolve rs;
| |||
const double z_now=EpConditionState(scen,p_decay,state_col,z_today,InpMinEns,p_state,rs);
| |||
PrintFormat(" %-24s %s: %d Newton steps, KL %.4f, ENS %.0f",
| |||
"state conditioning",EnumToString(rs.status),rs.iterations,rs.entropy,rs.ens);
| |||
if(z_now!=z_today)
| |||
PrintFormat(" %-24s today's state leaves under %.0f scenarios; target pulled to %.3f%%",
| |||
"",InpMinEns,100.0*z_now);
| |||
AddStage("decay | state","state",p_state);
| |||
| |||
//--- the views, stated against the conditioned distribution
| |||
const int vol_col =scen.Find(InpVolSymbol);
| |||
const int tail_col=scen.Find(InpTailSymbol);
| |||
const int ca=scen.Find(InpCorrA),cb=scen.Find(InpCorrB);
| |||
if(vol_col<0 || tail_col<0 || ca<0 || cb<0)
| |||
{
| |||
Print("EP_Demo: every view symbol must be in the basket");
| |||
return;
| |||
}
| |||
vector xv;
| |||
scen.Column(vol_col,xv);
| |||
const double vol_target=InpVolScale*EpVolatility(xv,p_state);
| |||
| |||
CEpViews views;
| |||
views.AddMean(state_col,EP_EQUAL,z_now);
| |||
const int v_vol =views.AddVolatility(vol_col,EP_EQUAL,vol_target);
| |||
const int v_tail=views.AddTail(tail_col,InpTailMove,EP_AT_LEAST,InpTailProb);
| |||
const int v_corr=views.AddCorrelation(ca,cb,EP_AT_MOST,InpCorr);
| |||
if(InpCorrPinVol)
| |||
{
| |||
vector xa,xb;
| |||
scen.Column(ca,xa);
| |||
scen.Column(cb,xb);
| |||
if(ca!=vol_col)
| |||
views.AddVolatility(ca,EP_EQUAL,EpVolatility(xa,p_state));
| |||
if(cb!=vol_col)
| |||
views.AddVolatility(cb,EP_EQUAL,EpVolatility(xb,p_state));
| |||
}
| |||
if(v_vol<0 || v_tail<0 || v_corr<0)
| |||
{
| |||
Print("EP_Demo: a view could not be added, check its inputs");
| |||
return;
| |||
}
| |||
vector p_post;
| |||
SEpSolve rv;
| |||
const bool pooled=views.Pool(scen,p_decay,solver,p_post,rv);
| |||
ReportSolve("state and views",rv,views);
| |||
if(!pooled)
| |||
return;
| |||
AddStage("decay | state + views","views",p_post);
| |||
if(InpConfidence<1.0)
| |||
{
| |||
vector p_mix;
| |||
EpBlend(p_state,p_post,InpConfidence,p_mix);
| |||
AddStage(StringFormat("blend c=%.2f",InpConfidence),"blend",p_mix);
| |||
}
| |||
| |||
Print("--- views -------------------------------------------------------");
| |||
PrintFormat(" %-34s %12s %12s %12s %12s","view","before","after","multiplier","nats/unit");
| |||
for(int k=0;k<views.Total();k++)
| |||
PrintFormat(" %-34s %12.6g %12.6g %12.4f %12.4g",views.Describe(k,scen),
| |||
views.Achieved(k,scen,p_state),views.Achieved(k,scen,p_post),
| |||
solver.Multiplier(k),solver.MarginalCost(k));
| |||
Print(" before = conditioned on the state only; multiplier 0 = the view was already true");
| |||
| |||
ReportStages(book,decay);
| |||
ReportSymbols(scen,n);
| |||
ReportWorstDays(scen,book,8);
| |||
Print("================================================================");
| |||
}
| |||
//+------------------------------------------------------------------+
|