Gamma-Exposure-MQL5/Include/GEX/GexProviderNative.mqh

132 lines
5.6 KiB
MQL5
Raw Permalink Normal View History

2026-07-07 19:24:01 +00:00
//+------------------------------------------------------------------+
//| 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
//+------------------------------------------------------------------+