options-automation/iron_condor.py

91 lines
3.8 KiB
Python
Raw Normal View History

from datetime import datetime
from dotenv import load_dotenv
from ibkr import Client
from option_type import CALL, PUT
from os import getenv
from tastytrade import Tastytrade
from tastytrade.order import create_credit_spread
from tastytrade.symbology import zero_dte_spx_contract as contract
load_dotenv()
ibkr_host = getenv('IBKR_HOST')
ibkr_port = getenv('IBKR_PORT')
ibkr_client = Client(host = ibkr_host, port = ibkr_port)
tastytrade_account = getenv('TASTYTRADE_ACCOUNT')
tastytrade_username = getenv('TASTYTRADE_USERNAME')
tastytrade_password = getenv('TASTYTRADE_PASSWORD')
tastytrade_client = Tastytrade(tastytrade_username, tastytrade_password)
tastytrade_client.login()
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)
target_delta = 0.20
def closest_contract_by_delta(target_delta, option_chain, option_type):
options = option_chain[option_chain['Type'] == option_type].copy()
options['Delta Distance'] = abs(options['Delta'] - target_delta)
return options.loc[options['Delta Distance'].idxmin()]
# Find the strikes that minimize the distance to the target delta.
short_put_contract = closest_contract_by_delta(-target_delta, option_chain, PUT)
short_call_contract = closest_contract_by_delta(target_delta, option_chain, CALL)
# When selecting long strikes, minimize the distance to a 50 point spread.
# TODO: Select long strike based on preferred price.
target_long_put_strike = short_put_contract['Strike'] - 50
target_long_call_strike = short_call_contract['Strike'] + 50
def closest_contract_by_strike(target_strike, option_chain, option_type):
options = option_chain[option_chain['Type'] == option_type].copy()
options['Strike Distance'] = abs(options['Strike'] - target_strike)
return options.loc[options['Strike Distance'].idxmin()]
long_put_contract = closest_contract_by_strike(target_long_put_strike, option_chain, PUT)
long_call_contract = closest_contract_by_strike(target_long_call_strike, option_chain, CALL)
# Build the iron condor.
short_put_strike = float(short_put_contract['Strike'])
long_put_strike = float(long_put_contract['Strike'])
short_call_strike = float(short_call_contract['Strike'])
long_call_strike = float(long_call_contract['Strike'])
put_spread_limit_price = short_put_contract['Bid'] - long_put_contract['Ask']
call_spread_limit_price = short_call_contract['Bid'] - long_call_contract['Ask']
print("Short Put Strike:", short_put_strike)
print('Long Put Strike:', long_put_strike)
print('Put Spread Limit Price:', put_spread_limit_price)
print('Short Call Strike:', short_call_strike)
print('Long Call Strike:', long_call_strike)
print('Call Spread Limit Price:', call_spread_limit_price)
put_credit_spread = create_credit_spread(
contract(PUT, short_put_strike),
contract(PUT, long_put_strike),
put_spread_limit_price, 1
)
call_credit_spread = create_credit_spread(
contract(CALL, short_call_strike),
contract(CALL, long_call_strike),
call_spread_limit_price, 1
)
put_spread_result = tastytrade_client.submit_order(tastytrade_account, put_credit_spread)
call_spread_result = tastytrade_client.submit_order(tastytrade_account, call_credit_spread)
print(put_spread_result)
print(call_spread_result)