vitals-account-monitor/mql5/Include/Vitals/VtExposure.mqh
Allan Niñal Allan Niñal d1de9e6dcd Vitals: free MT5 and MT4 account card source, Python reference, tests and README
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-21 23:54:12 +08:00
93 lines
3.5 KiB
MQL5
//+------------------------------------------------------------------+
//| VtExposure.mqh |
//| Copyright 2026, Allan Niñal |
//| https://www.allanninal.dev/mql5 |
//+------------------------------------------------------------------+
//| Open positions: long / short / total, risk at the stops, the |
//| positions without a stop, and the net exposure per currency in |
//| account money. Pure aggregation of numbers the live layer |
//| (VtLive.mqh) read from the terminal: the same function runs in |
//| the tests (tests/reference.py twins it). |
//| Exposure of a position = +/- its notional (account money): |
//| long = + base currency, - profit currency. When base == |
//| profit currency (an index CFD) the symbol is the "currency". |
//+------------------------------------------------------------------+
// SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0 (see LICENSE)
#ifndef VTEXPOSURE_MQH
#define VTEXPOSURE_MQH
#include "VtCore.mqh"
struct VtPos
{
long magic;
string symbol,comment;
int dir; // 0 long, 1 short
double volume,profit,pips,sl,risk,value; // profit incl. swap; risk = OrderCalcProfit at the stop (<=0 is a loss)
string base,ccy; // base and profit currency
};
void VtSummarize(CVtEngine &e,const VtPos &pos[],const int n,const double balance)
{
for(int k=0; k<3; k++)
{ e.o_n[k]=0; e.o_lots[k]=0; e.o_pl[k]=0; e.o_pips[k]=0; }
e.o_risk=0; e.o_nostop=0; e.o_nostop_first=""; e.o_nex=0;
ArrayResize(e.ex_name,0,8);
ArrayResize(e.ex_net,0,8);
for(int i=0; i<n; i++)
{
if(!e.PassPos(pos[i].symbol,pos[i].magic,pos[i].comment))
continue;
for(int k=0; k<3; k++)
if(k==2 || k==pos[i].dir)
{
e.o_n[k]++;
e.o_lots[k]+=pos[i].volume;
e.o_pl[k]+=pos[i].profit;
e.o_pips[k]+=pos[i].pips;
}
if(pos[i].sl>0)
e.o_risk+=pos[i].risk;
else
{
e.o_nostop++;
if(e.o_nostop_first=="")
e.o_nostop_first=StringFormat("%s %s %.2f",pos[i].symbol,pos[i].dir==0 ? "long" : "short",pos[i].volume);
}
double s=(pos[i].dir==0 ? 1.0 : -1.0);
string key[2];
double val[2];
int nk=0;
if(pos[i].base==pos[i].ccy)
{ key[0]=pos[i].symbol; val[0]=s*pos[i].value; nk=1; }
else
{ key[0]=pos[i].base; val[0]=s*pos[i].value; key[1]=pos[i].ccy; val[1]=-s*pos[i].value; nk=2; }
for(int q=0; q<nk; q++)
{
int f=-1;
for(int j=0; j<e.o_nex; j++)
if(e.ex_name[j]==key[q])
{ f=j; break; }
if(f<0)
{
f=e.o_nex++;
ArrayResize(e.ex_name,e.o_nex,8);
ArrayResize(e.ex_net,e.o_nex,8);
e.ex_name[f]=key[q]; e.ex_net[f]=0;
}
e.ex_net[f]+=val[q];
}
}
e.o_risk_pct=(balance>0 ? 100.0*e.o_risk/balance : 0.0);
for(int i=1; i<e.o_nex; i++) // |net| descending, then name
{
string nm=e.ex_name[i];
double v=e.ex_net[i];
int j=i-1;
while(j>=0 && (MathAbs(e.ex_net[j])<MathAbs(v) ||
(MathAbs(e.ex_net[j])==MathAbs(v) && StringCompare(e.ex_name[j],nm)>0)))
{ e.ex_name[j+1]=e.ex_name[j]; e.ex_net[j+1]=e.ex_net[j]; j--; }
e.ex_name[j+1]=nm; e.ex_net[j+1]=v;
}
}
#endif