hi, I did very little research into this. I have an IQFeed subscription for Futures and Futures options. Can anyone point me to how I get the "Futures Option Chains" inside Amibroker? Just want to play a bit with it. I do not have a subscription for "Equity options" but "Futures options" I guess are similar to play with. I have been playing a bit with "options max pain". There is Python code for that which uses yahoo data. But this data does not seem very reliable since my results are all over the place and quite different from sites like https://maximum-pain.com/. So I want to play a bit with "Futures Option Chains" inside Amibroker before I decide to possibly add the "Equity Options" subscription.
Yes.
Only difference is that Equity Options settle on the Spot price, however, Future Options settle on Futures price.
Future contract's price would be same as underlying's Spot price upon Future contract's expiry.
So, if you are dealing on Options derived from Futures and those Options also expire on the same day in which the Futures does, then technically there is no difference between the Futures and Equity options. The option price difference that appears before expiry is just to factor in cost-of-carry (intrinsic to Futures, not considered in cash Equity); but price movements would be in absolute tandem (provided good liquidity persist).
Using Exploration one may replicate any type of Option Chain, which then could be set on Auto-Repeat considering live-feed updates.
Hint: addrow in Exploration provides utmost customization possibilities.
thanks, yes maybe I could use exploration although I think IQFeed has code for the "option chain" in the API. At least the CSharp plugin seems to suggest that: https://github.com/mathpaquette/IQFeed.CSharpApiClient/search?q=option+chain
But I am first trying to get the data using Python and Yahoo and play with that first. I was getting strange results with the Yahoo data but maybe the Python code I used was no longer valid.
You mentioned that you've "Future Options" subscription from IQFeed.
So using IQFeed symbols in AFL and consequently AmiPy, you may pass values onto the functions of your Python code that would return calculations back to AFL for further consumption.
There is no need for Yahoo in this, if I am not missing anything else!
yes true but I have not worked on getting the option chain using the csharp plugin yet. Still have to figure out the code for that. So getting the data using Yahoo will get me started. If I manage to get the data using Yahoo then the next step will be using the csharp plugin. Getting the option chains with yahoo is pretty easy, see for example:
# python medium_options.py
import yfinance as yf
#import pandas as pd
#import numpy as np
#import datetime
def main():
symbol = input("Enter the Symbol: " )
osym = yf.Ticker(symbol)
op = osym.options
idx = 0
print(op[idx])
opc = osym.option_chain(op[idx])
print(opc.calls)
print(opc.puts)
if __name__ == "__main__":
main()
Hmm... so, the real problem is IQFeed's AB plugin does not push "Option Chain" data in one go.
Yahoo data is out of question taking its worth into account!
Instead of re-writing IQFeed's AB plugin from scratch to pull all "Option Chain" data then asynchronously distribute onto individual Option symbol in AB's DB, there are two easier alternatives that I can think of:
-
Each Option contract comes up with its respective symbol, how about constructing Option Chain in Explore AFL itself. We could begin with 40 symbols = 20 call + 20 put, where 20 = 5 ITM + 15 OTM on each side. Pass values to python using AmiPy and capture result of desired calculations.
-
Subscribe to IQFeed API, using Python play with those APIs to hit all Option Chain data in one go for the desired calculation and revert back to AFL with it.
Because even if you rewrite the plugin for Option Chain, each Option contract is required to be stored onto respective Option symbol in AB; that would anyways be again required to be consolidated in AFL for any meaningful usage. Then why take long haul?
Hmm... so, the real problem is IQFeed's AB plugin does not push "Option Chain" data in one go.
yes that was my original question. I do not know if the AB plugin has this built in or not. If not then I can possibly use the csharp plugin.
But working with yahoo first since I have not even yet started to get similar charts these guys get: https://maximum-pain.com/options/QQQ or https://swaggystocks.com/dashboard/options-max-pain/QQQ
Once my calculations are correct I will see how I get the data from IQFeed. Thanks for the suggestions!
i am getting strange results with Yahoo because their OI data is just wrong. See for instance:
https://finance.yahoo.com/quote/UNG/options?p=UNG
these 2 sites seem to have better data:
https://www.marketwatch.com/investing/fund/ung/options?mod=mw_quote_tab
https://www.optionistics.com/quotes/stock-option-chains/UNG
sent them an email and whatdoyouknow it is working again. Maybe thanks to me?
hi, I am working on some test code. I get the "Futures Options Chain" symbol list using the csharp plugin. Then I import this symbol list inside Amibroker. See code below. The Futures Option Data looks correct. However, the OI (open interest) data is not there. I tried using:
Title = "OI: " + GetRTData( "OpenInt" );
but it shows 0 for a specific symbol QNGK23C2100. But I know this value should be 377 since this is what IQFeed is showing in their App, see figure. How do I get this OI data?
Thank you
The code is below:
AFL code:
// option chain
pythonpath = "C:\\Users\\win 10\\AppData\\Local\\Programs\\Python\\Python38\\mypython\\iqfeed\\";
pythonfile = "iqfeedcsharpGetFuturesOptionChainAFL.py";
PyLoadFromFile( "fo1", pythonpath + pythonfile );
futuresOptionsChain = "";
//getFuturesOptionsChainTrigger = ParamTrigger( "Get Futures Options Chain", "Press Here" );
addFuturesOptionsChainSymbolTrigger = ParamTrigger( "Add Futures Options Chain Symbols", "Press Here" );
/*
// first run this
if( getFuturesOptionsChainTrigger )
{
futuresOptionsChain = PyEvalFunction( "fo1", "getFuturesOptionsChainSymbols", "QNG", "K", "23" );
StaticVarSetText( "futuresOptionsChain", futuresOptionsChain );
Say( "finished" );
}
"futuresOptionChain: " + StaticVarGetText( "futuresOptionChain" );
*/
if( addFuturesOptionsChainSymbolTrigger )
{
// get the Futures Option Chain
futuresOptionsChain = PyEvalFunction( "fo1", "getFuturesOptionsChainSymbols", "QNG", "K", "23" );
StaticVarSetText( "futuresOptionsChain", futuresOptionsChain );
// add the futures option symbols to Amibroker
AB = CreateObject( "Broker.Application" );
stks = AB.Stocks;
SymList = StaticVarGetText( "futuresOptionsChain" );
for( i = 0; ( symbol = StrExtract( SymList, i ) ) != ""; i++ )
{
stks.Add( symbol );
_TRACE( "" + symbol );
}
AB.RefreshAll();
Say( "finished" );
}
SetChartOptions( 1, chartShowDates, chartGridMiddle, 0, 0, 0 );
Plot( C, "Close", ColorRGB( 3, 157, 252 ), styleDots, Null, Null, 0, 0, 1 );
Title = "OI: " + GetRTData( "OpenInt" );
PYTHON code:
# https://github.com/mathpaquette/IQFeed.CSharpApiClient/blob/master/docs/USING-WITH-PYTHON.md
# Dynamically add IQFeed.CSharpApiClient DLL
# assembly_path = r"C:\<folder>"
# filename: iqfeedcsharpGetFuturesOptionChainAFL.py
if '__' + __file__ + '_initialized' not in globals():
globals()['__' + __file__ + '_initialized'] = True
assembly_path = r'C:/Program Files/AmiBroker/IQFeedCSharpApiClient'
import sys
sys.path.append(assembly_path)
# Reference IQFeed.CSharpApiClient DLL
import clr
clr.AddReference("IQFeed.CSharpApiClient")
from IQFeed.CSharpApiClient.Lookup import LookupClientFactory
from IQFeed.CSharpApiClient.Lookup.Chains import OptionSideFilterType
import AmiPy
def getFuturesOptionsChainSymbols(f_symbol, o_month, o_year):
'''
Month codes:
January: F
February: G
March: H
April: J
May: K
June: M
July: N
August: Q
September: U
October: V
November: X
December: Z
'''
f_symbol = str(f_symbol)
o_month = str(o_month)
o_year = str(o_year)
lookupClient = LookupClientFactory.CreateNew()
lookupClient.Connect()
try:
# symtax Symbol (QNG), OptionSideFilterType.CP, Month Code, Year Code
ticks = lookupClient.Chains.GetChainFutureOption( f_symbol, OptionSideFilterType.CP, o_month, o_year )
symbollist = ''
for tt in ticks:
symbollist += tt.Symbol
symbollist += ','
# remove last comma
symbollist = symbollist[:len(symbollist)-1]
AmiPy.Print(symbollist)
except Exception as err:
AmiPy.Print(f"Unexpected {err=}, {type(err)=}")
return symbollist
no I was wrong. The Open Interest is indeed available. I just needed to open a database in the daily timeframe. I was using the 5-min TF. All is therefore working fine.
for anyone interested and subscribed to IQFeed Futures and Futures options I have some code below. I decided for now not to do it inside Amibroker because it is a nightmare to download all the symbols. But this code is also very slow. The culprit is that I need to get the Open Interest for all symbols. This is very slow for some reason. Code below. It gives max pain for NQ 12800 and ES 4050 for the June expiration. That is not much lower but slightly lower target from here
Chart for ES June
# filesname: charpHistoricalDataTest.py
# to run code IQFeed should be launched already
# to run code type in the CMD window (path to python should be known)
# python charpHistoricalDataTest.py
# Dynamically add IQFeed.CSharpApiClient DLL
import sys
import clr
assembly_path = r'C:/Program Files/AmiBroker/IQFeedCSharpApiClient'
sys.path.append(assembly_path)
clr.AddReference("IQFeed.CSharpApiClient")
from System import DateTime
from datetime import datetime, timedelta
from IQFeed.CSharpApiClient.Lookup import LookupClientFactory
from IQFeed.CSharpApiClient.Lookup.Chains import OptionSideFilterType
import pandas as pd
import matplotlib.pyplot as plt
lookupClient = LookupClientFactory.CreateNew()
lookupClient.Connect()
def total_loss_at_strike(chainC, chainP, expiry_price):
"""Calculate loss at strike price"""
# All call options with strike price below the expiry price will result in loss for option writers
in_money_calls = chainC[chainC['Strike'] < expiry_price][["OpenInterest", "Strike"]]
in_money_calls["CE loss"] = (expiry_price - in_money_calls['Strike'])*in_money_calls["OpenInterest"]
# All put options with strike price above the expiry price will result in loss for option writers
in_money_puts = chainP[chainP['Strike'] > expiry_price][["OpenInterest", "Strike"]]
in_money_puts["PE loss"] = (in_money_puts['Strike'] - expiry_price)*in_money_puts["OpenInterest"]
total_loss = in_money_calls["CE loss"].sum() + in_money_puts["PE loss"].sum()
return total_loss
def createListofSymbolPlusStrikePrice(f_symbol, o_month, o_year, o_side):
'''
Month codes:
January: F
February: G
March: H
April: J
May: K
June: M
July: N
August: Q
September: U
October: V
November: X
December: Z
'''
f_symbol = str(f_symbol)
o_month = str(o_month)
o_year = str(o_year)
if( o_side == 1):
side = OptionSideFilterType.C
if( o_side == 2):
side = OptionSideFilterType.P
try:
ticks = lookupClient.Chains.GetChainFutureOption( f_symbol, side, o_month, o_year )
symbollist = ''
strikepricelist = ''
for tt in ticks:
symbollist += str(tt.Symbol)
symbollist += ','
strikepricelist += str(tt.StrikePrice)
strikepricelist += ','
# remove last comma
symbollist = symbollist[:len(symbollist)-1]
except Exception as err:
print(f"Unexpected {err=}, {type(err)=}")
return symbollist,strikepricelist
def getOpenInterestdata( sym, d1, d2 ):
sym = str(sym)
#ticks = lookupClient.Historical.GetHistoryDailyDatapoints(sym, 1)
ticks = lookupClient.Historical.GetHistoryDailyTimeframe(sym, d1, d2)
for tick in ticks:
s = str(tick)
datalist = s.split(',')
#print(int(str(datalist[6]).replace(' OpenInterest: ','')))
oi = int(str(datalist[6]).replace(' OpenInterest: ',''))
return oi
def main():
f_symbol = '@ES'
o_month = 'M'
o_year = '23'
yesterday = datetime.now() - timedelta(1)
d1 = DateTime(yesterday.year,yesterday.month,yesterday.day)
d2 = DateTime(yesterday.year,yesterday.month,yesterday.day)
# Calls
symbollist,strikepricelist = createListofSymbolPlusStrikePrice(f_symbol, o_month, o_year, 1)
list1 = symbollist.split(',')
list2 = strikepricelist.split(',')
df_calls = pd.DataFrame(list(zip(list1, list2)), columns =['Symbols', 'Strike'])
df_calls = df_calls.astype({'Strike':'float'})
df_calls = df_calls.sort_values(by=['Strike'])
df_calls['OpenInterest'] = 0
idx = 0
for index, row in df_calls.iterrows():
sym = row['Symbols']
oi = getOpenInterestdata( sym, d1, d2 )
df_calls.iloc[idx, 2] = oi
#print(idx,row['Strike'],row['Symbols'],oi)
idx += 1
print(df_calls)
# Puts
symbollist,strikepricelist = createListofSymbolPlusStrikePrice(f_symbol, o_month, o_year, 2)
list1 = symbollist.split(',')
list2 = strikepricelist.split(',')
df_puts = pd.DataFrame(list(zip(list1, list2)), columns =['Symbols', 'Strike'])
df_puts = df_puts.astype({'Strike':'float'})
df_puts = df_puts.sort_values(by=['Strike'])
df_puts['OpenInterest'] = 0
idx = 0
for index, row in df_puts.iterrows():
sym = row['Symbols']
oi = getOpenInterestdata( sym, d1, d2 )
df_puts.iloc[idx, 2] = oi
#print(idx,row['Strike'],row['Symbols'],oi)
idx += 1
print(df_puts)
strikes = list(df_calls['Strike'])
losses = [total_loss_at_strike(df_calls, df_puts, strike)/1000000 for strike in strikes]
m = losses.index(min(losses))
print("Max pain > {}".format(strikes[m]))
plt.plot(strikes, losses)
plt.ylabel('Total loss in (Millon)')
plt.show()
if __name__ == "__main__":
main()
This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.