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.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 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 from trades_table import insert_trade, update_on_stop_loss
load_dotenv() load_dotenv()
@ -21,7 +22,7 @@ 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_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. 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. 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) exit_order = client.submit_option_order(short_leg_exit)
logging.info('Short leg only exited.') 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. # Unsubscribe from market data updates once the trade has exited.
for leg in [short_leg, long_leg]: 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}') logging.info(f'Long Call Strike: {long_call_strike}')
ibkr_client = Client() 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) 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) 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) 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 Mid Price: {call_spread_order.mid_price}')
logging.info(f'Call Spread Fill Price: {call_spread_order.fill_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( monitor_spread_price(
trade = trade,
short_leg = short_call_leg, short_leg = short_call_leg,
long_leg = long_call_leg, long_leg = long_call_leg,
stop_price = call_spread_order.fill_price * 2, 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) 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 Mid Price: {put_spread_order.mid_price}')
logging.info(f'Put Spread Fill Price: {put_spread_order.fill_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( monitor_spread_price(
trade = trade,
short_leg = short_put_leg, short_leg = short_put_leg,
long_leg = long_put_leg, long_leg = long_put_leg,
stop_price = put_spread_order.fill_price * 2, stop_price = put_spread_order.fill_price * 2,
client = ibkr_client 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. # TODO: Add a shutdown hook.
ibkr_client.run_event_loop() ibkr_client.run_event_loop()

View File

@ -1,13 +1,13 @@
from datetime import datetime
from pandas import DataFrame from pandas import DataFrame
from backtesting.credit_targeting import iron_condor_strategy from database.trades import get_leg, get_spread, trade, upsert
from database.trades import get_leg, get_spread, upsert
from database.trades.action import Action from database.trades.action import Action
from database.trades.option_type import OptionType from database.trades.option_type import OptionType
from ibkr import option_type as IBKROptionType from ibkr import option_type as IBKROptionType
from ibkr.option_order import OptionOrder from ibkr.option_order import OptionOrder
from iron_condor_trade import IronCondorTrade
def translate_action(action_string: str) -> Action: def translate_action(action_string: str) -> Action:
for action in Action: for action in Action:
if action_string == action.value: 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) entry_slippage = round(spread_order.mid_price - spread_order.fill_price, 3)
) )
def insert_trade( def insert_trade(iron_condor: IronCondorTrade, call_spread_order: OptionOrder, put_spread_order: OptionOrder):
symbol: str,
target: float,
entry_time: datetime,
call_spread_order: OptionOrder,
put_spread_order: OptionOrder
):
upsert( upsert(
DataFrame([{ DataFrame([{
'Date': datetime.now().date(), 'Date': iron_condor.entry_time.date(),
'Symbol': symbol, 'Symbol': iron_condor.symbol,
'Strategy': iron_condor_strategy(target), 'Strategy': iron_condor.strategy,
'Entry Time': entry_time.replace(tzinfo = None), 'Entry Time': iron_condor.entry_time,
'Exit Time': None, # Required. 'Exit Time': None, # Required.
'Spreads': [spread(call_spread_order), spread(put_spread_order)], 'Spreads': [spread(call_spread_order), spread(put_spread_order)],
'Profit': None # Required. '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.
}])
)