//+------------------------------------------------------------------+ //| 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álculo: sinput double inpX=0.0; // Prêmio Atual: sinput double inpXc=0.0; // Prêmio caso o atual esteja Zero: sinput bool inpB=false; // Permitir opções com menor B: sinput bool inpDifX=false; // Permitir opções com menores X: sinput bool inpShowZeroE=false; // Permitir opções com zero de E: sinput bool inpLossK=false; // Permitir opções com perda de K: sinput string inpBlackList=""; // Ignorar as opções: const double inpDI=10.58; // Referê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á ordenado //const string _Symbol=_Symbol; const int inpSecondPlane=5; // Frequencia carregamento das opçõ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;i0.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çã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ções (Bruta) | //+------------------------------------------------------------------+ void LoadBruteOptions() { string strISIN = SymbolInfoString(_Symbol, SYMBOL_ISIN); ArrayFree(arrBruteOptions); ZeroMemory(arrBruteOptions); for(int i=0;i0.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)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ó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óxima séria da Opçã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; }