Extract spread monitoring logic into its own file
This commit is contained in:
parent
d7bce267ca
commit
84df97d96b
@ -2,19 +2,19 @@ import logging
|
|||||||
import nest_asyncio
|
import nest_asyncio
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from dataclasses import replace
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from os import getenv
|
from os import getenv
|
||||||
|
|
||||||
from ibkr import Client, OptionLeg, OptionOrder
|
from ibkr import Client, OptionLeg
|
||||||
from ibkr.option_type import CALL, PUT
|
from ibkr.option_type import CALL, PUT
|
||||||
from ibkr.order_action import BUY, SELL
|
from ibkr.order_action import BUY, SELL
|
||||||
|
|
||||||
from iron_condor_trade import IronCondorTrade
|
from iron_condor_trade import IronCondorTrade
|
||||||
|
from monitor_spread import monitor_spread
|
||||||
from options_chain import OptionsChain
|
from options_chain import OptionsChain
|
||||||
from option_type import OptionType
|
from option_type import OptionType
|
||||||
from trades_table import insert_trade, update_trade
|
from trades_table import insert_trade
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
@ -22,64 +22,6 @@ load_dotenv()
|
|||||||
# Necessary for monitoring spread prices asynchronously while interacting with the IBKR client.
|
# Necessary for monitoring spread prices asynchronously while interacting with the IBKR client.
|
||||||
nest_asyncio.apply()
|
nest_asyncio.apply()
|
||||||
|
|
||||||
def monitor_spread(trade: IronCondorTrade, spread_order: OptionOrder, client: Client):
|
|
||||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
|
||||||
Stop loss orders will not execute if trying to sell back a contract with no bid while paper trading.
|
|
||||||
Therefore, the spread price must be monitored and the spread manually exited if the stop price is reached.
|
|
||||||
If there is no bid for the long leg, only the short leg will be exited.
|
|
||||||
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
|
||||||
stop_price = spread_order.fill_price * trade.stop_multiple
|
|
||||||
stopped_out = False
|
|
||||||
|
|
||||||
short_leg = spread_order.legs[0]
|
|
||||||
long_leg = spread_order.legs[1]
|
|
||||||
market_data = {} # Stores real-time market data for each leg.
|
|
||||||
|
|
||||||
def on_market_data_update(update_event):
|
|
||||||
# Prevent the trade from being exited multiple times if there are other updates queued.
|
|
||||||
# This will prevent unintentionally entering new trades.
|
|
||||||
nonlocal stopped_out
|
|
||||||
if stopped_out:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Ensure there is market data for both legs before proceeding.
|
|
||||||
if short_leg in market_data and long_leg in market_data:
|
|
||||||
short_contract = market_data[short_leg]
|
|
||||||
long_contract = market_data[long_leg]
|
|
||||||
|
|
||||||
# If a contract has no bid -1.0 is returned, set it to 0 to avoid negative mid prices.
|
|
||||||
mid_price_short = (max(short_contract.bid, 0) + short_contract.ask) / 2
|
|
||||||
mid_price_long = (max(long_contract.bid, 0) + long_contract.ask) / 2
|
|
||||||
current_spread_price = mid_price_short - mid_price_long
|
|
||||||
|
|
||||||
if current_spread_price >= stop_price:
|
|
||||||
stopped_out = True
|
|
||||||
logging.info('Stop price reached or exceeded. Exiting trade.')
|
|
||||||
|
|
||||||
short_leg_exit = replace(short_leg, action = BUY if short_leg.action == SELL else SELL)
|
|
||||||
long_leg_exit = replace(long_leg, action = BUY if long_leg.action == SELL else SELL)
|
|
||||||
if long_contract.bid > 0:
|
|
||||||
exit_order = client.submit_spread_order(short_leg_exit, long_leg_exit)
|
|
||||||
logging.info('Whole spread exited.')
|
|
||||||
else:
|
|
||||||
exit_order = client.submit_option_order(short_leg_exit)
|
|
||||||
logging.info('Short leg only exited.')
|
|
||||||
|
|
||||||
exit_slippage = round(exit_order.fill_price - stop_price, 3)
|
|
||||||
logging.info(f'Exit Slippage: {exit_slippage}')
|
|
||||||
|
|
||||||
update_trade(trade, exit_order)
|
|
||||||
|
|
||||||
# Unsubscribe from market data updates once the trade has exited.
|
|
||||||
for leg in [short_leg, long_leg]:
|
|
||||||
market_data[leg].updateEvent -= on_market_data_update
|
|
||||||
|
|
||||||
for leg in [short_leg, long_leg]:
|
|
||||||
option_contract = client.get_option_contract(leg)
|
|
||||||
leg_market_data = client.get_market_data(option_contract, streaming = True)
|
|
||||||
market_data[leg] = leg_market_data
|
|
||||||
leg_market_data.updateEvent += on_market_data_update
|
|
||||||
|
|
||||||
def enter_iron_condor(entry_time: datetime):
|
def enter_iron_condor(entry_time: datetime):
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
filename = f'iron_condor_{entry_time.strftime("%H%M")}.log',
|
filename = f'iron_condor_{entry_time.strftime("%H%M")}.log',
|
||||||
|
67
monitor_spread.py
Normal file
67
monitor_spread.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from dataclasses import replace
|
||||||
|
|
||||||
|
from ibkr import Client, OptionOrder
|
||||||
|
from ibkr.order_action import BUY, SELL
|
||||||
|
|
||||||
|
from iron_condor_trade import IronCondorTrade
|
||||||
|
from trades_table import update_trade
|
||||||
|
|
||||||
|
def monitor_spread(trade: IronCondorTrade, spread_order: OptionOrder, client: Client):
|
||||||
|
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||||
|
Stop loss orders will not execute if trying to sell back a contract with no bid while paper trading.
|
||||||
|
Therefore, the spread price must be monitored and the spread manually exited if the stop price is reached.
|
||||||
|
If there is no bid for the long leg, only the short leg will be exited.
|
||||||
|
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||||
|
stop_price = spread_order.fill_price * trade.stop_multiple
|
||||||
|
stopped_out = False
|
||||||
|
|
||||||
|
short_leg = spread_order.legs[0]
|
||||||
|
long_leg = spread_order.legs[1]
|
||||||
|
market_data = {} # Stores real-time market data for each leg.
|
||||||
|
|
||||||
|
def on_market_data_update(update_event):
|
||||||
|
# Prevent the trade from being exited multiple times if there are other updates queued.
|
||||||
|
# This will prevent unintentionally entering new trades.
|
||||||
|
nonlocal stopped_out
|
||||||
|
if stopped_out:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Ensure there is market data for both legs before proceeding.
|
||||||
|
if short_leg in market_data and long_leg in market_data:
|
||||||
|
short_contract = market_data[short_leg]
|
||||||
|
long_contract = market_data[long_leg]
|
||||||
|
|
||||||
|
# If a contract has no bid -1.0 is returned, set it to 0 to avoid negative mid prices.
|
||||||
|
mid_price_short = (max(short_contract.bid, 0) + short_contract.ask) / 2
|
||||||
|
mid_price_long = (max(long_contract.bid, 0) + long_contract.ask) / 2
|
||||||
|
current_spread_price = mid_price_short - mid_price_long
|
||||||
|
|
||||||
|
if current_spread_price >= stop_price:
|
||||||
|
stopped_out = True
|
||||||
|
logging.info('Stop price reached or exceeded. Exiting trade.')
|
||||||
|
|
||||||
|
short_leg_exit = replace(short_leg, action = BUY if short_leg.action == SELL else SELL)
|
||||||
|
long_leg_exit = replace(long_leg, action = BUY if long_leg.action == SELL else SELL)
|
||||||
|
if long_contract.bid > 0:
|
||||||
|
exit_order = client.submit_spread_order(short_leg_exit, long_leg_exit)
|
||||||
|
logging.info('Whole spread exited.')
|
||||||
|
else:
|
||||||
|
exit_order = client.submit_option_order(short_leg_exit)
|
||||||
|
logging.info('Short leg only exited.')
|
||||||
|
|
||||||
|
exit_slippage = round(exit_order.fill_price - stop_price, 3)
|
||||||
|
logging.info(f'Exit Slippage: {exit_slippage}')
|
||||||
|
|
||||||
|
update_trade(trade, exit_order)
|
||||||
|
|
||||||
|
# Unsubscribe from market data updates once the trade has exited.
|
||||||
|
for leg in [short_leg, long_leg]:
|
||||||
|
market_data[leg].updateEvent -= on_market_data_update
|
||||||
|
|
||||||
|
for leg in [short_leg, long_leg]:
|
||||||
|
option_contract = client.get_option_contract(leg)
|
||||||
|
leg_market_data = client.get_market_data(option_contract, streaming = True)
|
||||||
|
market_data[leg] = leg_market_data
|
||||||
|
leg_market_data.updateEvent += on_market_data_update
|
Loading…
Reference in New Issue
Block a user