986 lines
No EOL
100 KiB
MQL5
986 lines
No EOL
100 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| OptionSniffer.mq5 |
|
|
//| Arthur Albano |
|
|
//| https://www.mql5.com/en/users/arthuralbano/ |
|
|
//+------------------------------------------------------------------+
|
|
#include <Graphics\Graphic.mqh>
|
|
|
|
#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
|
|
};
|
|
enum showSerie
|
|
{
|
|
Current,//Current
|
|
Next,//Next
|
|
Next_2x//Next Next
|
|
};
|
|
struct sOption
|
|
{
|
|
string strSymbol;
|
|
ENUM_SYMBOL_OPTION_RIGHT enRight;
|
|
double dbK;
|
|
double dbP;
|
|
double dbC;
|
|
ulong dtExpiration;
|
|
ulong dtLastDeal;
|
|
};
|
|
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;
|
|
---------------------------------*/
|
|
//double dbK;
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Entradas |
|
|
//+------------------------------------------------------------------+
|
|
sinput group "Globais";
|
|
sinput CalcMode inpCalcMode=Median;// Modo de Cálculo:
|
|
sinput datetime inpMaxExpiration=D'2023.12.31 23:00:00';//Máxima expiração:
|
|
sinput int inpSecondPlane=5;//Atualizações em segundo plano (s):
|
|
sinput group "Ativo";
|
|
sinput string inpNextCall="";//Rolar Call para:
|
|
sinput string inpNextPut="";//Rolar Put para:
|
|
sinput string inpBlackList="";//Opções para ignorar:
|
|
sinput group "Options";
|
|
sinput showSerie inpShowSerie=Next;//Série:
|
|
sinput bool inpShowNegativeDeltaX=true;//Mostrar com diferença de prêmios negativo:
|
|
sinput bool inpBreakMinorK=true;//Mostrar com ponto de equilibrio menor que atual:
|
|
sinput bool inpPlotGraph=false;//Modo Gráfico ?
|
|
sinput double inpFaixaK=100.0;//Faixa de Strikes (%):
|
|
sinput group "Option Price Model";
|
|
sinput datetime inpExpirationDI=D'2024.01.24 23:59';//Expiração DI:
|
|
sinput double inpr_0=13.625;//Índice DI:
|
|
//sinput datetime inpNextExpiration=D'2023.11.18 23:59';//Próximo Exercício:
|
|
//+------------------------------------------------------------------+
|
|
//| Globais |
|
|
//+------------------------------------------------------------------+
|
|
const double cCoust=2.49;
|
|
string strStatus="";
|
|
string strComment="";
|
|
string strFile="";
|
|
sOption arrBruteOptions[];//Usado como basse para carregar o arrOptions ( localmente em EA() ) já ordenado
|
|
bool bImprimiu=false;
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
LoadBruteOptions();
|
|
EA(); EventSetTimer(1);
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
ObjectsDeleteAll(0,"OptionSniffer");
|
|
EventKillTimer();
|
|
Comment("");
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Timer function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTimer()
|
|
{
|
|
EA();
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Função Principal |
|
|
//+------------------------------------------------------------------+
|
|
void EA()
|
|
{
|
|
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;
|
|
}
|
|
double dbB=0.0;
|
|
double dbLastPremium=lastDealPrice(_Symbol);
|
|
double dbS=0.0;
|
|
double dbE=0.0;
|
|
double dbY=0.0;
|
|
double dbYSum=0.0;
|
|
double dbYperMonth=0.0;
|
|
long lDaysToExpirate=0;
|
|
ulong ulPositionTicket=0;
|
|
for(int i=0;i<PositionsTotal();i++)
|
|
{
|
|
ulPositionTicket=PositionGetTicket(i);
|
|
if(PositionGetSymbol(i)==_Symbol)
|
|
{
|
|
dbB=(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)+PositionGetDouble(POSITION_PRICE_OPEN):SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)-PositionGetDouble(POSITION_PRICE_OPEN));
|
|
}
|
|
}
|
|
ulong ulHistoryDealTicket=0;
|
|
if(dbB==0)
|
|
{
|
|
for(int i=(HistoryDealsTotal()-1);i>=0;i--)
|
|
{
|
|
ulHistoryDealTicket=HistoryDealGetTicket(i);
|
|
if(
|
|
(SymbolInfoInteger(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL),SYMBOL_OPTION_RIGHT)==SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT))
|
|
&&(OptionBasis(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL))==OptionBasis(_Symbol))
|
|
)
|
|
{
|
|
if(
|
|
(SymbolInfoInteger(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL),SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL)
|
|
&&(HistoryDealGetInteger(ulHistoryDealTicket,DEAL_TYPE)==DEAL_TYPE_SELL)
|
|
)
|
|
{
|
|
dbB=//SymbolInfoDouble(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL),SYMBOL_OPTION_STRIKE);//+HistoryDealGetDouble(ulHistoryDealTicket,DEAL_PRICE);
|
|
dbB=((SymbolInfoInteger(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL),SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?1:-1)*HistoryDealGetDouble(ulHistoryDealTicket,DEAL_PRICE))+SymbolInfoDouble(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL),SYMBOL_OPTION_STRIKE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sOption stcBest_E; ZeroMemory(stcBest_E); double dbE_Best=-99999.99;
|
|
sOption stcBest_B; ZeroMemory(stcBest_B); double dbB_Best=(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?-9999.99:99999.99);
|
|
// sOption stcRollP; ZeroMemory(stcRollP);
|
|
// sOption stcRollC; ZeroMemory(stcRollC);
|
|
dbS=SymbolPrice(OptionBasis(_Symbol),inpCalcMode);
|
|
lDaysToExpirate=(SymbolInfoInteger(_Symbol,SYMBOL_EXPIRATION_TIME)-TimeLocal())/86400;
|
|
strComment=StringFormat("%s, " ,OptionBasis(_Symbol));
|
|
strComment+=IntegerToString(lDaysToExpirate)+" days to "+nextOptionSerie(StringSubstr(_Symbol,4,1))+" series.";
|
|
strComment+="\n";
|
|
strComment+=StringFormat("S=%.2f, "+(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?"C=":"P=")+"%.2f, B=%.2f, OPM=%.4f\n",SymbolPrice(OptionBasis(_Symbol),inpCalcMode),SymbolPrice(_Symbol,inpCalcMode),dbB,OptionPrice(_Symbol));
|
|
strComment+="\n\n";
|
|
for(int i=0;i<ArraySize(arrOptions);i++)
|
|
{
|
|
if(
|
|
(StringSubstr(arrOptions[i].strSymbol,4,1)==(inpShowSerie==Current?StringSubstr(_Symbol,4,1):(inpShowSerie==Next?nextOptionSerie(StringSubstr(_Symbol,4,1)):nextOptionSerie(nextOptionSerie(StringSubstr(_Symbol,4,1))))))
|
|
// &&(arrOptions[i].dbK>(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbC:arrOptions[i].dbP))
|
|
)
|
|
{
|
|
dbE=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?MathMax(0,arrOptions[i].dbC-MathMax(0,SymbolPrice(OptionBasis(_Symbol),inpCalcMode)-arrOptions[i].dbK)):MathMax(0,arrOptions[i].dbP-MathMax(0,arrOptions[i].dbK-SymbolPrice(OptionBasis(_Symbol),inpCalcMode))));
|
|
//dbY=dbE/(2/(1/(dbS+dbE)+1/dbS));
|
|
dbY=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbC:arrOptions[i].dbP)/arrOptions[i].dbK;
|
|
dbYperMonth=dbY/lDaysToExpirate*30;
|
|
if((dbE>dbE_Best)&&((arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbC:arrOptions[i].dbP)>=SymbolPrice(_Symbol,inpCalcMode)))
|
|
{
|
|
dbE_Best=dbE;
|
|
stcBest_E.strSymbol=arrOptions[i].strSymbol;
|
|
stcBest_E.enRight=arrOptions[i].enRight;
|
|
stcBest_E.dbK=arrOptions[i].dbK;
|
|
stcBest_E.dbC=arrOptions[i].dbC;
|
|
stcBest_E.dbP=arrOptions[i].dbP;
|
|
stcBest_E.dtExpiration=arrOptions[i].dtExpiration;
|
|
stcBest_E.dtExpiration=arrOptions[i].dtLastDeal;
|
|
if(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL)
|
|
{
|
|
if((arrOptions[i].dbK+arrOptions[i].dbC)>=dbB)
|
|
{
|
|
// stcRollC.strSymbol=arrOptions[i].strSymbol;
|
|
// stcRollC.enRight=arrOptions[i].enRight;
|
|
// stcRollC.dbK=arrOptions[i].dbK;
|
|
// stcRollC.dbC=arrOptions[i].dbC;
|
|
// stcRollC.dbP=arrOptions[i].dbP;
|
|
// stcRollC.dtExpiration=arrOptions[i].dtExpiration;
|
|
// stcRollC.dtExpiration=arrOptions[i].dtLastDeal;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((arrOptions[i].dbK-arrOptions[i].dbP)<=dbB)
|
|
{
|
|
// stcRollP.strSymbol=arrOptions[i].strSymbol;
|
|
// stcRollP.enRight=arrOptions[i].enRight;
|
|
// stcRollP.dbK=arrOptions[i].dbK;
|
|
// stcRollP.dbC=arrOptions[i].dbC;
|
|
// stcRollP.dbP=arrOptions[i].dbP;
|
|
// stcRollP.dtExpiration=arrOptions[i].dtExpiration;
|
|
// stcRollP.dtExpiration=arrOptions[i].dtLastDeal;
|
|
}
|
|
}
|
|
if(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL)
|
|
if(arrOptions[i].dbC>=SymbolPrice(_Symbol,inpCalcMode))
|
|
if(dbB_Best<(arrOptions[i].dbK+arrOptions[i].dbC))
|
|
{
|
|
dbB_Best=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbK+arrOptions[i].dbC:arrOptions[i].dbK-arrOptions[i].dbP);
|
|
stcBest_B.strSymbol=arrOptions[i].strSymbol;
|
|
stcBest_B.enRight=arrOptions[i].enRight;
|
|
stcBest_B.dbK=arrOptions[i].dbK;
|
|
stcBest_B.dbC=arrOptions[i].dbC;
|
|
stcBest_B.dbP=arrOptions[i].dbP;
|
|
stcBest_B.dtExpiration=arrOptions[i].dtExpiration;
|
|
stcBest_B.dtExpiration=arrOptions[i].dtLastDeal;
|
|
}
|
|
if(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_PUT)
|
|
if(arrOptions[i].dbP>=SymbolPrice(_Symbol,inpCalcMode))
|
|
if(dbB_Best>(arrOptions[i].dbK-arrOptions[i].dbP))
|
|
{
|
|
dbB_Best=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbK+arrOptions[i].dbC:arrOptions[i].dbK-arrOptions[i].dbP);
|
|
stcBest_B.strSymbol=arrOptions[i].strSymbol;
|
|
stcBest_B.enRight=arrOptions[i].enRight;
|
|
stcBest_B.dbK=arrOptions[i].dbK;
|
|
stcBest_B.dbC=arrOptions[i].dbC;
|
|
stcBest_B.dbP=arrOptions[i].dbP;
|
|
stcBest_B.dtExpiration=arrOptions[i].dtExpiration;
|
|
stcBest_B.dtExpiration=arrOptions[i].dtLastDeal;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------> OPÇÕES
|
|
//
|
|
if(SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)>0.0)
|
|
{
|
|
strComment+="Symbol; K; "+(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?"C":"P")+"; B; E; Y; y "+(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?"dC":"dP")+" OPM\n";
|
|
strComment+="------------------------------------------------------------------------------------------------------\n";
|
|
strFile="Symbol;S;K;C/P;B;E;Y;y;Mod\n";
|
|
if((stcBest_B.dbK>0.0)&&(stcBest_B.dbK<(dbS-(dbS*(inpFaixaK/100)))))
|
|
{
|
|
strComment+="\n";
|
|
strComment+=StringFormat("%s: %.2f; %.2f; %.2f; %.2f; %.2f%%; %.2f%%; %.2f; %.2f >B\n",
|
|
stcBest_B.strSymbol,
|
|
stcBest_B.dbK,
|
|
(stcBest_B.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_B.dbC:stcBest_B.dbP),
|
|
(stcBest_B.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_B.dbK+stcBest_B.dbC:stcBest_B.dbK-stcBest_B.dbP),
|
|
dbB_Best,
|
|
100*dbY,
|
|
100*dbYperMonth,
|
|
(stcBest_B.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_B.dbC:stcBest_B.dbP)-SymbolPrice(_Symbol,inpCalcMode),
|
|
OptionPrice(stcBest_B.strSymbol)
|
|
);
|
|
}
|
|
if((stcBest_E.dbK>0.0)&&(stcBest_E.dbK<(dbS-(dbS*(inpFaixaK/100)))))
|
|
{
|
|
strComment+=StringFormat("%s: %.2f; %.2f; %.2f; %.2f; %.2f%%; %.2f%%; %.2f; %.2f >E\n",
|
|
stcBest_E.strSymbol,
|
|
stcBest_E.dbK,
|
|
(stcBest_E.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_E.dbC:stcBest_E.dbP),
|
|
(stcBest_E.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_E.dbK+stcBest_E.dbC:stcBest_E.dbK-stcBest_E.dbP),
|
|
dbE_Best,
|
|
100*dbY,
|
|
100*dbYperMonth,
|
|
(stcBest_E.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_E.dbC:stcBest_E.dbP)-SymbolPrice(_Symbol,inpCalcMode),
|
|
OptionPrice(stcBest_E.strSymbol)
|
|
);
|
|
}
|
|
double K[]={};
|
|
double X[]={};
|
|
double X0[]={};
|
|
ZeroMemory(K);ZeroMemory(X);ZeroMemory(X0);
|
|
for(int i=0;i<ArraySize(arrOptions);i++)
|
|
{
|
|
if(
|
|
(StringSubstr(arrOptions[i].strSymbol,4,1)==(inpShowSerie==Current?StringSubstr(_Symbol,4,1):(inpShowSerie==Next?nextOptionSerie(StringSubstr(_Symbol,4,1)):nextOptionSerie(nextOptionSerie(StringSubstr(_Symbol,4,1))))))
|
|
&&(((arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbC:arrOptions[i].dbP)-SymbolPrice(_Symbol,inpCalcMode)>=0.0)||inpShowNegativeDeltaX)
|
|
&&((arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbK+arrOptions[i].dbC>dbB:arrOptions[i].dbK-arrOptions[i].dbP<dbB)||(inpBreakMinorK))
|
|
)
|
|
{
|
|
ObjectSetDouble(0,"OptionSniffer_Premium",OBJPROP_PRICE,SymbolPrice(_Symbol,inpCalcMode));
|
|
dbE=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?MathMax(0,arrOptions[i].dbC-MathMax(0,SymbolPrice(OptionBasis(_Symbol),inpCalcMode)-arrOptions[i].dbK)):MathMax(0,arrOptions[i].dbP-MathMax(0,arrOptions[i].dbK-SymbolPrice(OptionBasis(_Symbol),inpCalcMode))));
|
|
//dbY=dbE/(2/(1/(dbS+dbE)+1/dbS));
|
|
dbY=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbC:arrOptions[i].dbP)/arrOptions[i].dbK;
|
|
dbYperMonth=dbY/lDaysToExpirate*30;
|
|
if(
|
|
(arrOptions[i].dbK>(dbS-(dbS*(inpFaixaK/100))))
|
|
&&(arrOptions[i].dbK<(dbS+(dbS*(inpFaixaK/100))))
|
|
)
|
|
{
|
|
ArrayResize(K,K.Size()+1);ArrayResize(X,X.Size()+1);ArrayResize(X0,X0.Size()+1);
|
|
K[K.Size()-1]=arrOptions[i].dbK;X[X.Size()-1]=OptionPrice(arrOptions[i].strSymbol);X0[X.Size()-1]=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbC:arrOptions[i].dbP);
|
|
strComment+=StringFormat("%s: %.2f; %.2f; %.2f; %.2f; %.2f%%; %.2f%%; %.2f; %.2f",
|
|
arrOptions[i].strSymbol,
|
|
arrOptions[i].dbK,
|
|
(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbC:arrOptions[i].dbP),
|
|
(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbK+arrOptions[i].dbC:arrOptions[i].dbK-arrOptions[i].dbP),
|
|
dbE,
|
|
100*dbY,
|
|
100*dbYperMonth,
|
|
(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbC:arrOptions[i].dbP)-SymbolPrice(_Symbol,inpCalcMode),
|
|
OptionPrice(arrOptions[i].strSymbol)
|
|
);
|
|
if(arrOptions[i].strSymbol==stcBest_E.strSymbol)strComment+=" >E ";
|
|
if(arrOptions[i].strSymbol==stcBest_B.strSymbol)strComment+=" >B ";
|
|
strComment+="\n";
|
|
}
|
|
strFile+=StringFormat("%s;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f\n",
|
|
arrOptions[i].strSymbol,
|
|
dbS,
|
|
arrOptions[i].dbK,
|
|
(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbC:arrOptions[i].dbP),
|
|
(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbK+arrOptions[i].dbC:arrOptions[i].dbK-arrOptions[i].dbP),
|
|
dbE,
|
|
100*dbY,
|
|
100*dbYperMonth,
|
|
OptionPrice(arrOptions[i].strSymbol)
|
|
);
|
|
}
|
|
}
|
|
if((stcBest_B.dbK>0.0)&&(stcBest_B.dbK>(dbS+(dbS*(inpFaixaK/100)))))
|
|
{
|
|
strComment+=StringFormat("%s: %.2f; %.2f; %.2f; %.2f; %.2f%%; %.2f%%; %.2f; %.2f >B\n",
|
|
stcBest_B.strSymbol,
|
|
stcBest_B.dbK,
|
|
(stcBest_B.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_B.dbC:stcBest_B.dbP),
|
|
(stcBest_B.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_B.dbK+stcBest_B.dbC:stcBest_B.dbK-stcBest_B.dbP),
|
|
dbB_Best,
|
|
100*dbY,
|
|
100*dbYperMonth,
|
|
(stcBest_B.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_B.dbC:stcBest_B.dbP)-SymbolPrice(_Symbol,inpCalcMode),
|
|
OptionPrice(stcBest_B.strSymbol)
|
|
);
|
|
}
|
|
if((stcBest_E.dbK>0.0)&&(stcBest_E.dbK>(dbS+(dbS*(inpFaixaK/100)))))
|
|
{
|
|
strComment+=StringFormat("%s: %.2f; %.2f; %.2f; %.2f; %.2f%%; %.2f%%; %.2f; %.2f >E\n",
|
|
stcBest_E.strSymbol,
|
|
stcBest_E.dbK,
|
|
(stcBest_E.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_E.dbC:stcBest_E.dbP),
|
|
(stcBest_E.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_E.dbK+stcBest_E.dbC:stcBest_E.dbK-stcBest_E.dbP),
|
|
dbE_Best,
|
|
100*dbY,
|
|
100*dbYperMonth,
|
|
(stcBest_E.enRight==SYMBOL_OPTION_RIGHT_CALL?stcBest_E.dbC:stcBest_E.dbP)-SymbolPrice(_Symbol,inpCalcMode),
|
|
OptionPrice(stcBest_E.strSymbol)
|
|
);
|
|
}
|
|
// strComment+="\nRoll to "+(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?stcRollC.strSymbol:stcRollP.strSymbol);
|
|
|
|
if((inpPlotGraph)&&(SymbolInfoDouble(_Symbol,SYMBOL_OPTION_STRIKE)>0.0))
|
|
{
|
|
ObjectDelete(0,"OptionSniffer_Graph");
|
|
ChartSetInteger(0,CHART_SHOW_DATE_SCALE,0);
|
|
ChartSetInteger(0,CHART_SHOW_PRICE_SCALE,0);
|
|
ChartSetInteger(0,CHART_SHIFT,0);
|
|
|
|
CGraphic graphic;
|
|
graphic.Create(0,"OptionSniffer_Graph",0,0,0,(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS),(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS));
|
|
graphic.BackgroundMain(OptionBasis(_Symbol)+" ("+DoubleToString(dbS,2)+")"+" @ "+TimeToString(TimeCurrent(),TIME_DATE));
|
|
graphic.BackgroundMainSize(12);
|
|
graphic.XAxis().NameSize(12);
|
|
graphic.XAxis().Name("Strike (K)");
|
|
graphic.XAxis().ValuesFormat("%.2f");
|
|
graphic.YAxis().NameSize(12);
|
|
graphic.YAxis().Name("Premium ("+(SymbolInfoInteger(_Symbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?"C":"P")+")");
|
|
graphic.YAxis().ValuesFormat("%.2f");
|
|
|
|
double Sx[2]={dbS,dbS};
|
|
double dbGraphXmax = MathMax(MathMax(X0[0],X[0]),MathMax(X0[ArraySize(X0)-1],X[ArraySize(X)-1]));
|
|
double Sy[2]={0.0,dbGraphXmax};
|
|
CCurve *CCurveS = graphic.CurveAdd(Sx,Sy,ColorToARGB(clrChartreuse,255),CURVE_STEPS,OptionBasis(_Symbol));
|
|
CCurve *CCurveOPM = graphic.CurveAdd(K,X,0x000099,CURVE_LINES,"OPM");
|
|
CCurveOPM.LinesSmooth(true);
|
|
CCurve *CCUrveX0 = graphic.CurveAdd(K,X0,0x009900,CURVE_POINTS,"Premium");
|
|
|
|
graphic.CurvePlotAll();
|
|
graphic.Update();
|
|
}
|
|
else
|
|
{
|
|
ChartSetInteger(0,CHART_SHOW_DATE_SCALE,1);
|
|
ChartSetInteger(0,CHART_SHOW_PRICE_SCALE,1);
|
|
ChartSetInteger(0,CHART_SHIFT,1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------> ATIVO
|
|
//
|
|
int p=0;
|
|
strComment="";
|
|
stcDeal arrDealsSim[]; ZeroMemory(arrDealsSim);
|
|
ulHistoryDealTicket=0;
|
|
HistorySelect(0,TimeCurrent());
|
|
for(int i=0;i<HistoryDealsTotal();i++)
|
|
{
|
|
ulHistoryDealTicket=HistoryDealGetTicket(i);
|
|
if(
|
|
((HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL)==_Symbol)
|
|
||(OptionBasis(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL))==_Symbol))
|
|
&&(SymbolInfoDouble(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL),SYMBOL_OPTION_STRIKE)>0.0)
|
|
)
|
|
{
|
|
ArrayResize(arrDealsSim,ArraySize(arrDealsSim)+1);
|
|
p=ArraySize(arrDealsSim);p--;
|
|
arrDealsSim[p].lPositionID=HistoryDealGetInteger(ulHistoryDealTicket,DEAL_POSITION_ID);
|
|
arrDealsSim[p].strSymbol=HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL);
|
|
arrDealsSim[p].dbVolume=HistoryDealGetDouble(ulHistoryDealTicket,DEAL_VOLUME);
|
|
arrDealsSim[p].dtTime=HistoryDealGetInteger(ulHistoryDealTicket,DEAL_TIME);
|
|
if(SymbolInfoDouble(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL),SYMBOL_OPTION_STRIKE)>0.0)
|
|
{
|
|
arrDealsSim[p].enType=(SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?(ENUM_DEAL_TYPE)HistoryDealGetInteger(ulHistoryDealTicket,DEAL_TYPE):((ENUM_DEAL_TYPE)HistoryDealGetInteger(ulHistoryDealTicket,DEAL_TYPE)==DEAL_TYPE_SELL?(ENUM_DEAL_TYPE)DEAL_TYPE_BUY:(ENUM_DEAL_TYPE)DEAL_TYPE_SELL));
|
|
arrDealsSim[p].dbPrice=(SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?SymbolInfoDouble(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL),SYMBOL_OPTION_STRIKE)+HistoryDealGetDouble(ulHistoryDealTicket,DEAL_PRICE):SymbolInfoDouble(HistoryDealGetString(ulHistoryDealTicket,DEAL_SYMBOL),SYMBOL_OPTION_STRIKE)-HistoryDealGetDouble(ulHistoryDealTicket,DEAL_PRICE));
|
|
}
|
|
else
|
|
{
|
|
arrDealsSim[p].enType=(ENUM_DEAL_TYPE)HistoryDealGetInteger(ulHistoryDealTicket,DEAL_TYPE);
|
|
arrDealsSim[p].dbPrice=HistoryDealGetDouble(ulHistoryDealTicket,DEAL_PRICE);
|
|
}
|
|
arrDealsSim[p].lTicket=(long)ulHistoryDealTicket;
|
|
}
|
|
}
|
|
ulPositionTicket=0;
|
|
for(int i=0;i<PositionsTotal();i++)
|
|
{
|
|
ulPositionTicket=PositionGetTicket(i);
|
|
if(SymbolInfoInteger(PositionGetSymbol(i),SYMBOL_EXPIRATION_TIME)>0)
|
|
if(OptionBasis(PositionGetSymbol(i))==_Symbol)
|
|
{
|
|
ArrayResize(arrDealsSim,ArraySize(arrDealsSim)+1);
|
|
p=ArraySize(arrDealsSim);p--;
|
|
arrDealsSim[p].lPositionID=PositionGetInteger(POSITION_IDENTIFIER);
|
|
arrDealsSim[p].strSymbol=PositionGetSymbol(i);
|
|
arrDealsSim[p].dbVolume=PositionGetDouble(POSITION_VOLUME);
|
|
//arrDealsSim[p].dtTime=(SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_EXPIRATION_TIME)+TimeCurrent())/2;
|
|
arrDealsSim[p].dtTime=TimeCurrent();
|
|
arrDealsSim[p].enType=(SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?DEAL_TYPE_BUY:DEAL_TYPE_SELL);
|
|
arrDealsSim[p].dbPrice=(SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?SymbolInfoDouble(arrDealsSim[p].strSymbol,SYMBOL_OPTION_STRIKE)+SymbolPrice(arrDealsSim[p].strSymbol,inpCalcMode):SymbolInfoDouble(arrDealsSim[p].strSymbol,SYMBOL_OPTION_STRIKE)-SymbolPrice(arrDealsSim[p].strSymbol,inpCalcMode));
|
|
arrDealsSim[p].lTicket=(long)ulPositionTicket;
|
|
}
|
|
}
|
|
if(StringLen(inpNextCall)>0) //-------------------------------------------------------Roll Call para o Symbol informado explícitamente. (input)
|
|
{
|
|
if(SymbolInfoDouble(inpNextCall,SYMBOL_OPTION_STRIKE)>0.0)
|
|
{
|
|
if(SymbolInfoInteger(inpNextCall,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL)
|
|
{
|
|
ArrayResize(arrDealsSim,ArraySize(arrDealsSim)+1);
|
|
p=ArraySize(arrDealsSim);p--;
|
|
arrDealsSim[p].lPositionID=0;
|
|
arrDealsSim[p].strSymbol=inpNextCall;
|
|
arrDealsSim[p].dbVolume=0.0;
|
|
arrDealsSim[p].dtTime=(SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_EXPIRATION_TIME)+TimeCurrent())/2;
|
|
arrDealsSim[p].enType=DEAL_TYPE_SELL;
|
|
arrDealsSim[p].dbPrice=SymbolInfoDouble(inpNextCall,SYMBOL_OPTION_STRIKE)+SymbolPrice(inpNextCall,inpCalcMode);
|
|
arrDealsSim[p].lTicket=0;
|
|
}
|
|
else
|
|
{
|
|
strComment+="Erro: "+inpNextCall+" não é uma opção de compra (CALL)!";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strComment+="Erro: "+inpNextCall+" não é Opção!\n\n";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//-------------------------------------------------------------------------------------------------------------------------------------------------Roll Call para a Ordem
|
|
string strSymbolOrder="";
|
|
|
|
ulong ulOrderTicket=0;
|
|
for(int i=0;i<OrdersTotal();i++)
|
|
{
|
|
ulOrderTicket=OrderGetTicket(i);
|
|
if(
|
|
(OptionBasis(OrderGetString(ORDER_SYMBOL))==_Symbol)
|
|
&&(SymbolInfoInteger(OrderGetString(ORDER_SYMBOL),SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL)
|
|
&&(OrderGetInteger(ORDER_TYPE)==ORDER_TYPE_SELL_LIMIT)
|
|
)
|
|
{
|
|
strSymbolOrder=OrderGetString(ORDER_SYMBOL);
|
|
ArrayResize(arrDealsSim,ArraySize(arrDealsSim)+1);
|
|
p=ArraySize(arrDealsSim);p--;
|
|
arrDealsSim[p].lPositionID=0;
|
|
arrDealsSim[p].strSymbol=strSymbolOrder;
|
|
arrDealsSim[p].dbVolume=-1.0;
|
|
arrDealsSim[p].dtTime=(SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_EXPIRATION_TIME)+TimeCurrent())/2;
|
|
arrDealsSim[p].enType=DEAL_TYPE_SELL;
|
|
arrDealsSim[p].dbPrice=SymbolInfoDouble(strSymbolOrder,SYMBOL_OPTION_STRIKE)+OrderGetDouble(ORDER_PRICE_OPEN);
|
|
arrDealsSim[p].lTicket=0;
|
|
//dbCurrentExtrinsic=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?MathMax(0,arrOptions[i].dbC-MathMax(0,SymbolPrice(OptionBasis(arrOptions[i].strSymbol),inpCalcMode)-arrOptions[i].dbK)):MathMax(0,arrOptions[i].dbP-MathMax(0,arrOptions[i].dbK-SymbolPrice(OptionBasis(arrOptions[i].strSymbol),inpCalcMode))));
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------------------------------------------------------Roll Call para o Symbol calculado
|
|
if(strSymbolOrder.Length()==0)
|
|
{
|
|
int iDealB=0;
|
|
for(int i=(ArraySize(arrDealsSim)-1);i>=0;i--)
|
|
{
|
|
if((SymbolInfoInteger(arrDealsSim[i].strSymbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL)&&(arrDealsSim[i].enType==DEAL_TYPE_SELL))
|
|
{
|
|
iDealB=i;
|
|
break;
|
|
}
|
|
}
|
|
double dbCurrentExtrinsic=0.0;
|
|
ArrayResize(arrDealsSim,ArraySize(arrDealsSim)+1);
|
|
p=ArraySize(arrDealsSim);p--;
|
|
for(int i=(ArraySize(arrOptions)-1);i>=0;i--)
|
|
{
|
|
if(
|
|
nextOptionSerie(StringSubstr(arrDealsSim[iDealB].strSymbol,4,1))==StringSubstr(arrOptions[i].strSymbol,4,1)
|
|
&&((arrOptions[i].dbK+arrOptions[i].dbC)>arrDealsSim[iDealB].dbPrice)
|
|
&&(dbCurrentExtrinsic<((arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?MathMax(0,arrOptions[i].dbC-MathMax(0,SymbolPrice(OptionBasis(arrOptions[i].strSymbol),inpCalcMode)-arrOptions[i].dbK)):MathMax(0,arrOptions[i].dbP-MathMax(0,arrOptions[i].dbK-SymbolPrice(OptionBasis(arrOptions[i].strSymbol),inpCalcMode))))))
|
|
&&(arrOptions[i].dbC>=SymbolPrice(arrDealsSim[iDealB].strSymbol,inpCalcMode))
|
|
&&(StringFind(inpBlackList,arrOptions[i].strSymbol,0)<0)
|
|
//&&(SymbolInfoInteger(arrOptions[i].strSymbol,SYMBOL_EXPIRATION_TIME)<D'2022.12.31 23:00:00')
|
|
)
|
|
{
|
|
arrDealsSim[p].lPositionID=0;
|
|
arrDealsSim[p].strSymbol=arrOptions[i].strSymbol;
|
|
arrDealsSim[p].dbVolume=0.0;
|
|
arrDealsSim[p].dtTime=(SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_EXPIRATION_TIME)+TimeCurrent())/2;
|
|
//arrDealsSim[p].dtTime=TimeCurrent();
|
|
arrDealsSim[p].enType=DEAL_TYPE_SELL;
|
|
arrDealsSim[p].dbPrice=SymbolInfoDouble(arrOptions[i].strSymbol,SYMBOL_OPTION_STRIKE)+arrOptions[i].dbC;
|
|
arrDealsSim[p].lTicket=0;
|
|
dbCurrentExtrinsic=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?MathMax(0,arrOptions[i].dbC-MathMax(0,SymbolPrice(OptionBasis(arrOptions[i].strSymbol),inpCalcMode)-arrOptions[i].dbK)):MathMax(0,arrOptions[i].dbP-MathMax(0,arrOptions[i].dbK-SymbolPrice(OptionBasis(arrOptions[i].strSymbol),inpCalcMode))));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ObjectsDeleteAll(0,"OptionSniffer");
|
|
string strObjectName="";
|
|
strObjectName=StringFormat( "OptionSniffer #%.0f %s %.0f %s at %.4f",
|
|
arrDealsSim[p].lTicket,
|
|
StringSubstr(EnumToString((ENUM_DEAL_TYPE)arrDealsSim[p].enType),10,4),
|
|
arrDealsSim[p].dbVolume,
|
|
arrDealsSim[p].strSymbol,
|
|
arrDealsSim[p].dbPrice
|
|
);
|
|
ObjectCreate(0,strObjectName+" EXPIRATION",OBJ_ARROW_STOP,0,SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_EXPIRATION_TIME),SymbolInfoDouble(arrDealsSim[p].strSymbol,SYMBOL_OPTION_STRIKE));
|
|
ObjectSetInteger(0,strObjectName+" EXPIRATION",OBJPROP_COLOR,clrBlue);
|
|
ObjectCreate(0,strObjectName+" OPM",OBJ_ARROW_DOWN,0,arrDealsSim[p].dtTime,SymbolInfoDouble(arrDealsSim[p].strSymbol,SYMBOL_OPTION_STRIKE)+OptionPrice(arrDealsSim[p].strSymbol));ObjectSetInteger(0,strObjectName+" OPM",OBJPROP_COLOR,clrRed);
|
|
if(StringLen(inpNextPut)>0)
|
|
{
|
|
if(SymbolInfoDouble(inpNextPut,SYMBOL_OPTION_STRIKE)>0.0)
|
|
{
|
|
if(SymbolInfoInteger(inpNextPut,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_PUT)
|
|
{
|
|
ArrayResize(arrDealsSim,ArraySize(arrDealsSim)+1);
|
|
p=ArraySize(arrDealsSim);p--;
|
|
arrDealsSim[p].lPositionID=0;
|
|
arrDealsSim[p].strSymbol=inpNextPut;
|
|
arrDealsSim[p].dbVolume=0.0;
|
|
arrDealsSim[p].dtTime=(SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_EXPIRATION_TIME)+TimeCurrent())/2;
|
|
arrDealsSim[p].enType=DEAL_TYPE_BUY;
|
|
arrDealsSim[p].dbPrice=SymbolInfoDouble(inpNextPut,SYMBOL_OPTION_STRIKE)-SymbolPrice(inpNextPut,inpCalcMode);
|
|
arrDealsSim[p].lTicket=0;
|
|
strObjectName=StringFormat( "OptionSniffer #%.0f %s %.0f %s at %.4f",
|
|
arrDealsSim[p].lTicket,
|
|
StringSubstr(EnumToString((ENUM_DEAL_TYPE)arrDealsSim[p].enType),10,4),
|
|
arrDealsSim[p].dbVolume,
|
|
arrDealsSim[p].strSymbol,
|
|
arrDealsSim[p].dbPrice
|
|
);
|
|
// ObjectCreate(0,
|
|
// strObjectName+" EXPIRATION",
|
|
// OBJ_ARROW_STOP,
|
|
// 0,
|
|
// SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_EXPIRATION_TIME),
|
|
// SymbolInfoDouble(arrDealsSim[p].strSymbol,SYMBOL_OPTION_STRIKE)
|
|
// );ObjectSetInteger(0,strObjectName+" EXPIRATION",OBJPROP_COLOR,clrBlue);
|
|
// ObjectCreate(0,
|
|
// strObjectName+" OPC",
|
|
// OBJ_ARROW_UP,
|
|
// 0,
|
|
// arrDealsSim[p].dtTime,
|
|
// SymbolInfoDouble(arrDealsSim[p].strSymbol,SYMBOL_OPTION_STRIKE)-OptionPrice(arrDealsSim[p].strSymbol)
|
|
// ); ObjectSetInteger(0,strObjectName+" Model",OBJPROP_COLOR,clrBlue);
|
|
}
|
|
else
|
|
{
|
|
strComment+="Erro: "+inpNextPut+" não é uma opção de venda (PUT)!";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strComment+="Erro: "+inpNextPut+" não é Opção!";
|
|
}
|
|
}
|
|
else //-------------------------------------------------------Roll Put para o Symbol calculado
|
|
{
|
|
int iDealB=0;
|
|
for(int i=(ArraySize(arrDealsSim)-1);i>=0;i--)
|
|
{
|
|
if((SymbolInfoInteger(arrDealsSim[i].strSymbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_PUT)&&(arrDealsSim[i].enType==DEAL_TYPE_BUY))
|
|
{
|
|
iDealB=i;
|
|
break;
|
|
}
|
|
}
|
|
//PrintFormat("%s %.2f", arrDealsSim[iDealB].strSymbol, arrDealsSim[iDealB].dbPrice );
|
|
double dbCurrentExtrinsic=0.0;
|
|
ArrayResize(arrDealsSim,ArraySize(arrDealsSim)+1);
|
|
p=ArraySize(arrDealsSim);p--;
|
|
for(int i=(ArraySize(arrOptions)-1);i>=0;i--)
|
|
{
|
|
if(
|
|
nextOptionSerie(StringSubstr(arrDealsSim[iDealB].strSymbol,4,1))==StringSubstr(arrOptions[i].strSymbol,4,1)
|
|
&&((arrOptions[i].dbK-arrOptions[i].dbP)<arrDealsSim[iDealB].dbPrice)
|
|
//&&(dbCurrentExtrinsic<((arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?MathMax(0,arrOptions[i].dbC-MathMax(0,SymbolPrice(OptionBasis(arrOptions[i].strSymbol),inpCalcMode)-arrOptions[i].dbK)):MathMax(0,arrOptions[i].dbP-MathMax(0,arrOptions[i].dbK-SymbolPrice(OptionBasis(arrOptions[i].strSymbol),inpCalcMode))))))
|
|
&&(arrOptions[i].dbP>=SymbolPrice(arrDealsSim[iDealB].strSymbol,inpCalcMode))
|
|
//&&(SymbolInfoInteger(arrOptions[i].strSymbol,SYMBOL_EXPIRATION_TIME)<D'2022.12.31 23:00:00')
|
|
)
|
|
{
|
|
arrDealsSim[p].lPositionID=0;
|
|
arrDealsSim[p].strSymbol=arrOptions[i].strSymbol;
|
|
arrDealsSim[p].dbVolume=0.0;
|
|
arrDealsSim[p].dtTime=(SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_EXPIRATION_TIME)+TimeCurrent())/2;
|
|
arrDealsSim[p].enType=DEAL_TYPE_BUY;
|
|
arrDealsSim[p].dbPrice=SymbolInfoDouble(arrOptions[i].strSymbol,SYMBOL_OPTION_STRIKE)-arrOptions[i].dbP;
|
|
arrDealsSim[p].lTicket=0;
|
|
dbCurrentExtrinsic=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?MathMax(0,arrOptions[i].dbC-MathMax(0,SymbolPrice(OptionBasis(arrOptions[i].strSymbol),inpCalcMode)-arrOptions[i].dbK)):MathMax(0,arrOptions[i].dbP-MathMax(0,arrOptions[i].dbK-SymbolPrice(OptionBasis(arrOptions[i].strSymbol),inpCalcMode))));
|
|
}
|
|
}
|
|
}
|
|
strObjectName=StringFormat( "OptionSniffer #%.0f %s %.0f %s at %.4f",
|
|
arrDealsSim[p].lTicket,
|
|
StringSubstr(EnumToString((ENUM_DEAL_TYPE)arrDealsSim[p].enType),10,4),
|
|
arrDealsSim[p].dbVolume,
|
|
arrDealsSim[p].strSymbol,
|
|
arrDealsSim[p].dbPrice
|
|
);
|
|
ObjectCreate(0,strObjectName+" EXPIRATION",OBJ_ARROW_STOP,0,SymbolInfoInteger(arrDealsSim[p].strSymbol,SYMBOL_EXPIRATION_TIME),SymbolInfoDouble(arrDealsSim[p].strSymbol,SYMBOL_OPTION_STRIKE));
|
|
ObjectSetInteger(0,strObjectName+" EXPIRATION",OBJPROP_COLOR,clrRed);
|
|
ObjectCreate(0,strObjectName+" OPM",OBJ_ARROW_UP,0,arrDealsSim[p].dtTime,SymbolInfoDouble(arrDealsSim[p].strSymbol,SYMBOL_OPTION_STRIKE)-OptionPrice(arrDealsSim[p].strSymbol));ObjectSetInteger(0,strObjectName+" OPM",OBJPROP_COLOR,clrBlue);
|
|
strObjectName="";
|
|
ulong ulOrderTicket=0;
|
|
for(int i=0;i<ArraySize(arrDealsSim);i++)
|
|
{
|
|
strObjectName=StringFormat( "OptionSniffer #%.0f %s %.0f %s at %.4f",
|
|
arrDealsSim[i].lTicket,
|
|
StringSubstr(EnumToString((ENUM_DEAL_TYPE)arrDealsSim[i].enType),10,4),
|
|
arrDealsSim[i].dbVolume,
|
|
arrDealsSim[i].strSymbol,
|
|
arrDealsSim[i].dbPrice
|
|
);
|
|
ObjectCreate(0,
|
|
strObjectName,
|
|
(arrDealsSim[i].enType==DEAL_TYPE_BUY?OBJ_ARROW_BUY:OBJ_ARROW_SELL),
|
|
0,
|
|
arrDealsSim[i].dtTime,
|
|
arrDealsSim[i].dbPrice
|
|
);
|
|
for(int j=0;j<PositionsTotal();j++)
|
|
{
|
|
ulPositionTicket=PositionGetTicket(j);
|
|
if(PositionGetSymbol(j)==arrDealsSim[i].strSymbol)
|
|
{
|
|
if(SymbolInfoInteger(arrDealsSim[i].strSymbol,SYMBOL_EXPIRATION_TIME)>0)
|
|
{
|
|
ObjectCreate(0,
|
|
strObjectName+" EXPIRATION",
|
|
OBJ_ARROW_STOP,
|
|
0,
|
|
SymbolInfoInteger(arrDealsSim[i].strSymbol,SYMBOL_EXPIRATION_TIME),
|
|
SymbolInfoDouble(arrDealsSim[i].strSymbol,SYMBOL_OPTION_STRIKE)
|
|
);ObjectSetInteger(0,strObjectName+" EXPIRATION",OBJPROP_COLOR,(SymbolInfoInteger(PositionGetString(POSITION_SYMBOL),SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?clrBlue:clrRed));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
int iDeal=1;
|
|
stcDeal lastDealOption; ZeroMemory(lastDealOption);
|
|
strComment+=" Quem Vende a CALL TEM o DEVER de VENDER a ação no vencimento pelo STRIKE.\n";
|
|
for(int i=0;i<ArraySize(arrDealsSim);i++)
|
|
{
|
|
if(SymbolInfoDouble(arrDealsSim[i].strSymbol,SYMBOL_OPTION_STRIKE)>0.0)
|
|
if(SymbolInfoInteger(arrDealsSim[i].strSymbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL)
|
|
{
|
|
strComment+=StringFormat("%3.0f. %s %4s %s %.4f",
|
|
iDeal++,
|
|
arrDealsSim[i].strSymbol,
|
|
StringSubstr(EnumToString((ENUM_DEAL_TYPE)arrDealsSim[i].enType),10,5),
|
|
(arrDealsSim[i].dbVolume>0.0?TimeToString(arrDealsSim[i].dtTime,TIME_DATE|TIME_MINUTES|TIME_SECONDS):(arrDealsSim[i].dbVolume<0.0?" ORDER ":" SIMULATED ")),
|
|
arrDealsSim[i].dbPrice
|
|
);
|
|
if(i>0)
|
|
{
|
|
if(lastDealOption.dbPrice>0.0)
|
|
{
|
|
strObjectName=StringFormat( "OptionSniffer #%.0f %s %.0f %s at %.4f Trend",
|
|
arrDealsSim[i].lTicket,
|
|
StringSubstr(EnumToString((ENUM_DEAL_TYPE)arrDealsSim[i].enType),10,4),
|
|
arrDealsSim[i].dbVolume,
|
|
arrDealsSim[i].strSymbol,
|
|
arrDealsSim[i].dbPrice
|
|
);
|
|
ObjectCreate(
|
|
0,
|
|
strObjectName,
|
|
OBJ_TREND,
|
|
0,
|
|
arrDealsSim[i].dtTime, arrDealsSim[i].dbPrice,
|
|
lastDealOption.dtTime, lastDealOption.dbPrice
|
|
);
|
|
ObjectSetInteger(0,strObjectName,OBJPROP_STYLE,STYLE_DOT);ObjectSetInteger(0,strObjectName,OBJPROP_COLOR,clrDarkSlateGray);
|
|
if(arrDealsSim[i].enType==DEAL_TYPE_BUY)strComment+=StringFormat(" %.4f %.2f%%", lastDealOption.dbPrice-arrDealsSim[i].dbPrice, 100*(lastDealOption.dbPrice-arrDealsSim[i].dbPrice)/(2/(1/lastDealOption.dbPrice+1/arrDealsSim[i].dbPrice)) );
|
|
if(arrDealsSim[i].enType==DEAL_TYPE_BUY)
|
|
{
|
|
ObjectCreate(0,strObjectName+" Yield",OBJ_TEXT,0,(arrDealsSim[i].dtTime+lastDealOption.dtTime)/2,(arrDealsSim[i].dbPrice+lastDealOption.dbPrice)/2);
|
|
ObjectSetString(0,strObjectName+" Yield",OBJPROP_TEXT, StringFormat("%.2f%%",100*(lastDealOption.dbPrice-arrDealsSim[i].dbPrice)/(2/(1/lastDealOption.dbPrice+1/arrDealsSim[i].dbPrice))));
|
|
ObjectSetInteger(0,strObjectName+" Yield",OBJPROP_FONTSIZE,8);
|
|
ObjectSetInteger(0,strObjectName+" Yield",OBJPROP_ANCHOR,ANCHOR_CENTER);
|
|
ObjectSetInteger(0,strObjectName+" Yield",OBJPROP_COLOR,(lastDealOption.dbPrice>arrDealsSim[i].dbPrice?clrLime:clrRed));
|
|
}
|
|
}
|
|
}
|
|
lastDealOption=arrDealsSim[i];
|
|
strComment+="\n";
|
|
}
|
|
}
|
|
iDeal=1;
|
|
ZeroMemory(lastDealOption);
|
|
strComment+="\n";
|
|
strComment+=" Quem Vende a PUT TEM o DEVER de COMPRAR a ação no vencimento pelo STRIKE.\n";
|
|
for(int i=0;i<ArraySize(arrDealsSim);i++)
|
|
{
|
|
if(SymbolInfoDouble(arrDealsSim[i].strSymbol,SYMBOL_OPTION_STRIKE)>0.0)
|
|
if(SymbolInfoInteger(arrDealsSim[i].strSymbol,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_PUT)
|
|
{
|
|
strComment+=StringFormat("%3.0f. %s %4s %s %.4f",
|
|
iDeal++,
|
|
arrDealsSim[i].strSymbol,
|
|
StringSubstr(EnumToString((ENUM_DEAL_TYPE)arrDealsSim[i].enType),10,5),
|
|
(arrDealsSim[i].dbVolume>0.0?TimeToString(arrDealsSim[i].dtTime,TIME_DATE|TIME_MINUTES|TIME_SECONDS):" SIMULATED "),
|
|
arrDealsSim[i].dbPrice
|
|
);
|
|
if(i>0)
|
|
{
|
|
if(lastDealOption.dbPrice>0.0)
|
|
{
|
|
strObjectName=StringFormat( "OptionSniffer #%.0f %s %.0f %s at %.4f Trend",
|
|
arrDealsSim[i].lTicket,
|
|
StringSubstr(EnumToString((ENUM_DEAL_TYPE)arrDealsSim[i].enType),10,4),
|
|
arrDealsSim[i].dbVolume,
|
|
arrDealsSim[i].strSymbol,
|
|
arrDealsSim[i].dbPrice
|
|
);
|
|
ObjectCreate(
|
|
0,
|
|
strObjectName,
|
|
OBJ_TREND,
|
|
0,
|
|
arrDealsSim[i].dtTime, arrDealsSim[i].dbPrice,
|
|
lastDealOption.dtTime, lastDealOption.dbPrice
|
|
);
|
|
if(arrDealsSim[i].enType==DEAL_TYPE_BUY)strComment+=StringFormat(" %.4f %.2f%%", lastDealOption.dbPrice-arrDealsSim[i].dbPrice, 100*(lastDealOption.dbPrice-arrDealsSim[i].dbPrice)/(2/(1/lastDealOption.dbPrice+1/arrDealsSim[i].dbPrice)) );
|
|
if((arrDealsSim[i].enType==DEAL_TYPE_BUY)&&(iDeal>3))
|
|
{
|
|
ObjectCreate(0,strObjectName+" Yield",OBJ_TEXT,0,(arrDealsSim[i].dtTime+lastDealOption.dtTime)/2,(arrDealsSim[i].dbPrice+lastDealOption.dbPrice)/2);
|
|
ObjectSetString(0,strObjectName+" Yield",OBJPROP_TEXT, StringFormat("%.2f%%",100*(lastDealOption.dbPrice-arrDealsSim[i].dbPrice)/(2/(1/lastDealOption.dbPrice+1/arrDealsSim[i].dbPrice))));
|
|
ObjectSetInteger(0,strObjectName+" Yield",OBJPROP_FONTSIZE,8);
|
|
ObjectSetInteger(0,strObjectName+" Yield",OBJPROP_ANCHOR,ANCHOR_CENTER);
|
|
ObjectSetInteger(0,strObjectName+" Yield",OBJPROP_COLOR,(lastDealOption.dbPrice>arrDealsSim[i].dbPrice?clrLime:clrRed));
|
|
}
|
|
ObjectSetInteger(0,strObjectName,OBJPROP_STYLE,STYLE_DOT);ObjectSetInteger(0,strObjectName,OBJPROP_COLOR,clrDarkSlateGray);
|
|
}
|
|
}
|
|
lastDealOption=arrDealsSim[i];
|
|
strComment+="\n";
|
|
}
|
|
}
|
|
ObjectCreate(0,"OptionSniffer_SymbolPrice",OBJ_HLINE,0,0,SymbolPrice(_Symbol,inpCalcMode));ObjectSetInteger(0,"OptionSniffer_SymbolPrice",OBJPROP_STYLE,STYLE_DOT);ObjectSetInteger(0,"OptionSniffer_SymbolPrice",OBJPROP_COLOR,clrYellow);
|
|
dbS=SymbolPrice(_Symbol,inpCalcMode);
|
|
strFile="Symbol;S;K;C/P;B;E;Y;y;Mod\n";
|
|
for(int i=0;i<ArraySize(arrOptions);i++)
|
|
{
|
|
dbE=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?MathMax(0,arrOptions[i].dbC-MathMax(0,SymbolPrice(OptionBasis(_Symbol),inpCalcMode)-arrOptions[i].dbK)):MathMax(0,arrOptions[i].dbP-MathMax(0,arrOptions[i].dbK-SymbolPrice(OptionBasis(_Symbol),inpCalcMode))));
|
|
//dbY=dbE/(2/(1/(dbS+dbE)+1/dbS));
|
|
dbY=(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbC:arrOptions[i].dbP)/arrOptions[i].dbK;
|
|
dbYperMonth=dbY/lDaysToExpirate*30;
|
|
strFile+=StringFormat("%s;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f;%.2f\n",
|
|
arrOptions[i].strSymbol,
|
|
dbS,
|
|
arrOptions[i].dbK,
|
|
(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbC:arrOptions[i].dbP),
|
|
(arrOptions[i].enRight==SYMBOL_OPTION_RIGHT_CALL?arrOptions[i].dbK+arrOptions[i].dbC:arrOptions[i].dbK-arrOptions[i].dbP),
|
|
dbE,
|
|
100*dbY,
|
|
100*dbYperMonth,
|
|
OptionPrice(arrOptions[i].strSymbol)
|
|
);
|
|
}
|
|
}
|
|
StringReplace(strFile,".",",");
|
|
string strFileName=_Symbol+".csv";
|
|
int iHandle=FileOpen(strFileName,FILE_WRITE|FILE_CSV);
|
|
FileWrite(iHandle,strFile);
|
|
FileClose(iHandle);
|
|
Comment(strStatus+"\n"+strComment);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 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(_Symbol, 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(_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)<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);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Próxima séria da Opção |
|
|
//+------------------------------------------------------------------+
|
|
string nextOptionSerie(const string strSymbol)
|
|
{
|
|
string strReturn="";
|
|
string strCall="ABCDEFGHIJKL";
|
|
string strPut="MNOPQRSTUVWX";
|
|
string strSerieSymbol=strSymbol;
|
|
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;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 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;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| 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)));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Preço da Última Operação |
|
|
//+------------------------------------------------------------------+
|
|
double lastDealPrice(const string strSymbol)
|
|
{
|
|
HistorySelect(0,TimeCurrent());
|
|
double dbPriceReturn=0.0;
|
|
ulong ulDealTicket;
|
|
for(int i=(HistoryDealsTotal()-1);i>=0;i--)
|
|
{
|
|
ulDealTicket=HistoryDealGetTicket(i);
|
|
dbPriceReturn=HistoryDealGetDouble(ulDealTicket,DEAL_PRICE);
|
|
if(HistoryDealGetString(ulDealTicket,DEAL_SYMBOL)==strSymbol) return(dbPriceReturn);
|
|
}
|
|
return(NULL);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Preço da Opção por A.A. |
|
|
//+------------------------------------------------------------------+
|
|
double OptionPrice(const string strOption)
|
|
{
|
|
double z=(SymbolInfoInteger(strOption,SYMBOL_OPTION_RIGHT)==SYMBOL_OPTION_RIGHT_CALL?1.0:-1.0);
|
|
long t=(SymbolInfoInteger(strOption,SYMBOL_EXPIRATION_TIME)-TimeCurrent())/86400;
|
|
long t0=(inpExpirationDI-TimeCurrent())/86400;
|
|
double r=inpr_0/100;
|
|
double S=SymbolPrice(OptionBasis(strOption),inpCalcMode);
|
|
double K=SymbolInfoDouble(strOption,SYMBOL_OPTION_STRIKE);
|
|
return double (1.0/2.0)*MathSqrt(MathPow(K-S,2.0)+4.0*(double)t/(double)t0*MathPow(r*S,2.0))-z*(K-S)/2.0;
|
|
}
|
|
//+------------------------------------------------------------------+ |