279 lines
24 KiB
MQL5
279 lines
24 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| OptionConvert.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
|
|
};
|
|
struct sOption
|
|
{
|
|
string strSymbol;
|
|
ENUM_SYMBOL_OPTION_RIGHT enRight;
|
|
double dbK;
|
|
double dbP;
|
|
double dbC;
|
|
ulong dtExpiration;
|
|
ulong dtLastDeal;
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Entradas |
|
|
//+------------------------------------------------------------------+
|
|
sinput group "Globais";
|
|
sinput CalcMode inpCalcMode=Median;// Modo de Cálculo:
|
|
sinput datetime inpMaxExpiration=D'2022.12.31 23:00:00';//Máxima expiração:
|
|
sinput int inpSecondPlane=5;//Atualizações em segundo plano (s):
|
|
sinput string inpSymbolConvert="BOVA11";//Converter posição para o Símbolo:
|
|
sinput string inpSymbolToConvert="";
|
|
//+------------------------------------------------------------------+
|
|
//| Globais |
|
|
//+------------------------------------------------------------------+
|
|
string strStatus="";
|
|
string strComment="";
|
|
sOption arrBruteOptions[];//Usado como basse para carregar o arrOptions ( localmente em EA() ) já ordenado
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
//--- create timer
|
|
LoadBruteOptions();
|
|
EventSetTimer(1);
|
|
|
|
//---
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
//--- destroy timer
|
|
EventKillTimer();
|
|
Comment("");
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
//---
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Timer function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTimer()
|
|
{
|
|
//---
|
|
EA();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Função Principal |
|
|
//+------------------------------------------------------------------+
|
|
void EA()
|
|
{
|
|
if(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_PUT)
|
|
{
|
|
|
|
if((MathMod((double)TimeLocal(),inpSecondPlane)==0.0))LoadBruteOptions();
|
|
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));
|
|
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].dtLastDeal=arrBruteOptions[s].dtLastDeal;
|
|
arrOptions[i].dbK=arrBruteOptions[s].dbK;
|
|
arrOptions[i].dbC=arrBruteOptions[s].dbC;
|
|
arrOptions[i].dbP=arrBruteOptions[s].dbP;
|
|
}
|
|
|
|
strComment="Symbol Q K X B Q.K Q.X Q.B Caixa Break Saldo K/S\n";
|
|
|
|
double dbEquivalentK=0.0;
|
|
double dbQxK=0.0;
|
|
double dbQxX=0.0;
|
|
double dbQxB=0.0;
|
|
ulong ulPositionTicket=0;
|
|
for(int i=0;i<PositionsTotal();i++)
|
|
{
|
|
ulPositionTicket=PositionGetTicket(i);
|
|
if(PositionGetSymbol(i)==_Symbol)
|
|
{
|
|
dbEquivalentK=SymbolPrice(inpSymbolConvert,inpCalcMode)*SymbolInfoDouble(PositionGetSymbol(i),SYMBOL_OPTION_STRIKE)/SymbolPrice(OptionBasis(_Symbol),inpCalcMode);
|
|
dbQxK=SymbolInfoDouble(PositionGetSymbol(i),SYMBOL_OPTION_STRIKE)*PositionGetDouble(POSITION_VOLUME);
|
|
//dbQxX=PositionGetDouble(POSITION_PRICE_OPEN)*PositionGetDouble(POSITION_VOLUME);
|
|
dbQxX=SymbolPrice(PositionGetSymbol(i),inpCalcMode)*PositionGetDouble(POSITION_VOLUME);
|
|
dbQxB=(SymbolInfoDouble(PositionGetSymbol(i),SYMBOL_OPTION_STRIKE)-SymbolPrice(PositionGetSymbol(i),inpCalcMode))*PositionGetDouble(POSITION_VOLUME);
|
|
strComment+=StringFormat("%8s %4.0f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %.4f (K=%.2f)",
|
|
PositionGetSymbol(i),
|
|
PositionGetDouble(POSITION_VOLUME),
|
|
SymbolInfoDouble(PositionGetSymbol(i),SYMBOL_OPTION_STRIKE),
|
|
//PositionGetDouble(POSITION_PRICE_OPEN),
|
|
SymbolPrice(PositionGetSymbol(i),inpCalcMode),
|
|
SymbolInfoDouble(PositionGetSymbol(i),SYMBOL_OPTION_STRIKE)-SymbolPrice(PositionGetSymbol(i),inpCalcMode),
|
|
SymbolInfoDouble(PositionGetSymbol(i),SYMBOL_OPTION_STRIKE)*PositionGetDouble(POSITION_VOLUME),
|
|
SymbolPrice(PositionGetSymbol(i),inpCalcMode)*PositionGetDouble(POSITION_VOLUME),
|
|
dbQxB,
|
|
SymbolInfoDouble(PositionGetSymbol(i),SYMBOL_OPTION_STRIKE)/SymbolPrice(OptionBasis(_Symbol),inpCalcMode),
|
|
dbEquivalentK
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
double dbQ=0.0;
|
|
double dbCaixa=0.0;
|
|
double dbBreak=0.0;
|
|
strComment+="\n";
|
|
for(int i=0;i<ArraySize(arrOptions);i++)
|
|
{
|
|
dbQ=NormalizeDouble(dbQxK/arrOptions[i].dbK,0);
|
|
dbCaixa=(dbQ*arrOptions[i].dbP)-dbQxX;
|
|
dbBreak=dbQxB-(dbQ*(arrOptions[i].dbK-arrOptions[i].dbP));
|
|
if(
|
|
(SymbolInfoInteger(arrOptions[i].strSymbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_PUT)
|
|
&&( ((dbBreak)>0.0)||(dbCaixa>0.0) )
|
|
//&&(arrOptions[i].dbK>=dbEquivalentK)
|
|
//&&(arrOptions[i].dbP>0.0)
|
|
&&((StringLen(inpSymbolToConvert)>0?inpSymbolToConvert==arrOptions[i].strSymbol:true))
|
|
)
|
|
{
|
|
strComment+=StringFormat("%8s %4.0f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %4.2f %.4f\n",
|
|
arrOptions[i].strSymbol,
|
|
dbQ,
|
|
arrOptions[i].dbK,
|
|
arrOptions[i].dbP,
|
|
arrOptions[i].dbK-arrOptions[i].dbP,
|
|
dbQ*arrOptions[i].dbK,
|
|
dbQ*arrOptions[i].dbP,
|
|
dbQ*(arrOptions[i].dbK-arrOptions[i].dbP),
|
|
dbCaixa,
|
|
dbBreak,
|
|
dbCaixa+dbBreak,
|
|
arrOptions[i].dbK/SymbolPrice(OptionBasis(arrOptions[i].strSymbol),inpCalcMode)
|
|
);
|
|
}
|
|
}
|
|
Comment(strStatus+"\n"+strComment);
|
|
|
|
}
|
|
else
|
|
{
|
|
Comment("");
|
|
}
|
|
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| 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;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Carrega a Matriz de Opções (Bruta) |
|
|
//+------------------------------------------------------------------+
|
|
void LoadBruteOptions()
|
|
{
|
|
string strISIN = SymbolInfoString(inpSymbolConvert, SYMBOL_ISIN);
|
|
ArrayFree(arrBruteOptions);
|
|
ZeroMemory(arrBruteOptions);
|
|
strStatus="Carregando opções....\n";
|
|
Comment(strStatus+strComment);
|
|
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(inpSymbolConvert,SYMBOL_ISIN)))||((StringSubstr(SymbolInfoString(SymbolName(i,false),SYMBOL_ISIN),2,5)==StringSubstr(SymbolInfoString(inpSymbolConvert,SYMBOL_ISIN),2,5))))
|
|
&&(SymbolInfoInteger(SymbolName(i,false),SYMBOL_EXPIRATION_TIME)<inpMaxExpiration)
|
|
&&(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);
|
|
arrBruteOptions[ArraySize(arrBruteOptions)-1].dtLastDeal=SymbolInfoInteger(SymbolName(i,false),SYMBOL_TIME);
|
|
}
|
|
}
|
|
strStatus="";
|
|
Comment(strStatus+"\n"+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)));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 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;
|
|
}
|
|
//+------------------------------------------------------------------+
|