Skip to content

Commit

Permalink
fix #969
Browse files Browse the repository at this point in the history
  • Loading branch information
rob committed Jul 4, 2023
1 parent be6c520 commit 98c019b
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 27 deletions.
11 changes: 11 additions & 0 deletions sysdata/config/defaults.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ roll_status_auto_update:
## or Ask to request from user
default_roll_state_if_undecided: Ask
#
# Force certain instruments to use a specific execution algo
# Algos must be classes eg sysexecution.algos.algo_market.algoMarket
# Copy and paste this into private_config to modify
execution_algos:
default_algo: sysexecution.algos.algo_original_best.algoOriginalBest
market_algo: sysexecution.algos.algo_market.algoMarket
limit_order_algo: sysexecution.algos.algo_limit_orders.algoLimit
best_algo: sysexecution.algos.algo_original_best.algoOriginalBest
algo_overrides:
some_market_name_eg_IRON: sysexecution.algos.algo_market.algoMarket
#
# BACKTESTING STUFF
#
# Raw data
Expand Down
123 changes: 98 additions & 25 deletions sysexecution/algos/allocate_algo_to_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
Depends on instrument order and type of order
"""
from dataclasses import dataclass, field
from sysproduction.data.orders import dataOrders
from sysproduction.data.broker import dataBroker
from syscore.constants import arg_not_supplied
Expand All @@ -20,12 +21,11 @@
from sysexecution.orders.contract_orders import contractOrder
from sysexecution.orders.list_of_orders import listOfOrders

DEFAULT_ALGO = MARKET_ALGO = "sysexecution.algos.algo_market.algoMarket"
ORIGINAL_BEST = "sysexecution.algos.algo_original_best.algoOriginalBest"
LIMIT_ALGO = "sysexecution.algos.algo_limit_orders.algoLimit"

def get_list_of_algos(data: dataBlob):
config = get_algo_allocation_config(data)

list_of_algos = [MARKET_ALGO, ORIGINAL_BEST]
return [config.market_algo, config.default_algo]


def allocate_algo_to_list_of_contract_orders(
Expand All @@ -40,11 +40,14 @@ def allocate_algo_to_list_of_contract_orders(
:param list_of_contract_orders:
:return: list of contract orders with algo added
"""

config = get_algo_allocation_config(data)
new_list_of_contract_orders = []
for contract_order in list_of_contract_orders:
contract_order = check_and_if_required_allocate_algo_to_single_contract_order(
data, contract_order, instrument_order
data=data,
config=config,
contract_order=contract_order,
instrument_order=instrument_order,
)
new_list_of_contract_orders.append(contract_order)

Expand All @@ -53,19 +56,37 @@ def allocate_algo_to_list_of_contract_orders(
return new_list_of_contract_orders


@dataclass
class AlgoConfig:
default_algo: str
market_algo: str
limit_order_algo: str
best_algo: str
algo_overrides: field(default_factory=dict)


def get_algo_allocation_config(data: dataBlob) -> AlgoConfig:
config = data.config.get_element("execution_algos")

return AlgoConfig(
default_algo=config["default_algo"],
market_algo=config["market_algo"],
limit_order_algo=config["limit_order_algo"],
algo_overrides=config["algo_overrides"],
best_algo=config["best_algo"],
)


def check_and_if_required_allocate_algo_to_single_contract_order(
data: dataBlob, contract_order: contractOrder, instrument_order: instrumentOrder
data: dataBlob,
config: AlgoConfig,
contract_order: contractOrder,
instrument_order: instrumentOrder,
) -> contractOrder:
"""

:param data: dataBlog
:param instrument_order: parent instrument order
:param list_of_contract_orders:
:return: list of contract orders with algo added
"""
log = contract_order.log_with_attributes(data.log)

if contract_order.algo_to_use != "":
if already_has_algo_allocated(contract_order):
# Already done
return contract_order

Expand All @@ -74,52 +95,104 @@ def check_and_if_required_allocate_algo_to_single_contract_order(
# not used yet, but maybe in the future
is_roll_order = instrument_order.roll_order

if instrument_order_type == market_order_type:
if algo_allocation_is_overriden_for_instrument(
contract_order=contract_order, config=config
):
contract_order = allocate_algo_for_specific_instrument_with_override(
contract_order=contract_order, config=config
)
elif instrument_order_type == market_order_type:
log.debug("Market order type, so allocating to algo_market")
contract_order.algo_to_use = MARKET_ALGO
contract_order = allocate_market_algo(
contract_order=contract_order, config=config
)

elif (
instrument_order_type == best_order_type
or instrument_order_type == zero_roll_order_type
):
contract_order = allocate_for_best_execution_no_limit(
data=data, contract_order=contract_order
config=config, data=data, contract_order=contract_order
)

elif instrument_order_type == limit_order_type:
contract_order = allocate_for_limit_order(data, contract_order=contract_order)
contract_order = allocate_for_limit_order(
data=data, config=config, contract_order=contract_order
)

elif instrument_order_type == balance_order_type:
log.critical("Balance orders aren't executed, shouldn't even be here!")
return missing_order
else:
log.warning(
"Don't recognise order type %s so allocating to default algo_market"
% instrument_order_type
"Don't recognise order type %s so allocating to default %s"
% (instrument_order_type, config.default_algo)
)
contract_order = allocate_default_algo(
contract_order=contract_order, config=config
)
contract_order.algo_to_use = DEFAULT_ALGO

return contract_order


def already_has_algo_allocated(contract_order: contractOrder) -> bool:
return contract_order.algo_to_use != ""


def algo_allocation_is_overriden_for_instrument(
contract_order: contractOrder, config: AlgoConfig
) -> bool:

instrument_code = contract_order.instrument_code
instruments_with_keys = list(config.algo_overrides.keys())

return instrument_code in instruments_with_keys


def allocate_algo_for_specific_instrument_with_override(
config: AlgoConfig, contract_order: contractOrder
) -> contractOrder:
instrument_code = contract_order.instrument_code
default_algo = config.default_algo
algo_to_use = config.algo_overrides.get(instrument_code, default_algo)

contract_order.algo_to_use = algo_to_use

return contract_order


def allocate_market_algo(
contract_order: contractOrder, config: AlgoConfig
) -> contractOrder:
contract_order.algo_to_use = config.market_algo
return contract_order


def allocate_for_best_execution_no_limit(
data: dataBlob, contract_order: contractOrder
data: dataBlob, config: AlgoConfig, contract_order: contractOrder
) -> contractOrder:
# in the future could be randomized...
log = contract_order.log_with_attributes(data.log)

log.debug("'Best' order so allocating to original_best")
contract_order.algo_to_use = ORIGINAL_BEST
contract_order.algo_to_use = config.best_algo

return contract_order


def allocate_for_limit_order(
data: dataBlob, contract_order: contractOrder
data: dataBlob, config: AlgoConfig, contract_order: contractOrder
) -> contractOrder:
# in the future could be randomized...
log = contract_order.log_with_attributes(data.log)
log.debug("Allocating to limit order")
contract_order.algo_to_use = LIMIT_ALGO
contract_order.algo_to_use = config.limit_order_algo

return contract_order


def allocate_default_algo(
contract_order: contractOrder, config: AlgoConfig
) -> contractOrder:
contract_order.algo_to_use = config.default_algo
return contract_order
1 change: 0 additions & 1 deletion sysexecution/strategies/dynamic_optimised_positions.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,6 @@ class dataForObjectiveInstance:
maximum_position_contracts: portfolioWeights
constraints: constraintsForDynamicOpt
speed_control: speedControlForDynamicOpt
constraints: constraintsForDynamicOpt

@property
def weights_prior(self) -> portfolioWeights:
Expand Down
3 changes: 2 additions & 1 deletion sysproduction/interactive_order_stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
instrumentOrderType,
balance_order_type as instrument_balance_order_type,
)
from sysexecution.algos.allocate_algo_to_order import list_of_algos
from sysexecution.algos.allocate_algo_to_order import get_list_of_algos
from sysbrokers.IB.ib_connection import connectionIB
from syscore.constants import arg_not_supplied

Expand Down Expand Up @@ -508,6 +508,7 @@ def enter_manual_contract_order(data, instrument_order):
print("It's unlikely you meant to do this...")

NO_ALGO = "None: allow system to allocate"
list_of_algos = get_list_of_algos(data)
algo_to_use = print_menu_of_values_and_get_response(
list_of_algos, default_str=NO_ALGO
)
Expand Down

0 comments on commit 98c019b

Please sign in to comment.