132 lines
5.6 KiB
MQL5
132 lines
5.6 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| GexProviderNative.mqh |
| |||
//| MMQ — Muhammad Minhas Qamar |
| |||
//| www.mql5.com/en/articles/23410 |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "MMQ — Muhammad Minhas Qamar"
| |||
#property link "https://www.mql5.com/en/articles/23410"
| |||
#property version "1.00"
| |||
| |||
#ifndef GEX_GEXPROVIDERNATIVE_MQH
| |||
#define GEX_GEXPROVIDERNATIVE_MQH
| |||
| |||
#include <GEX/BlackScholes.mqh>
| |||
#include <GEX/GexData.mqh>
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Reads the broker's native MetaTrader 5 option symbols and |
| |||
//| produces the same OptionQuote list the CSV provider does. The |
| |||
//| one field GEX needs beyond the surface is open interest, which |
| |||
//| the terminal exposes as SYMBOL_SESSION_INTEREST. |
| |||
//+------------------------------------------------------------------+
| |||
class CGexProviderNative
| |||
{
| |||
public:
| |||
//--- underlying: base symbol (e.g. "SP" / "GAZP"); rate: risk-free
| |||
bool Load(const string underlying, const double rate, OptionQuote &out[]);
| |||
| |||
private:
| |||
bool IsOption(const string sym) const;
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| A symbol is an option if the server reports an option right for |
| |||
//| it. Brokers that do not support options return an error / zero |
| |||
//| here, so this doubles as the "options available?" test. |
| |||
//+------------------------------------------------------------------+
| |||
bool CGexProviderNative::IsOption(const string sym) const
| |||
{
| |||
long right = 0;
| |||
if(!SymbolInfoInteger(sym, SYMBOL_OPTION_RIGHT, right))
| |||
return(false);
| |||
//--- options carry a valid strike; use it as a second confirmation
| |||
double strike = SymbolInfoDouble(sym, SYMBOL_OPTION_STRIKE);
| |||
return(strike > 0.0);
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Enumerate all symbols, collect the options on the requested |
| |||
//| underlying, and build one OptionQuote per contract, reading the |
| |||
//| open interest that turns a contract's gamma into a real dealer |
| |||
//| exposure. Contracts whose open interest is zero contribute |
| |||
//| nothing to the profile and are dropped by the aggregation later, |
| |||
//| so we keep them here only for completeness of the diagnostic. |
| |||
//+------------------------------------------------------------------+
| |||
bool CGexProviderNative::Load(const string underlying, const double rate, OptionQuote &out[])
| |||
{
| |||
ArrayResize(out, 0);
| |||
int total = SymbolsTotal(false); // false = all symbols, not just Market Watch
| |||
if(total <= 0)
| |||
{
| |||
Print("CGexProviderNative: no symbols available");
| |||
return(false);
| |||
}
| |||
| |||
double spot = SymbolInfoDouble(underlying, SYMBOL_BID);
| |||
if(spot <= 0.0)
| |||
spot = SymbolInfoDouble(underlying, SYMBOL_LAST);
| |||
| |||
datetime now = TimeCurrent();
| |||
if(now == 0)
| |||
now = TimeLocal();
| |||
int found = 0, withServerIV = 0, withOI = 0;
| |||
| |||
for(int i = 0; i < total; i++)
| |||
{
| |||
string sym = SymbolName(i, false);
| |||
if(!IsOption(sym))
| |||
continue;
| |||
| |||
//--- match the underlying via SYMBOL_BASIS (the derivative's base asset)
| |||
string basis = SymbolInfoString(sym, SYMBOL_BASIS);
| |||
if(basis != underlying)
| |||
continue;
| |||
| |||
//--- make sure the symbol's quotes are available
| |||
SymbolSelect(sym, true);
| |||
| |||
OptionQuote q;
| |||
long right = (long)SymbolInfoInteger(sym, SYMBOL_OPTION_RIGHT);
| |||
q.right = (right == SYMBOL_OPTION_RIGHT_PUT) ? OPT_PUT : OPT_CALL;
| |||
q.strike = SymbolInfoDouble(sym, SYMBOL_OPTION_STRIKE);
| |||
q.expiry = (datetime)SymbolInfoInteger(sym, SYMBOL_EXPIRATION_TIME);
| |||
q.spot = spot;
| |||
q.rate = rate;
| |||
| |||
//--- open interest: the number of outstanding contracts at this strike
| |||
//--- (SYMBOL_SESSION_INTEREST is a double property, not an integer one)
| |||
q.open_interest = SymbolInfoDouble(sym, SYMBOL_SESSION_INTEREST);
| |||
if(q.open_interest > 0.0)
| |||
withOI++;
| |||
| |||
//--- option mid price from the current bid/ask
| |||
double bid = SymbolInfoDouble(sym, SYMBOL_BID);
| |||
double ask = SymbolInfoDouble(sym, SYMBOL_ASK);
| |||
q.price = (bid > 0.0 && ask > 0.0) ? 0.5 * (bid + ask) : SymbolInfoDouble(sym, SYMBOL_LAST);
| |||
| |||
//--- prefer the server-computed implied volatility if present
| |||
double serverVol = SymbolInfoDouble(sym, SYMBOL_PRICE_VOLATILITY);
| |||
double T = (double)(q.expiry - now) / (365.0 * 24 * 3600);
| |||
if(serverVol > 0.0)
| |||
{ q.iv = serverVol / 100.0; withServerIV++; } // property is a percentage
| |||
else
| |||
q.iv = ImpliedVol(q.right, q.price, q.spot, q.strike, q.rate, 0.0, T);
| |||
| |||
int s = ArraySize(out);
| |||
ArrayResize(out, s + 1);
| |||
out[s] = q;
| |||
found++;
| |||
}
| |||
| |||
PrintFormat("CGexProviderNative: underlying=%s spot=%.4f options found=%d server IV on %d with OI %d",
| |||
underlying, spot, found, withServerIV, withOI);
| |||
if(found == 0)
| |||
Print("CGexProviderNative: no option symbols matched. This account/broker may not offer options, or the underlying name differs from SYMBOL_BASIS.");
| |||
else
| |||
if(withOI == 0)
| |||
Print("CGexProviderNative: options matched but none carry open interest; GEX needs OI, so the profile will be empty. Use the CSV source to demonstrate the tool.");
| |||
return(found > 0);
| |||
}
| |||
| |||
#endif // GEX_GEXPROVIDERNATIVE_MQH
| |||
//+------------------------------------------------------------------+
|