//+------------------------------------------------------------------+ //| 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 #include //+------------------------------------------------------------------+ //| 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 //+------------------------------------------------------------------+