OptionsRoll/OptionsRoll.mq5

341 lines
37 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 16:15:04 +02:00
<EFBFBD><EFBFBD>//+------------------------------------------------------------------+
//| OptionsRoll.mq5 |
//| Copyright 2022, Allan Serotini |
//| 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
DWAP // Daily Weighted Average Price
};
struct sOption
{
string strSymbol;
ENUM_SYMBOL_OPTION_RIGHT enRight;
double dbK;
double dbP;
double dbC;
ulong dtExpiration;
// ulong dtLastDeal;
};
//+------------------------------------------------------------------+
//| Expert input values |
//+------------------------------------------------------------------+
sinput CalcMode inpCalcMode = Median; // Modo de C<EFBFBD>lculo:
sinput double inpX=0.0; // Pr<EFBFBD>mio Atual:
sinput double inpXc=0.0; // Pr<EFBFBD>mio caso o atual esteja Zero:
sinput bool inpB=false; // Permitir op<EFBFBD><EFBFBD>es com menor B:
sinput bool inpDifX=false; // Permitir op<EFBFBD><EFBFBD>es com menores X:
sinput bool inpShowZeroE=false; // Permitir op<EFBFBD><EFBFBD>es com zero de E:
sinput bool inpLossK=false; // Permitir op<EFBFBD><EFBFBD>es com perda de K:
sinput string inpBlackList=""; // Ignorar as op<EFBFBD><EFBFBD>es:
const double inpDI=10.58; // Refer<EFBFBD>ncia (% a.a.):
const double inpRPortfolio=23.92; // Retorno da Carteira (% a.a.):
//+------------------------------------------------------------------+
//| Globals |
//+------------------------------------------------------------------+
sOption arrBruteOptions[];//Usado como basse para carregar o arrOptions ( localmente em EA() ) j<EFBFBD> ordenado
//const string _Symbol=_Symbol;
const int inpSecondPlane=5; // Frequencia carregamento das op<EFBFBD><EFBFBD>es (s):
string strComment="";
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
//--- create timer
ChartSetColorBars(clrLime);
EventSetTimer(1);
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- destroy timer
ObjectsDeleteAll(0,"OptionRoll");
ChartSetInteger(0,CHART_COLOR_BACKGROUND,clrBlack);
ChartSetColorBars(clrLime);
EventKillTimer();
Comment("");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
//---
if(SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)>0.0)
{
if((MathMod((double)TimeLocal(),inpSecondPlane)==0.0))LoadBruteOptions();
double dbE=0.0;
double dbYe=0.0;
ulong vSort[][2];
ZeroMemory(vSort);
ArrayResize(vSort,ArraySize(arrBruteOptions));
for(int i=0;i<ArraySize(arrBruteOptions);i++)
{
//vSort[i,0]=(arrBruteOptions[i].dtExpiration*1000000)+(arrBruteOptions[i].enRight*100000)+StringToInteger(DoubleToString(arrBruteOptions[i].dbK*1000,0));
if(arrBruteOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL) dbE=MathMax(0,arrBruteOptions[i].dbC-MathMax(0,(SymbolPrice(OptionBasis(_Symbol),inpCalcMode)-arrBruteOptions[i].dbK)));
if(arrBruteOptions[i].enRight==SYMBOL_OPTION_RIGHT_PUT) dbE=MathMax(0,arrBruteOptions[i].dbP-MathMax(0,(arrBruteOptions[i].dbK-SymbolPrice(OptionBasis(_Symbol),inpCalcMode))));
dbYe=dbE/arrBruteOptions[i].dbK*100;
vSort[i,0]=(arrBruteOptions[i].dtExpiration*1000000)+(arrBruteOptions[i].enRight*100000)+StringToInteger(DoubleToString(dbYe*1000,0));
vSort[i,1]=i;
}
ArraySort(vSort);
int s=0;
sOption arrOptions[];
ArrayResize(arrOptions,ArraySize(arrBruteOptions));
for(int i=0;i<ArraySize(arrOptions);i++)
{
s=(int)vSort[i,1];
arrOptions[i].enRight=arrBruteOptions[s].enRight;
arrOptions[i].strSymbol=arrBruteOptions[s].strSymbol;
arrOptions[i].dtExpiration=arrBruteOptions[s].dtExpiration;
arrOptions[i].dbK=arrBruteOptions[s].dbK;
arrOptions[i].dbC=arrBruteOptions[s].dbC;
arrOptions[i].dbP=arrBruteOptions[s].dbP;
}
ObjectCreate(0,"OptionRoll_PriceLine",OBJ_HLINE,0,0,SymbolPrice(_Symbol,inpCalcMode));
ObjectSetInteger(0,"OptionRoll_PriceLine",OBJPROP_STYLE,STYLE_DOT);
ObjectSetInteger(0,"OptionRoll_PriceLine",OBJPROP_COLOR,clrGold);
/*
strComment=(inpX>0.0?StringFormat("S=%.2f, B=%.4f %s, X=%.2f, Bx=%.2f\n",
SymbolPrice(OptionBasis(_Symbol),inpCalcMode),
(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)+inpX:SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)-inpX),
StringSubstr(TimeToString(SymbolInfoInteger(_Symbol,SYMBOL_EXPIRATION_TIME),TIME_DATE),8,2)+"/"+StringSubstr(TimeToString(SymbolInfoInteger(_Symbol,SYMBOL_EXPIRATION_TIME),TIME_DATE),5,2), //(arrOptions[i].dtExpiration-TimeLocal())/60/60/24,
(SymbolPrice(_Symbol,inpCalcMode)==0.0?inpXc:SymbolPrice(_Symbol,inpCalcMode)),
(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)+(SymbolPrice(_Symbol,inpCalcMode)==0.0?inpXc:SymbolPrice(_Symbol,inpCalcMode)):SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)-(SymbolPrice(_Symbol,inpCalcMode)==0.0?inpXc:SymbolPrice(_Symbol,inpCalcMode)))
):"");
*/
SymbolSelect(OptionBasis(_Symbol),true);
strComment=StringFormat("%s=%.2f, B=%.4f %s, X=%.2f, Bx=%.2f, Xopm=%.2f, Bopm=%.2f\n",
OptionBasis(_Symbol),
SymbolPrice(OptionBasis(_Symbol),inpCalcMode),
(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)+inpX:SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)-inpX),
StringSubstr(TimeToString(SymbolInfoInteger(_Symbol,SYMBOL_EXPIRATION_TIME),TIME_DATE),8,2)+"/"+StringSubstr(TimeToString(SymbolInfoInteger(_Symbol,SYMBOL_EXPIRATION_TIME),TIME_DATE),5,2), //(arrOptions[i].dtExpiration-TimeLocal())/60/60/24,
(SymbolPrice(_Symbol,inpCalcMode)==0.0?inpXc:SymbolPrice(_Symbol,inpCalcMode)),
(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)+(SymbolPrice(_Symbol,inpCalcMode)==0.0?inpXc:SymbolPrice(_Symbol,inpCalcMode)):SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)-(SymbolPrice(_Symbol,inpCalcMode)==0.0?inpXc:SymbolPrice(_Symbol,inpCalcMode)))
);
if(ChartGetInteger(0,CHART_IS_MAXIMIZED))
{
strComment+="\n";
ChartSetInteger(0,CHART_COLOR_BACKGROUND,clrBlack);
ChartSetColorBars(clrLime);
}
double _B0=(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)+(inpX>0.0?inpX:SymbolPrice(_Symbol,inpCalcMode)):SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)-(inpX>0.0?inpX:SymbolPrice(_Symbol,inpCalcMode)));
double _X0=(SymbolPrice(_Symbol,inpCalcMode)==0.0?inpXc:SymbolPrice(_Symbol,inpCalcMode));
double _B=0.0;
double _X=0.0;
double _I=0.0;
double _E=0.0;
double dbReturn=0.0;
string strFormatMaximized="%-8s %4.2f %3.2f (E=%3.2f%%) %4.2f %s : %.4f %.2f%%\n";
if(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL)
{
for(int i=ArraySize(arrOptions)-1;i>=0;i--)
{
_B=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbK+arrOptions[i].dbC:arrOptions[i].dbK-arrOptions[i].dbP);
_X=arrOptions[i].dbC;
_I=MathMax(0,SymbolPrice(OptionBasis(_Symbol),inpCalcMode)-arrOptions[i].dbK);
_E=MathMax(0,_X-_I);
dbReturn=(arrOptions[i].dbK-SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)+arrOptions[i].dbC-_X0)
/
(2/(1/(arrOptions[i].dbK+arrOptions[i].dbC)+1/(SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)+_X0)));
if(
(StringSubstr(arrOptions[i].strSymbol,4,1)==nextOptionSerie(StringSubstr(_Symbol,4,1)))
&&((_B>=_B0)||(_B>=(SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)+SymbolPrice(_Symbol,inpCalcMode)))||inpB)
&&((_X>=SymbolPrice(_Symbol,inpCalcMode))||inpDifX)
&&((_E>0.0)||inpShowZeroE)
&&((arrOptions[i].dbK>=SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE))||inpLossK)
&&(StringFind(inpBlackList,arrOptions[i].strSymbol)<0)
)
{
strComment+=StringFormat(strFormatMaximized,
arrOptions[i].strSymbol,
arrOptions[i].dbK, _X,
_E/_X*100,
_B,
StringSubstr(TimeToString(arrOptions[i].dtExpiration,TIME_DATE),8,2)+"/"+StringSubstr(TimeToString(arrOptions[i].dtExpiration,TIME_DATE),5,2), //(arrOptions[i].dtExpiration-TimeLocal())/60/60/24,
(arrOptions[i].dbK-SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE))+(arrOptions[i].dbC-_X0),
//100*dbReturn
((100*dbReturn)/((arrOptions[i].dtExpiration-TimeLocal())/60/60/24))*30
);
if(!ChartGetInteger(0,CHART_IS_MAXIMIZED)) if((((100*dbReturn)/((arrOptions[i].dtExpiration-TimeLocal())/60/60/24))*30*12)>inpDI) ChartSetColorBars(clrCyan);
if(!ChartGetInteger(0,CHART_IS_MAXIMIZED)) if((((100*dbReturn)/((arrOptions[i].dtExpiration-TimeLocal())/60/60/24))*30*12)>inpRPortfolio) ChartSetColorBars(clrDarkGoldenrod);
if(!ChartGetInteger(0,CHART_IS_MAXIMIZED)) break;
}
}
}
if(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_PUT)
{
for(int i=ArraySize(arrOptions)-1;i>=0;i--)
{
_B=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbK+arrOptions[i].dbC:arrOptions[i].dbK-arrOptions[i].dbP);
_X=arrOptions[i].dbP;
_I=MathMax(0,arrOptions[i].dbK-SymbolPrice(OptionBasis(_Symbol),inpCalcMode));
_E=MathMax(0,_X-_I);
dbReturn=(((SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)-arrOptions[i].dbK)+(arrOptions[i].dbP-_X0))/arrOptions[i].dbK);
if(
(StringSubstr(arrOptions[i].strSymbol,4,1)==nextOptionSerie(StringSubstr(_Symbol,4,1)))
&&((_B<=_B0)||inpB)
&&((_X>=(SymbolPrice(_Symbol,inpCalcMode)==0.0?inpXc:SymbolPrice(_Symbol,inpCalcMode)))||inpDifX)
&&((_E>0.0)||inpShowZeroE)
&&((arrOptions[i].dbK<=SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE))||inpLossK)
&&(StringFind(inpBlackList,arrOptions[i].strSymbol)<0)
)
{
strComment+=StringFormat(strFormatMaximized,
arrOptions[i].strSymbol,
arrOptions[i].dbK, _X,
_E/_X*100.0,
_B,
StringSubstr(TimeToString(arrOptions[i].dtExpiration,TIME_DATE),8,2)+"/"+StringSubstr(TimeToString(arrOptions[i].dtExpiration,TIME_DATE),5,2), //(arrOptions[i].dtExpiration-TimeLocal())/60/60/24,
(SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)-arrOptions[i].dbK)+(arrOptions[i].dbP-_X0), //SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)-arrOptions[i].dbK,
((100*dbReturn)/((arrOptions[i].dtExpiration-TimeLocal())/60/60/24))*30
);
if(!ChartGetInteger(0,CHART_IS_MAXIMIZED)) if((((100*dbReturn)/((arrOptions[i].dtExpiration-TimeLocal())/60/60/24))*30*12)>inpDI) ChartSetColorBars(clrCyan);
if(!ChartGetInteger(0,CHART_IS_MAXIMIZED)) if((((100*dbReturn)/((arrOptions[i].dtExpiration-TimeLocal())/60/60/24))*30*12)>inpRPortfolio) ChartSetColorBars(clrDarkGoldenrod);
if(!ChartGetInteger(0,CHART_IS_MAXIMIZED)) break;
}
}
}
Comment(strComment);
}
else
{
// Ativos
}
}
//+------------------------------------------------------------------+
//| Symbol Price |
//+------------------------------------------------------------------+
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;
}
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)));
}
//+------------------------------------------------------------------+
//| Ativo subjacente da op<EFBFBD><EFBFBD>o |
//+------------------------------------------------------------------+
string OptionBasis(const string strSymbol)
{
string strReturn=StringSubstr(SymbolInfoString(strSymbol,SYMBOL_ISIN),2,4);
if(StringSubstr(SymbolInfoString(strSymbol, SYMBOL_ISIN), 6, 1)=="9")
{
strReturn+="11";
}
else
{
strReturn+=StringSubstr(SymbolInfoString(strSymbol,SYMBOL_ISIN),6,1);
}
return strReturn;
}
//+------------------------------------------------------------------+
//| Carrega a Matriz de Op<EFBFBD><EFBFBD>es (Bruta) |
//+------------------------------------------------------------------+
void LoadBruteOptions()
{
string strISIN = SymbolInfoString(_Symbol, SYMBOL_ISIN);
ArrayFree(arrBruteOptions);
ZeroMemory(arrBruteOptions);
for(int i=0;i<SymbolsTotal(false);i++)
{
if(
(SymbolInfoDouble(SymbolName(i,false),SYMBOL_OPTION_STRIKE)>0.0)
&&((SymbolInfoInteger(SymbolName(i,false),SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL)||(SymbolInfoInteger(SymbolName(i,false),SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_PUT))
&&((StringSubstr(SymbolInfoString(SymbolName(i,false),SYMBOL_ISIN),2,5)==ISINtoSymbol(SymbolInfoString(_Symbol,SYMBOL_ISIN)))||((StringSubstr(SymbolInfoString(SymbolName(i,false),SYMBOL_ISIN),2,5)==StringSubstr(SymbolInfoString(_Symbol,SYMBOL_ISIN),2,5))))
&&(SymbolInfoInteger(SymbolName(i,false),SYMBOL_EXPIRATION_TIME)<D'2024.12.31 23:00:00')
&&(SymbolInfoInteger(SymbolName(i,false),SYMBOL_EXPIRATION_TIME)>TimeLocal())
)
{
SymbolSelect(SymbolName(i,false),true);
ArrayResize(arrBruteOptions,ArraySize(arrBruteOptions)+1);
arrBruteOptions[ArraySize(arrBruteOptions)-1].strSymbol=SymbolName(i,false);
arrBruteOptions[ArraySize(arrBruteOptions)-1].enRight=(ENUM_SYMBOL_OPTION_RIGHT)SymbolInfoInteger(SymbolName(i,false),SYMBOL_OPTION_RIGHT);
arrBruteOptions[ArraySize(arrBruteOptions)-1].dtExpiration=SymbolInfoInteger(SymbolName(i,false),SYMBOL_EXPIRATION_TIME);
arrBruteOptions[ArraySize(arrBruteOptions)-1].dbK=SymbolInfoDouble(SymbolName(i,false),SYMBOL_OPTION_STRIKE);
if(SymbolInfoInteger(SymbolName(i,false),SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL) arrBruteOptions[ArraySize(arrBruteOptions)-1].dbC=SymbolPrice(SymbolName(i,false),inpCalcMode);
if(SymbolInfoInteger(SymbolName(i,false),SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_PUT) arrBruteOptions[ArraySize(arrBruteOptions)-1].dbP=SymbolPrice(SymbolName(i,false),inpCalcMode);
}
}
}
//+------------------------------------------------------------------+
//| Converte C<EFBFBD>digo ISIN para Symbol |
//+------------------------------------------------------------------+
string ISINtoSymbol(const string strISIN)
{
string strSymbol=StringSubstr(strISIN,2,4);
if(StringSubstr(strISIN,9,2)=="PR") strSymbol+="4";
if(StringSubstr(strISIN,9,2)=="OR") strSymbol+="3";
if(StringSubstr(strISIN,9,2)=="00") strSymbol+="9";
return strSymbol;
}
//+------------------------------------------------------------------+
//| Pr<EFBFBD>xima s<EFBFBD>ria da Op<EFBFBD><EFBFBD>o |
//+------------------------------------------------------------------+
string nextOptionSerie(const string strSerieSymbol)
{
string strReturn="";
string strCall="ABCDEFGHIJKL";
string strPut="MNOPQRSTUVWX";
if(StringFind(strCall,strSerieSymbol,0)>=0) strReturn=StringSubstr(strCall,StringFind(strCall,strSerieSymbol,0)+1,1); if((StringFind(strCall,strSerieSymbol,0)+1)==StringLen(strCall)) strReturn="A";
if(StringFind(strPut,strSerieSymbol,0)>=0) strReturn=StringSubstr(strPut,StringFind(strPut,strSerieSymbol,0)+1,1); if((StringFind(strPut,strSerieSymbol,0)+1)==StringLen(strPut)) strReturn="M";
return strReturn;
}
void ChartSetColorBars(const color clrChange)
{
//ChartSetInteger(0,CHART_COLOR_BACKGROUND,clrChange);
ChartSetInteger(0,CHART_COLOR_CHART_UP,clrChange);
ChartSetInteger(0,CHART_COLOR_CHART_LINE,clrChange);
ChartSetInteger(0,CHART_COLOR_CHART_DOWN,clrChange);
return;
}