From 2c5ca682951e56f7d7f567cfc8c335a6ce91348d Mon Sep 17 00:00:00 2001 From: rob Date: Mon, 3 Jul 2023 14:49:54 +0100 Subject: [PATCH] fix #932 --- sysdata/production/trade_limits.py | 28 ++++++--- sysobjects/production/roll_state.py | 7 +++ sysproduction/data/controls.py | 29 ++++++++- .../interactive_update_roll_status.py | 62 ++++++++++++++++++- 4 files changed, 114 insertions(+), 12 deletions(-) diff --git a/sysdata/production/trade_limits.py b/sysdata/production/trade_limits.py index 1e9814e754..0ea97f83b8 100644 --- a/sysdata/production/trade_limits.py +++ b/sysdata/production/trade_limits.py @@ -52,7 +52,15 @@ def no_limit( period_days=period_days, ) - def what_trade_is_possible( + def what_trade_is_possible_for_instrument( + self, instrument_code: str, proposed_trade: int + ) -> int: + combined_list = self.get_trade_limits_for_instrument(instrument_code) + possible_trade = combined_list.what_trade_is_possible(proposed_trade) + + return possible_trade + + def what_trade_is_possible_for_instrument_strategy( self, instrument_strategy: instrumentStrategy, proposed_trade: int ) -> int: combined_list = self._get_list_of_all_relevant_trade_limits(instrument_strategy) @@ -73,11 +81,11 @@ def remove_trade(self, instrument_strategy: instrumentStrategy, trade: int): def _get_list_of_all_relevant_trade_limits( self, instrument_strategy: instrumentStrategy ) -> listOfTradeLimits: - instrument_trade_limits = self._get_trade_limits_for_instrument( + instrument_trade_limits = self.get_trade_limits_for_instrument( instrument_strategy.instrument_code ) strategy_instrument_trade_limits = ( - self._get_trade_limits_for_instrument_strategy(instrument_strategy) + self.get_trade_limits_for_instrument_strategy(instrument_strategy) ) combined_list = listOfTradeLimits( @@ -86,14 +94,18 @@ def _get_list_of_all_relevant_trade_limits( return combined_list - def _get_trade_limits_for_instrument(self, instrument_code: str) -> list: + def get_trade_limits_for_instrument( + self, instrument_code: str + ) -> listOfTradeLimits: instrument_strategy = instrument_strategy_for_instrument_only(instrument_code) - return self._get_trade_limits_for_instrument_strategy(instrument_strategy) + return listOfTradeLimits( + self.get_trade_limits_for_instrument_strategy(instrument_strategy) + ) - def _get_trade_limits_for_instrument_strategy( + def get_trade_limits_for_instrument_strategy( self, instrument_strategy: instrumentStrategy - ) -> list: + ) -> listOfTradeLimits: all_keys = self._get_all_limit_keys() relevant_keys = all_keys.for_given_instrument_strategy(instrument_strategy) trade_limits = [ @@ -101,7 +113,7 @@ def _get_trade_limits_for_instrument_strategy( for isd_key in relevant_keys ] - return trade_limits + return listOfTradeLimits(trade_limits) def _update_list_of_trade_limits(self, list_of_trade_limits: list): result = [ diff --git a/sysobjects/production/roll_state.py b/sysobjects/production/roll_state.py index 96fb5ec318..c83c056c04 100644 --- a/sysobjects/production/roll_state.py +++ b/sysobjects/production/roll_state.py @@ -30,6 +30,13 @@ } +def is_double_sided_trade_roll_state(roll_state: RollState): + if roll_state in [RollState.Force, RollState.Force_Outright]: + return True + else: + return False + + def is_forced_roll_state(roll_state: RollState): if roll_state in [RollState.Force, RollState.Force_Outright, RollState.Close]: return True diff --git a/sysproduction/data/controls.py b/sysproduction/data/controls.py index 920f5f42f3..0b9e1149b0 100644 --- a/sysproduction/data/controls.py +++ b/sysproduction/data/controls.py @@ -122,9 +122,32 @@ def what_trade_is_possible_for_strategy_instrument( self, instrument_strategy: instrumentStrategy, proposed_trade: tradeQuantity ) -> int: - proposed_trade_as_int = proposed_trade.total_abs_qty() - possible_trade = self.db_trade_limit_data.what_trade_is_possible( - instrument_strategy, proposed_trade_as_int + proposed_trade_qty = proposed_trade.total_abs_qty() + possible_trade = self.what_trade_qty_possible_for_instrument_strategy( + instrument_strategy=instrument_strategy, + proposed_trade_qty=proposed_trade_qty, + ) + + return possible_trade + + def what_trade_qty_possible_for_instrument_strategy( + self, instrument_strategy: instrumentStrategy, proposed_trade_qty: int + ) -> int: + + possible_trade = ( + self.db_trade_limit_data.what_trade_is_possible_for_instrument_strategy( + instrument_strategy, proposed_trade_qty + ) + ) + + return possible_trade + + def what_trade_qty_possible_for_instrument_code( + self, instrument_code, proposed_trade_qty: int + ) -> int: + + possible_trade = self.db_trade_limit_data.what_trade_is_possible_for_instrument( + instrument_code=instrument_code, proposed_trade=proposed_trade_qty ) return possible_trade diff --git a/sysproduction/interactive_update_roll_status.py b/sysproduction/interactive_update_roll_status.py index b8d7fccef9..eeaed9afbe 100644 --- a/sysproduction/interactive_update_roll_status.py +++ b/sysproduction/interactive_update_roll_status.py @@ -29,6 +29,7 @@ RollState, no_roll_state, no_open_state, + is_double_sided_trade_roll_state, ) from sysproduction.reporting.api import reportingApi @@ -36,7 +37,7 @@ from sysproduction.reporting.reporting_functions import run_report_with_data_blob from sysproduction.data.positions import diagPositions, updatePositions -from sysproduction.data.controls import updateOverrides +from sysproduction.data.controls import updateOverrides, dataTradeLimits from sysproduction.data.contracts import dataContracts from sysproduction.data.prices import diagPrices, get_valid_instrument_code_from_user @@ -505,6 +506,11 @@ def modify_roll_state( confirm_adjusted_price_change=confirm_adjusted_price_change, ) + ## Following roll states require trading: force, forceoutright, close + check_trading_limits_for_roll_state( + data=data, roll_state_required=roll_state_required + ) + def roll_state_was_no_open_now_something_else(data: dataBlob, instrument_code: str): print( @@ -658,5 +664,59 @@ def _get_roll_adjusted_multiple_prices_object_ffill_option( return rolling_adj_and_mult_object +def check_trading_limits_for_roll_state( + data: dataBlob, roll_state_required: RollState, instrument_code: str +): + abs_trades_required_for_roll = calculate_abs_trades_required_for_roll( + data=data, + roll_state_required=roll_state_required, + instrument_code=instrument_code, + ) + trades_possible = get_remaining_trades_possible_today_in_contracts_for_instrument( + data=data, + instrument_code=instrument_code, + proposed_trade_qty=abs_trades_required_for_roll, + ) + if trades_possible < abs_trades_required_for_roll: + print("**** WARNING ****") + print( + "Roll for %s requires %d contracts, but we can only trade %d today" + % (instrument_code, abs_trades_required_for_roll, trades_possible) + ) + print( + "Use interactive controls/trade limits to set higher limit (and don't forget to reset afterwards)" + ) + + +def calculate_abs_trades_required_for_roll( + data: dataBlob, roll_state_required: RollState, instrument_code: str +) -> int: + data_contacts = dataContracts(data) + diag_positions = diagPositions(data) + current_priced_contract_id = data_contacts.get_priced_contract_id( + instrument_code=instrument_code + ) + position = diag_positions.get_position_for_contract( + futuresContract( + instrument_object=instrument_code, + contract_date_object=current_priced_contract_id, + ) + ) + + if is_double_sided_trade_roll_state(roll_state_required): + position = position * 2 + + return position + + +def get_remaining_trades_possible_today_in_contracts_for_instrument( + data: dataBlob, instrument_code: str, proposed_trade_qty +) -> int: + data_trade_limits = dataTradeLimits(data) + return data_trade_limits.what_trade_qty_possible_for_instrument_code( + instrument_code=instrument_code, proposed_trade_qty=proposed_trade_qty + ) + + if __name__ == "__main__": interactive_update_roll_status()