2023-12-07 16:04:57 +00:00
|
|
|
import logging
|
2023-12-15 22:48:47 +00:00
|
|
|
import nest_asyncio
|
2023-12-07 16:04:57 +00:00
|
|
|
import traceback
|
|
|
|
|
2023-12-01 20:47:46 +00:00
|
|
|
from datetime import datetime
|
2023-09-15 15:45:19 +00:00
|
|
|
from dotenv import load_dotenv
|
2024-02-22 20:23:59 +00:00
|
|
|
from os import getenv
|
|
|
|
|
2024-02-29 13:45:59 +00:00
|
|
|
from ibkr import Client, OptionLeg
|
2023-12-01 20:47:46 +00:00
|
|
|
from ibkr.option_type import CALL, PUT
|
|
|
|
from ibkr.order_action import BUY, SELL
|
2024-02-22 20:23:59 +00:00
|
|
|
|
2024-02-27 18:52:07 +00:00
|
|
|
from iron_condor_trade import IronCondorTrade
|
2024-02-29 13:45:59 +00:00
|
|
|
from monitor_spread import monitor_spread
|
2024-02-13 16:49:02 +00:00
|
|
|
from options_chain import OptionsChain
|
|
|
|
from option_type import OptionType
|
2024-02-29 13:45:59 +00:00
|
|
|
from trades_table import insert_trade
|
2023-09-15 14:17:44 +00:00
|
|
|
|
2023-09-15 15:45:19 +00:00
|
|
|
load_dotenv()
|
|
|
|
|
2023-12-15 22:48:47 +00:00
|
|
|
# Allows for starting an event loop even if there's already one running in the current thread.
|
|
|
|
# Necessary for monitoring spread prices asynchronously while interacting with the IBKR client.
|
|
|
|
nest_asyncio.apply()
|
|
|
|
|
2024-02-13 18:50:50 +00:00
|
|
|
def enter_iron_condor(entry_time: datetime):
|
2023-12-15 22:48:47 +00:00
|
|
|
logging.basicConfig(
|
2024-02-14 20:34:34 +00:00
|
|
|
filename = f'iron_condor_{entry_time.strftime("%H%M")}.log',
|
|
|
|
level = logging.INFO,
|
|
|
|
format = '%(asctime)s : %(levelname)s : %(message)s',
|
|
|
|
datefmt = '%Y-%m-%d %H:%M:%S'
|
2023-12-15 22:48:47 +00:00
|
|
|
)
|
2023-12-07 16:04:57 +00:00
|
|
|
try:
|
2024-02-13 18:50:50 +00:00
|
|
|
_enter_iron_condor(entry_time)
|
2024-04-21 14:49:12 +00:00
|
|
|
except Exception:
|
2024-02-14 20:35:38 +00:00
|
|
|
logging.error('Error: %s', traceback.format_exc())
|
2023-12-07 16:04:57 +00:00
|
|
|
|
2024-02-13 18:50:50 +00:00
|
|
|
def _enter_iron_condor(entry_time: datetime):
|
2023-12-15 22:48:47 +00:00
|
|
|
# The weekly symbol for SPX (SPXW) is required in order to distinguish from monthly options.
|
2023-12-01 20:47:46 +00:00
|
|
|
symbol, sub_symbol = 'SPX', 'SPXW'
|
|
|
|
expiration = datetime.now()
|
|
|
|
|
2024-02-13 18:51:42 +00:00
|
|
|
options_chain = OptionsChain('$SPXW.X', expiration)
|
2024-02-13 16:49:02 +00:00
|
|
|
logging.info(options_chain)
|
2023-12-01 20:47:46 +00:00
|
|
|
|
2024-02-13 16:49:02 +00:00
|
|
|
credit_target = float(getenv('CREDIT_TARGET'))
|
|
|
|
short_put_contract = options_chain.closest_contract_by_credit(credit_target, OptionType.PUT)
|
|
|
|
short_call_contract = options_chain.closest_contract_by_credit(credit_target, OptionType.CALL)
|
2023-12-01 20:47:46 +00:00
|
|
|
|
|
|
|
# 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
|
|
|
|
|
2024-02-13 16:49:02 +00:00
|
|
|
long_put_contract = options_chain.closest_contract_by_strike(target_long_put_strike, OptionType.PUT)
|
|
|
|
long_call_contract = options_chain.closest_contract_by_strike(target_long_call_strike, OptionType.CALL)
|
2023-12-01 20:47:46 +00:00
|
|
|
|
|
|
|
# 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'])
|
|
|
|
|
2023-12-07 16:04:57 +00:00
|
|
|
logging.info(f'Short Put Strike: {short_put_strike}')
|
|
|
|
logging.info(f'Long Put Strike: {long_put_strike}')
|
|
|
|
logging.info(f'Short Call Strike: {short_call_strike}')
|
|
|
|
logging.info(f'Long Call Strike: {long_call_strike}')
|
2023-12-01 20:47:46 +00:00
|
|
|
|
2024-02-19 16:53:18 +00:00
|
|
|
ibkr_client = Client()
|
2024-02-27 18:52:07 +00:00
|
|
|
trade = IronCondorTrade(symbol, credit_target, entry_time, float(getenv('STOP_MULTIPLE')))
|
2024-02-19 16:53:18 +00:00
|
|
|
|
2023-12-01 20:47:46 +00:00
|
|
|
short_call_leg = OptionLeg(symbol, expiration, short_call_strike, CALL, SELL, sub_symbol)
|
|
|
|
long_call_leg = OptionLeg(symbol, expiration, long_call_strike, CALL, BUY, sub_symbol)
|
|
|
|
|
2024-02-19 22:29:49 +00:00
|
|
|
call_spread_order = ibkr_client.submit_spread_order(short_call_leg, long_call_leg)
|
2024-02-22 20:36:57 +00:00
|
|
|
logging.info(f'Call Spread Mid Price: {call_spread_order.mid_price}')
|
|
|
|
logging.info(f'Call Spread Fill Price: {call_spread_order.fill_price}')
|
2024-02-27 18:52:07 +00:00
|
|
|
logging.info(f'Call Spread Slippage: {call_spread_order.mid_price - call_spread_order.fill_price}')
|
2024-02-19 22:29:49 +00:00
|
|
|
|
2024-02-28 19:20:10 +00:00
|
|
|
monitor_spread(trade, call_spread_order, ibkr_client)
|
2024-01-25 15:18:53 +00:00
|
|
|
|
2023-12-01 20:47:46 +00:00
|
|
|
short_put_leg = OptionLeg(symbol, expiration, short_put_strike, PUT, SELL, sub_symbol)
|
|
|
|
long_put_leg = OptionLeg(symbol, expiration, long_put_strike, PUT, BUY, sub_symbol)
|
|
|
|
|
2024-02-19 22:29:49 +00:00
|
|
|
put_spread_order = ibkr_client.submit_spread_order(short_put_leg, long_put_leg)
|
2024-02-22 20:36:57 +00:00
|
|
|
logging.info(f'Put Spread Mid Price: {put_spread_order.mid_price}')
|
|
|
|
logging.info(f'Put Spread Fill Price: {put_spread_order.fill_price}')
|
2024-02-27 18:52:07 +00:00
|
|
|
logging.info(f'Put Spread Slippage: {put_spread_order.mid_price - put_spread_order.fill_price}')
|
2024-02-19 22:29:49 +00:00
|
|
|
|
2024-02-28 19:20:10 +00:00
|
|
|
monitor_spread(trade, put_spread_order, ibkr_client)
|
2024-01-25 15:18:53 +00:00
|
|
|
|
2024-02-27 18:52:07 +00:00
|
|
|
insert_trade(trade, call_spread_order, put_spread_order)
|
2024-01-25 15:18:53 +00:00
|
|
|
|
2023-12-15 22:48:47 +00:00
|
|
|
# TODO: Add a shutdown hook.
|
|
|
|
ibkr_client.run_event_loop()
|