Update trades table upon stopping out of a spread so that it includes the closing price and slippage incurred on exit

This commit is contained in:
moshferatu 2024-02-27 10:52:07 -08:00
parent b6c0738e00
commit d868192002
2 changed files with 49 additions and 21 deletions

View File

@ -11,9 +11,10 @@ from ibkr import Client, OptionLeg
from ibkr.option_type import CALL, PUT
from ibkr.order_action import BUY, SELL
from iron_condor_trade import IronCondorTrade
from options_chain import OptionsChain
from option_type import OptionType
from trades_table import insert_trade
from trades_table import insert_trade, update_on_stop_loss
load_dotenv()
@ -21,7 +22,7 @@ load_dotenv()
# Necessary for monitoring spread prices asynchronously while interacting with the IBKR client.
nest_asyncio.apply()
def monitor_spread_price(short_leg: OptionLeg, long_leg: OptionLeg, stop_price: float, client: Client):
def monitor_spread_price(trade: IronCondorTrade, short_leg: OptionLeg, long_leg: OptionLeg, stop_price: float, 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.
@ -60,7 +61,10 @@ def monitor_spread_price(short_leg: OptionLeg, long_leg: OptionLeg, stop_price:
exit_order = client.submit_option_order(short_leg_exit)
logging.info('Short leg only exited.')
logging.info(f'Exit Slippage: {round(exit_order.fill_price - stop_price, 3)}')
exit_slippage = round(exit_order.fill_price - stop_price, 3)
logging.info(f'Exit Slippage: {exit_slippage}')
update_on_stop_loss(trade, short_leg.option_type, exit_order.fill_price, exit_slippage)
# Unsubscribe from market data updates once the trade has exited.
for leg in [short_leg, long_leg]:
@ -116,6 +120,7 @@ def _enter_iron_condor(entry_time: datetime):
logging.info(f'Long Call Strike: {long_call_strike}')
ibkr_client = Client()
trade = IronCondorTrade(symbol, credit_target, entry_time, float(getenv('STOP_MULTIPLE')))
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)
@ -123,9 +128,10 @@ def _enter_iron_condor(entry_time: datetime):
call_spread_order = ibkr_client.submit_spread_order(short_call_leg, long_call_leg)
logging.info(f'Call Spread Mid Price: {call_spread_order.mid_price}')
logging.info(f'Call Spread Fill Price: {call_spread_order.fill_price}')
logging.info(f'Call Spread Slippage: {call_spread_order.fill_price - call_spread_order.mid_price}')
logging.info(f'Call Spread Slippage: {call_spread_order.mid_price - call_spread_order.fill_price}')
monitor_spread_price(
trade = trade,
short_leg = short_call_leg,
long_leg = long_call_leg,
stop_price = call_spread_order.fill_price * 2,
@ -138,16 +144,17 @@ def _enter_iron_condor(entry_time: datetime):
put_spread_order = ibkr_client.submit_spread_order(short_put_leg, long_put_leg)
logging.info(f'Put Spread Mid Price: {put_spread_order.mid_price}')
logging.info(f'Put Spread Fill Price: {put_spread_order.fill_price}')
logging.info(f'Put Spread Slippage: {put_spread_order.fill_price - put_spread_order.mid_price}')
logging.info(f'Put Spread Slippage: {put_spread_order.mid_price - put_spread_order.fill_price}')
monitor_spread_price(
trade = trade,
short_leg = short_put_leg,
long_leg = long_put_leg,
stop_price = put_spread_order.fill_price * 2,
client = ibkr_client
)
insert_trade(symbol, credit_target, entry_time, call_spread_order, put_spread_order)
insert_trade(trade, call_spread_order, put_spread_order)
# TODO: Add a shutdown hook.
ibkr_client.run_event_loop()

View File

@ -1,13 +1,13 @@
from datetime import datetime
from pandas import DataFrame
from backtesting.credit_targeting import iron_condor_strategy
from database.trades import get_leg, get_spread, upsert
from database.trades import get_leg, get_spread, trade, upsert
from database.trades.action import Action
from database.trades.option_type import OptionType
from ibkr import option_type as IBKROptionType
from ibkr.option_order import OptionOrder
from iron_condor_trade import IronCondorTrade
def translate_action(action_string: str) -> Action:
for action in Action:
if action_string == action.value:
@ -37,22 +37,43 @@ def spread(spread_order: OptionOrder) -> dict:
entry_slippage = round(spread_order.mid_price - spread_order.fill_price, 3)
)
def insert_trade(
symbol: str,
target: float,
entry_time: datetime,
call_spread_order: OptionOrder,
put_spread_order: OptionOrder
):
def insert_trade(iron_condor: IronCondorTrade, call_spread_order: OptionOrder, put_spread_order: OptionOrder):
upsert(
DataFrame([{
'Date': datetime.now().date(),
'Symbol': symbol,
'Strategy': iron_condor_strategy(target),
'Entry Time': entry_time.replace(tzinfo = None),
'Date': iron_condor.entry_time.date(),
'Symbol': iron_condor.symbol,
'Strategy': iron_condor.strategy,
'Entry Time': iron_condor.entry_time,
'Exit Time': None, # Required.
'Spreads': [spread(call_spread_order), spread(put_spread_order)],
'Profit': None # Required.
}])
)
def update_on_stop_loss(iron_condor: IronCondorTrade, option_type: str, close_price: float, exit_slippage: float):
date = iron_condor.entry_time.date()
trade_record = trade(
date = date,
symbol = iron_condor.symbol,
strategy = iron_condor.strategy,
entry_time = iron_condor.entry_time
)
spreads = trade_record['Spreads'].iloc[0]
spread_index = 0 if option_type == 'C' else 1 # 'C' for call spread, 'P' for put spread.
spreads[spread_index]['Close'] = close_price
spreads[spread_index]['Exit Slippage'] = exit_slippage
upsert(
DataFrame([{
'Date': date,
'Symbol': iron_condor.symbol,
'Strategy': iron_condor.strategy,
'Entry Time': iron_condor.entry_time,
'Exit Time': None,
'Spreads': spreads,
'Profit': None # To be updated end of day.
}])
)