284 lines
No EOL
28 KiB
MQL5
284 lines
No EOL
28 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Portfolio.mq5 |
|
|
//| Copyright 2022, Allan |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#define SWAP(A, B) { A += B; B = A - B; A -= B; }
|
|
#define SORT(a,b,c) {if(a > b) SWAP(a,b) if(a > c) SWAP(a,c) if (b>c) SWAP(b,c) }
|
|
//+------------------------------------------------------------------+
|
|
//| Estruturas |
|
|
//+------------------------------------------------------------------+
|
|
enum CalcMode
|
|
{
|
|
VWAP,//Session Average Weighted
|
|
Last,//Last deal quote
|
|
AskBidWatch,//Watch(Ask+Bid)/2
|
|
Median//Mediana
|
|
};
|
|
struct stcDeal
|
|
{
|
|
long lPositionID;
|
|
string strSymbol;
|
|
ENUM_DEAL_TYPE enType;
|
|
ulong dtTime;
|
|
double dbVolume;
|
|
double dbPrice;
|
|
/*------------------------------
|
|
long lTicket;
|
|
long lOrder;
|
|
long lTime_msc;
|
|
ENUM_DEAL_ENTRY enEntry;
|
|
long lMagic;
|
|
ENUM_DEAL_REASON enReason;
|
|
double dbComission;
|
|
double dbSwap;
|
|
double dbProfit;
|
|
double dbFee;
|
|
double dbSL;
|
|
double dbTP;
|
|
string strComment;
|
|
---------------------------------*/
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Entradas |
|
|
//+------------------------------------------------------------------+
|
|
sinput CalcMode inpCalcMode=Median;//Modo de Cálculo:
|
|
sinput ulong inpRatesQuantidade=52;//Quantidade de dias:
|
|
sinput double inpFinancialTicket=500.0;//Ficha financeira (brl):
|
|
sinput double inpBalanceCorrection=0.0;//Correção do Patrimônio (brl):
|
|
//+------------------------------------------------------------------+
|
|
//| Variaveis Globais |
|
|
//+------------------------------------------------------------------+
|
|
double gdbBalance=inpBalanceCorrection;
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
EventSetTimer(1);
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
EventKillTimer();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Timer function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTimer()
|
|
{
|
|
//gdbBalance=AccountInfoDouble(ACCOUNT_BALANCE)+inpBalanceCorrection;
|
|
//double dbCorrectionBalance=AccountInfoDouble(ACCOUNT_BALANCE)-inpBalanceCorrection;
|
|
//gdbBalance=AccountInfoDouble(ACCOUNT_BALANCE)+dbCorrectionBalance;
|
|
stcDeal arrLostDeals[];
|
|
/*
|
|
ArrayResize(arrLostDeals,11);
|
|
arrLostDeals[0].strSymbol="GOAU4F";
|
|
arrLostDeals[0].dtTime=D'2022.03.02 13:09:11';
|
|
arrLostDeals[0].enType=DEAL_TYPE_SELL;
|
|
arrLostDeals[0].dbVolume=9.0;
|
|
arrLostDeals[0].dbPrice=10.77;
|
|
arrLostDeals[1].strSymbol="GOAU4F";
|
|
arrLostDeals[1].dtTime=D'2022.03.03 00:00:00';
|
|
arrLostDeals[1].enType=DEAL_TYPE_SELL;
|
|
arrLostDeals[1].dbVolume=9.0;
|
|
arrLostDeals[1].dbPrice=11.29;
|
|
arrLostDeals[2].strSymbol="B3SA3F";
|
|
arrLostDeals[2].dtTime=D'2022.03.03 00:00:00';
|
|
arrLostDeals[2].enType=DEAL_TYPE_SELL;
|
|
arrLostDeals[2].dbVolume=9.0;
|
|
arrLostDeals[2].dbPrice=15.19;
|
|
arrLostDeals[3].strSymbol="B3SA3F";
|
|
arrLostDeals[3].dtTime=D'2022.03.03 00:00:01';
|
|
arrLostDeals[3].enType=DEAL_TYPE_BUY;
|
|
arrLostDeals[3].dbVolume=9.0;
|
|
arrLostDeals[3].dbPrice=14.73;
|
|
arrLostDeals[4].strSymbol="IRBR3F";
|
|
arrLostDeals[4].dtTime=D'2022.03.03 00:00:00';
|
|
arrLostDeals[4].enType=DEAL_TYPE_SELL;
|
|
arrLostDeals[4].dbVolume=30.0;
|
|
arrLostDeals[4].dbPrice=3.22;
|
|
arrLostDeals[5].strSymbol="ENJU3F";
|
|
arrLostDeals[5].dtTime=D'2022.03.03 14:54:40';
|
|
arrLostDeals[5].enType=DEAL_TYPE_BUY;
|
|
arrLostDeals[5].dbVolume=35.0;
|
|
arrLostDeals[5].dbPrice=2.9;
|
|
arrLostDeals[6].strSymbol="HASH11";
|
|
arrLostDeals[6].dtTime=D'2022.03.02 13:25:33';
|
|
arrLostDeals[6].enType=DEAL_TYPE_SELL;
|
|
arrLostDeals[6].dbVolume=3.0;
|
|
arrLostDeals[6].dbPrice=40.0;
|
|
arrLostDeals[7].strSymbol="HASH11";
|
|
arrLostDeals[7].dtTime=D'2022.03.02 13:25:33';
|
|
arrLostDeals[7].enType=DEAL_TYPE_SELL;
|
|
arrLostDeals[7].dbVolume=3.0;
|
|
arrLostDeals[7].dbPrice=40.0;
|
|
arrLostDeals[8].strSymbol="HASH11";
|
|
arrLostDeals[8].dtTime=D'2022.03.03 16:26:05';
|
|
arrLostDeals[8].enType=DEAL_TYPE_BUY;
|
|
arrLostDeals[8].dbVolume=3.0;
|
|
arrLostDeals[8].dbPrice=37.25;
|
|
arrLostDeals[9].strSymbol="CMIN3F";
|
|
arrLostDeals[9].dtTime=D'2022.03.02 13:01:13';
|
|
arrLostDeals[9].enType=DEAL_TYPE_SELL;
|
|
arrLostDeals[9].dbVolume=15.0;
|
|
arrLostDeals[9].dbPrice=6.38;
|
|
arrLostDeals[10].strSymbol="CMIN3F";
|
|
arrLostDeals[10].dtTime=D'2022.03.03 13:01:13';
|
|
arrLostDeals[10].enType=DEAL_TYPE_SELL;
|
|
arrLostDeals[10].dbVolume=15.0;
|
|
arrLostDeals[10].dbPrice=6.69;
|
|
*/
|
|
string strComment="";
|
|
double dbExposure=0.0;
|
|
ulong ulPositionTicket=0;
|
|
for(int i=0;i<PositionsTotal();i++)
|
|
{
|
|
ulPositionTicket=PositionGetTicket(i);
|
|
dbExposure+=PositionGetDouble(POSITION_PRICE_OPEN)*PositionGetDouble(POSITION_VOLUME);
|
|
}
|
|
MqlRates arrMqlRatesPositions[];
|
|
double dbPortfolioReturn=0.0;
|
|
double dbPortfolioRisk=0.0;
|
|
ulPositionTicket=0;
|
|
for(int i=0;i<PositionsTotal();i++)
|
|
{
|
|
ulPositionTicket=PositionGetTicket(i);
|
|
CopyRates(PositionGetSymbol(i),PERIOD_D1,0,(int)inpRatesQuantidade,arrMqlRatesPositions);
|
|
double dbRetornoMedia=0.0;
|
|
for(int j=0;j<ArraySize(arrMqlRatesPositions);j++) if(j>0) dbRetornoMedia+=(arrMqlRatesPositions[j].close-arrMqlRatesPositions[j-1].close)/(2/(1/(arrMqlRatesPositions[j].close)+(1/arrMqlRatesPositions[j-1].close)));
|
|
dbRetornoMedia=dbRetornoMedia/(ArraySize(arrMqlRatesPositions)-1);
|
|
dbPortfolioReturn+=dbRetornoMedia*PositionGetDouble(POSITION_VOLUME)*PositionGetDouble(POSITION_PRICE_OPEN)/gdbBalance;
|
|
double dbRetornoVariancia=0.0;
|
|
for(int j=0;j<ArraySize(arrMqlRatesPositions);j++) if(j>0) dbRetornoVariancia+=MathPow(((arrMqlRatesPositions[j].close-arrMqlRatesPositions[j-1].close)/(2/(1/arrMqlRatesPositions[j].close+1/arrMqlRatesPositions[j-1].close)-dbRetornoMedia)),2);
|
|
dbRetornoVariancia=dbRetornoVariancia/(ArraySize(arrMqlRatesPositions)-1);
|
|
dbPortfolioRisk+=(dbRetornoVariancia)*MathPow(PositionGetDouble(POSITION_VOLUME)*PositionGetDouble(POSITION_PRICE_OPEN)/gdbBalance,2);
|
|
}
|
|
MqlRates arrMqlRatesSymbol[];
|
|
CopyRates(_Symbol,PERIOD_D1,0,(int)inpRatesQuantidade,arrMqlRatesSymbol);
|
|
double dbSymbol_r=0.0;
|
|
for(int i=0;i<ArraySize(arrMqlRatesSymbol);i++)if(i>0)dbSymbol_r+=(arrMqlRatesSymbol[i].close-arrMqlRatesSymbol[i-1].close)/(2/(1/(arrMqlRatesSymbol[i].close)+(1/arrMqlRatesSymbol[i-1].close)));
|
|
dbSymbol_r=dbSymbol_r/(ArraySize(arrMqlRatesSymbol)-1);
|
|
double dbSymbol_s=0.0;
|
|
for(int i=0;i<ArraySize(arrMqlRatesSymbol);i++)if(i>0)dbSymbol_s+=MathPow(((arrMqlRatesSymbol[i].close-arrMqlRatesSymbol[i-1].close)/(2/(1/arrMqlRatesSymbol[i].close+1/arrMqlRatesSymbol[i-1].close)-dbSymbol_r)),2);
|
|
dbSymbol_s=dbSymbol_s/(ArraySize(arrMqlRatesSymbol)-1);
|
|
strComment+="\n";
|
|
strComment+=StringFormat("Balance: %.2f, r=%.2f%%, s=%.2f%%, ld=%.2f%%\n%7s: r=%.2f%%, s=%.2f%%, cv=%.5f, vol=%.0f, w=%.2f%%\n",
|
|
gdbBalance,
|
|
100*dbPortfolioReturn,
|
|
100*MathSqrt(dbPortfolioRisk),
|
|
100*dbExposure/gdbBalance,
|
|
_Symbol,
|
|
100*dbSymbol_r,
|
|
100*MathSqrt(dbSymbol_s),
|
|
MathSqrt(dbSymbol_s)/dbSymbol_r,
|
|
MathRound(inpFinancialTicket/SymbolPrice(_Symbol,inpCalcMode)),
|
|
100*(MathRound(inpFinancialTicket/SymbolPrice(_Symbol,inpCalcMode))*SymbolPrice(_Symbol,inpCalcMode))/gdbBalance
|
|
);
|
|
strComment+="\n";
|
|
stcDeal lastDeal;
|
|
ulong ulHistoryDealTicket=0;
|
|
HistorySelect(0,TimeCurrent());
|
|
for(int i=0;i<SymbolsTotal(true);i++)
|
|
{
|
|
if(SymbolInfoDouble(SymbolName(i,true),SYMBOL_OPTION_STRIKE)==0.0)
|
|
{
|
|
ZeroMemory(lastDeal);
|
|
for(int h=0;h<HistoryDealsTotal();h++)
|
|
{
|
|
ulHistoryDealTicket=HistoryDealGetTicket(h);
|
|
if(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL)==SymbolName(i,true))
|
|
if(HistoryDealGetInteger(ulHistoryDealTicket,DEAL_TIME)>(long)lastDeal.dtTime)
|
|
{
|
|
lastDeal.strSymbol=HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL);
|
|
lastDeal.enType=(ENUM_DEAL_TYPE)HistoryDealGetInteger(ulHistoryDealTicket,DEAL_TYPE);
|
|
lastDeal.dtTime=HistoryDealGetInteger(ulHistoryDealTicket,DEAL_TIME);
|
|
lastDeal.dbPrice=HistoryDealGetDouble(ulHistoryDealTicket,DEAL_PRICE);
|
|
lastDeal.dbVolume=HistoryDealGetDouble(ulHistoryDealTicket,DEAL_VOLUME);
|
|
lastDeal.lPositionID=HistoryDealGetInteger(ulHistoryDealTicket,DEAL_POSITION_ID);
|
|
}
|
|
}
|
|
if(lastDeal.dbPrice>0.0)
|
|
{
|
|
stcDeal dealSimulation; ZeroMemory(dealSimulation);
|
|
dealSimulation.strSymbol=lastDeal.strSymbol;
|
|
dealSimulation.dbPrice=SymbolPrice(dealSimulation.strSymbol,inpCalcMode);
|
|
dealSimulation.dbVolume=MathRound(inpFinancialTicket/SymbolPrice(lastDeal.strSymbol,inpCalcMode));
|
|
if(lastDeal.enType==DEAL_TYPE_BUY)if(SymbolPrice(dealSimulation.strSymbol,inpCalcMode)>lastDeal.dbPrice){dealSimulation.enType=(ENUM_DEAL_TYPE)DEAL_TYPE_SELL;ObjectCreate(0,"Stocks_SimulationSell",OBJ_HLINE,0,0,lastDeal.dbPrice/(1-(lastDeal.dbPrice*dealSimulation.dbVolume)/gdbBalance));ObjectSetInteger(0,"Stocks_SimulationSell",OBJPROP_STYLE,STYLE_DASHDOT);ObjectSetInteger(0,"Stocks_SimulationSell",OBJPROP_COLOR,clrFireBrick);}
|
|
if(lastDeal.enType==DEAL_TYPE_BUY)if(SymbolPrice(dealSimulation.strSymbol,inpCalcMode)<=lastDeal.dbPrice){dealSimulation.enType=(ENUM_DEAL_TYPE)DEAL_TYPE_BUY;}
|
|
if(lastDeal.enType==DEAL_TYPE_SELL)if(SymbolPrice(dealSimulation.strSymbol,inpCalcMode)>lastDeal.dbPrice){dealSimulation.enType=(ENUM_DEAL_TYPE)DEAL_TYPE_SELL;ObjectCreate(0,"Stocks_SimulationSell",OBJ_HLINE,0,0,lastDeal.dbPrice/(1-(lastDeal.dbPrice*dealSimulation.dbVolume)/gdbBalance));ObjectSetInteger(0,"Stocks_SimulationSell",OBJPROP_STYLE,STYLE_DASHDOT);ObjectSetInteger(0,"Stocks_SimulationSell",OBJPROP_COLOR,clrFireBrick);}
|
|
if(lastDeal.enType==DEAL_TYPE_SELL)if(SymbolPrice(dealSimulation.strSymbol,inpCalcMode)<=lastDeal.dbPrice){dealSimulation.enType=(ENUM_DEAL_TYPE)DEAL_TYPE_BUY;ObjectCreate(0,"Stocks_SimulationBuy",OBJ_HLINE,0,0,lastDeal.dbPrice*(1-(lastDeal.dbPrice*dealSimulation.dbVolume)/gdbBalance));ObjectSetInteger(0,"Stocks_SimulationBuy",OBJPROP_STYLE,STYLE_DASHDOT);ObjectSetInteger(0,"Stocks_SimulationBuy",OBJPROP_COLOR,clrMidnightBlue);}
|
|
if(((dealSimulation.enType==DEAL_TYPE_BUY?(lastDeal.dbPrice-dealSimulation.dbPrice)/(2/(1/dealSimulation.dbPrice+1/lastDeal.dbPrice)):(dealSimulation.dbPrice-lastDeal.dbPrice)/(2/(1/dealSimulation.dbPrice+1/lastDeal.dbPrice))) )>((MathRound(inpFinancialTicket/SymbolPrice(lastDeal.strSymbol,inpCalcMode))*SymbolPrice(lastDeal.strSymbol,inpCalcMode))/gdbBalance) )
|
|
{
|
|
if(dealSimulation.enType==DEAL_TYPE_SELL)
|
|
{
|
|
double dbExposureVolume=0.0;
|
|
ulPositionTicket=0;
|
|
for(int p=0;p<PositionsTotal();p++)
|
|
{
|
|
ulPositionTicket=PositionGetTicket(p);
|
|
if(PositionGetSymbol(p)==lastDeal.strSymbol)
|
|
{
|
|
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)dbExposureVolume+=PositionGetDouble(POSITION_VOLUME);
|
|
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)dbExposureVolume-=PositionGetDouble(POSITION_VOLUME);
|
|
}
|
|
}
|
|
if(dbExposureVolume>0)
|
|
strComment+=StringFormat("%s %s %5.2f%%\n",
|
|
StringSubstr(EnumToString((ENUM_DEAL_TYPE)dealSimulation.enType),10,5),
|
|
dealSimulation.strSymbol,
|
|
100*((dealSimulation.enType==DEAL_TYPE_BUY?(lastDeal.dbPrice-dealSimulation.dbPrice)/(2/(1/dealSimulation.dbPrice+1/lastDeal.dbPrice)):(dealSimulation.dbPrice-lastDeal.dbPrice)/(2/(1/dealSimulation.dbPrice+1/lastDeal.dbPrice))))
|
|
);
|
|
}
|
|
else
|
|
{
|
|
strComment+=StringFormat("%s %s %5.2f%%\n",
|
|
StringSubstr(EnumToString((ENUM_DEAL_TYPE)dealSimulation.enType),10,5),
|
|
dealSimulation.strSymbol,
|
|
100*((dealSimulation.enType==DEAL_TYPE_BUY?(lastDeal.dbPrice-dealSimulation.dbPrice)/(2/(1/dealSimulation.dbPrice+1/lastDeal.dbPrice)):(dealSimulation.dbPrice-lastDeal.dbPrice)/(2/(1/dealSimulation.dbPrice+1/lastDeal.dbPrice))))
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Comment(strComment);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Ativo subjacente da opção |
|
|
//+------------------------------------------------------------------+
|
|
double SymbolPrice(const string SYMBOL, const CalcMode CALCMODE)
|
|
{
|
|
double result = 0.0;
|
|
switch(CALCMODE)
|
|
{
|
|
case Last:
|
|
return(SymbolInfoDouble(SYMBOL,SYMBOL_LAST));
|
|
break;
|
|
case VWAP:
|
|
return(SymbolInfoDouble(SYMBOL,SYMBOL_SESSION_AW));
|
|
break;
|
|
case Median:
|
|
return(TrueMedian(SymbolInfoDouble(SYMBOL,SYMBOL_BID),SymbolInfoDouble(SYMBOL,SYMBOL_ASK),SymbolInfoDouble(SYMBOL,SYMBOL_LAST)));
|
|
break;
|
|
case AskBidWatch:
|
|
return(SymbolInfoDouble(SYMBOL,SYMBOL_ASK)+SymbolInfoDouble(SYMBOL,SYMBOL_BID))/2;
|
|
break;
|
|
}
|
|
return(result);
|
|
}
|
|
double TrueMedian(double first, double second,double third)
|
|
{
|
|
SORT(first,second,third);
|
|
return((third<=0.0)?0.0:((second<=0.0)?third:((first<=0.0)?((third+second)/2.0):second)));
|
|
}
|
|
//+------------------------------------------------------------------+ |