Initial commit of IBKR client
This commit is contained in:
commit
9ddfbb11ae
52
client.py
Normal file
52
client.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from exchange import SMART
|
||||||
|
from ib_insync import IB, Index, Option
|
||||||
|
from ib_insync.util import isNan
|
||||||
|
from market_data_type import LIVE
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
|
class Client:
|
||||||
|
|
||||||
|
def __init__(self, host: str, port: int, client_id = 1) -> None:
|
||||||
|
self.ib = IB()
|
||||||
|
self.ib.connect(host, port, clientId = client_id)
|
||||||
|
self.ib.reqMarketDataType(LIVE)
|
||||||
|
|
||||||
|
def get_ticker(self, symbol: str, exchange: str):
|
||||||
|
underlying = Index(symbol, exchange)
|
||||||
|
self.ib.qualifyContracts(underlying)
|
||||||
|
return self.ib.reqTickers(underlying)[0]
|
||||||
|
|
||||||
|
def get_option_chain(self, symbol: str, expiration: datetime, sub_symbol: str = None,
|
||||||
|
contract_filter: Callable = None) -> pd.DataFrame:
|
||||||
|
expiration_date = expiration.strftime('%Y%m%d')
|
||||||
|
contract_details = self.ib.reqContractDetails(Option(symbol, expiration_date, exchange = SMART))
|
||||||
|
contracts = [_.contract for _ in contract_details if not sub_symbol or _.contract.tradingClass == sub_symbol]
|
||||||
|
if contract_filter:
|
||||||
|
contracts = list(filter(contract_filter, contracts))
|
||||||
|
|
||||||
|
option_data = []
|
||||||
|
for contract in contracts:
|
||||||
|
option = Option(symbol, expiration_date, contract.strike, contract.right, exchange = SMART, currency = 'USD')
|
||||||
|
if sub_symbol:
|
||||||
|
option.tradingClass = sub_symbol
|
||||||
|
option_data_snapshot = self.ib.reqMktData(option, '', True, False)
|
||||||
|
option_data.append(option_data_snapshot)
|
||||||
|
|
||||||
|
option_chain = []
|
||||||
|
for option in option_data:
|
||||||
|
print('Processing Option: ', option)
|
||||||
|
while isNan(option.bid) or isNan(option.ask) or option.modelGreeks is None:
|
||||||
|
# TODO: Add a timeout?
|
||||||
|
self.ib.sleep()
|
||||||
|
option_chain.append({
|
||||||
|
'Strike': option.contract.strike,
|
||||||
|
'Type': option.contract.right, # 'C' for Call, 'P' for Put.
|
||||||
|
'Bid': option.bid,
|
||||||
|
'Ask': option.ask,
|
||||||
|
'Delta': option.modelGreeks.delta,
|
||||||
|
})
|
||||||
|
|
||||||
|
return pd.DataFrame(option_chain)
|
1
exchange.py
Normal file
1
exchange.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
SMART = 'SMART'
|
1
market_data_type.py
Normal file
1
market_data_type.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
LIVE = 1
|
18
option_chain_example.py
Normal file
18
option_chain_example.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
from client import Client
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
ibkr_client = Client(host = '127.0.0.1', port = 7497)
|
||||||
|
|
||||||
|
underlying_ticker = ibkr_client.get_ticker('SPX', 'CBOE')
|
||||||
|
current_price = underlying_ticker.last
|
||||||
|
|
||||||
|
# Filtering strikes based on distance from current price speeds up the request.
|
||||||
|
max_strike_distance = 100
|
||||||
|
def contract_filter(contract):
|
||||||
|
if contract.right == 'C':
|
||||||
|
return contract.strike <= (current_price + max_strike_distance) and contract.strike >= current_price
|
||||||
|
return contract.strike <= current_price and contract.strike >= (current_price - max_strike_distance)
|
||||||
|
|
||||||
|
# The weekly symbol for SPX (SPXW) is required in order to distinguish from monthly options.
|
||||||
|
option_chain = ibkr_client.get_option_chain('SPX', datetime.now(), sub_symbol = 'SPXW', contract_filter = contract_filter)
|
||||||
|
print(option_chain)
|
98
option_chain_example_old.py
Normal file
98
option_chain_example_old.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#########################################################################
|
||||||
|
# Not currently using this as the call to reqTickers takes > 10 seconds #
|
||||||
|
#########################################################################
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
from exchange import SMART
|
||||||
|
from ib_insync import *
|
||||||
|
|
||||||
|
ib = IB()
|
||||||
|
ib.connect('127.0.0.1', 7497, clientId=1) # Assuming TWS is running on the current machine.
|
||||||
|
|
||||||
|
underlying = Index('SPX', 'CBOE')
|
||||||
|
ib.qualifyContracts(underlying)
|
||||||
|
underlying_ticker = ib.reqTickers(underlying)[0]
|
||||||
|
|
||||||
|
atm = underlying_ticker.last
|
||||||
|
print('Last Price:', atm)
|
||||||
|
|
||||||
|
chains = ib.reqSecDefOptParams(underlying.symbol, '', underlying.secType, underlying.conId)
|
||||||
|
|
||||||
|
chain = next(c for c in chains if c.tradingClass == 'SPXW' and c.exchange == SMART)
|
||||||
|
today = datetime.datetime.now().strftime('%Y%m%d')
|
||||||
|
expirations = [exp for exp in chain.expirations if exp == today]
|
||||||
|
|
||||||
|
max_strike_distance = 100
|
||||||
|
call_strikes = sorted(strike for strike in chain.strikes if strike <= (atm + max_strike_distance) and strike >= atm)
|
||||||
|
put_strikes = sorted(strike for strike in chain.strikes if strike <= atm and strike >= (atm - max_strike_distance))
|
||||||
|
|
||||||
|
put_contracts = [Option('SPX', expiration, strike, 'P', SMART) for expiration in expirations for strike in put_strikes]
|
||||||
|
call_contracts = [Option('SPX', expiration, strike, 'C', SMART) for expiration in expirations for strike in call_strikes]
|
||||||
|
contracts = put_contracts + call_contracts
|
||||||
|
qualified_contracts = ib.qualifyContracts(*contracts)
|
||||||
|
|
||||||
|
# Requesting market data (e.g., current bid / ask) for each contract.
|
||||||
|
# This is what takes a long time.
|
||||||
|
tickers = ib.reqTickers(*qualified_contracts)
|
||||||
|
data = []
|
||||||
|
for ticker in tickers:
|
||||||
|
symbol = ticker.contract.localSymbol
|
||||||
|
strike = ticker.contract.strike
|
||||||
|
right = 'CALL' if ticker.contract.right == 'C' else 'PUT'
|
||||||
|
# TODO: Bid and Ask.
|
||||||
|
price = iv = delta = gamma = vega = theta = underlying_price = None
|
||||||
|
# TODO: The model greeks are not always available, wait for them.
|
||||||
|
if ticker.modelGreeks is not None:
|
||||||
|
price = ticker.modelGreeks.optPrice
|
||||||
|
iv = ticker.modelGreeks.impliedVol
|
||||||
|
delta = ticker.modelGreeks.delta
|
||||||
|
gamma = ticker.modelGreeks.gamma
|
||||||
|
vega = ticker.modelGreeks.vega
|
||||||
|
theta = ticker.modelGreeks.theta
|
||||||
|
underlying_price = ticker.modelGreeks.undPrice
|
||||||
|
data.append([symbol, strike, right, price, iv, delta, gamma, vega, theta, underlying_price])
|
||||||
|
|
||||||
|
option_chain = pd.DataFrame(data, columns=['Symbol', 'Strike', 'Type', 'Price', 'IV', 'Delta', 'Gamma', 'Vega', 'Theta', 'Underlying Price'])
|
||||||
|
|
||||||
|
print('Option Chain:')
|
||||||
|
print(option_chain)
|
||||||
|
|
||||||
|
target_delta = 0.10
|
||||||
|
|
||||||
|
# Separate calls and puts.
|
||||||
|
calls = option_chain[option_chain['Type'] == 'CALL'].copy()
|
||||||
|
puts = option_chain[option_chain['Type'] == 'PUT'].copy()
|
||||||
|
|
||||||
|
# Find the difference between the target delta and actual delta for calls and puts.
|
||||||
|
calls['Delta Delta'] = abs(calls['Delta'] - target_delta)
|
||||||
|
puts['Delta Delta'] = abs(puts['Delta'] + target_delta)
|
||||||
|
|
||||||
|
# Find the row where this difference is minimized for calls.
|
||||||
|
closest_call_strike = calls.loc[calls['Delta Delta'].idxmin()]
|
||||||
|
|
||||||
|
# Find the row where this difference is minimized for puts.
|
||||||
|
closest_put_strike = puts.loc[puts['Delta Delta'].idxmin()]
|
||||||
|
|
||||||
|
# Determine the target strikes.
|
||||||
|
target_long_call_strike = closest_call_strike['Strike'] + 50
|
||||||
|
target_long_put_strike = closest_put_strike['Strike'] - 50
|
||||||
|
|
||||||
|
def find_closest_strike(target_strike, option_type, option_chain):
|
||||||
|
options = option_chain[option_chain['Type'] == option_type].copy()
|
||||||
|
options['Strike Distance'] = abs(options['Strike'] - target_strike)
|
||||||
|
nearest_strike = options.loc[options['Strike Distance'].idxmin()]
|
||||||
|
return nearest_strike
|
||||||
|
|
||||||
|
# Find the closest call and put strikes to the desired targets
|
||||||
|
closest_long_call_strike = find_closest_strike(target_long_call_strike, 'CALL', option_chain)
|
||||||
|
closest_long_put_strike = find_closest_strike(target_long_put_strike, 'PUT', option_chain)
|
||||||
|
|
||||||
|
# For entering an iron condor.
|
||||||
|
print('Short Call Strike:', closest_call_strike['Strike'])
|
||||||
|
print('Long Call Strike:', closest_long_call_strike['Strike'])
|
||||||
|
print("Short Put Strike:", closest_put_strike['Strike'])
|
||||||
|
print('Long Put Strike:', closest_long_put_strike['Strike'])
|
||||||
|
|
||||||
|
ib.disconnect()
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ib_insync
|
||||||
|
pandas
|
Loading…
Reference in New Issue
Block a user