MLThon Order package

Submodules

mlthon.order.order module

class Order[source]

Bases: object

get_attachment()[source]
get_client_id()[source]
get_exchange_id()[source]
get_exec_instructions()[source]
get_instrument_id()[source]
get_leaves_qty()[source]
get_leaves_qty_after_modify_prep()[source]
get_pending_price()[source]
get_pending_qty()[source]
get_price()[source]
get_side()[source]
get_tif()[source]
get_type()[source]
has_any_pending_request()[source]
is_cancel_pending()[source]
is_cancelled()[source]
is_modify_pending()[source]
is_new_pending()[source]
is_open()[source]

mlthon.order.order_mgr module

class OrderMgr(client_id_prefix)[source]

Bases: object

Class handling the tracking of orders and their information. Used to manage the orders in the strategies. Note that an OrderMgr instance is assumed to be associated to a single exchange.

add_open_order(client_id, exchange_id, instrument_id, side, price, qty, order_type, tif, exec_instr, nullable_attachment)[source]

To be invoked to add open orders that existed before creating the OrderMgr instance. This method is not recommended in practice. Should clear all the open orders when stop trading and thus when a new strat is started, no existing open orders would be there.

Parameters
  • client_id (str) – client ID, identification in our internal system.

  • exchange_id (str) – ID in external exchange.

  • instrument_id (int) – product of this order, represented via its ID, e.g. 30000000000 represents BTC-USD inverse perpetual in Bybit.

  • side (Side) – side of this order, Side.Buy or Side.Sell.

  • price (Price) – price of this order.

  • qty (Qty) – quantity of this order.

  • order_type (OrderType) – type of this order, OrderType.Unset or OrderType.Limit (limit order).

  • tif (TIF) – time in force, TIF.Unset, TIF.GTC (Good to Cancel), TIF.IOC (Execute immediately, even partially, the rest is cancelled).

  • exec_instr (ExecInstructions) – execution instruction, ExecInstructions.Unset or ExecInstructions.PostOnly

  • nullable_attachment (Optional[object]) – an any-type object researchers want to keep track of in the lifecycle of this order. It would be attached to this order. Could be None.

Return type

None

apply_cancel_order_ack(client_id)[source]

To be invoked upon reception of a CancelOrderAck msg from the exchange (e.g. use it in IStrategy.on_cancel_order_ack` callback method of the strat). Note that this does not mean the order is effectively cancelled, simply that the cancel request has been received by the exchange. The effective cancellation of the order happens by invoking apply_order_cancelled(). No-op if the client_id does not exist.

Parameters

client_id (str) – client_id of the order for which the CancelOrderAck has been received.

Return type

None

Example

class DemoStrat():
    def __init__(self, cfg):  # or no cfg
        ...
    ......
    def on_cancel_order_ack(self, exchange: Exchange, client_id: str):
        self._order_mgr_.apply_cancel_order_ack(client_id=client_id)
        self.log_.info("on_cancel_order_ack: {ep} {clid}".format(ep=exchange.name, clid=client_id))
apply_cancel_order_reject(client_id)[source]

To be invoked upon reception of a CancelOrderReject msg from the exchange (e.g. use it in IStrategy.on_cancel_order_reject` callback method of the strat). No-op if the client_id does not exist.

Parameters

client_id (str) – client_id of the order for which the CancelOrderReject has been received.

Return type

None

Example

class DemoStrat():
    def __init__(self, cfg):  # or no cfg
        ...
    ......
    def on_cancel_order_reject(self, exchange: Exchange, client_id: str, reject_code: GtwRejectCode, reject_reason: str):
        self._order_mgr_.apply_cancel_order_reject(client_id=client_id)
        self.log_.info("on_cancel_order_reject: {ep} {clid} {rej_code} '{rej_detail}'".format(ep=exchange.name, clid=client_id, rej_code=reject_code.name, rej_detail=reject_reason))
apply_modify_order_ack(client_id, leaves_qty)[source]

To be invoked upon reception of a ModifyOrderAck msg from the exchange (e.g. use it in IStrategy.on_modify_order_ack callback method of the strat). No-op if the client_id does not exist. An example is provided below (could check it in demo strat as well). An example is provided below.

Parameters
  • client_id (str) – client_id of the order for which the ModifyOrderAck has been received.

  • leaves_qty (Qty) – leaves_qty: quantity left as provided by the exchange.

Return type

None

Example

class DemoStrat():
    def __init__(self, cfg):  # or no cfg
        ...
    ......
    def on_modify_order_ack(self, exchange: Exchange, client_id: str, new_price: Price, new_qty: Qty, leaves_qty: Qty):
        order = self._order_mgr_.get_order_with_client_id(client_id)
        if order:
            self._order_mgr_.apply_modify_order_ack(client_id=client_id, leaves_qty=leaves_qty)
            self.log_.info("on_modify_order_ack: {ep} {clid} {qty} @ {price}".format(ep=exchange.name, clid=client_id, qty=new_qty, price=new_price))
apply_modify_order_reject(client_id)[source]

To be invoked upon reception of a ModifyOrderReject msg from the exchange (e.g. use it in IStrategy.on_modify_order_reject callback method of the strat). No-op if the client_id does not exist. An example is provided below.

Parameters

client_id (str) – client_id of the order for which the ModifyOrderReject has been received.

Return type

None

Example

class DemoStrat():
    def __init__(self, cfg):  # or no cfg
        ...
    ......
    def on_modify_order_reject(self, exchange: Exchange, client_id: str, reject_code: GtwRejectCode,
                       reject_reason: str):
        self._order_mgr_.apply_modify_order_reject(client_id=client_id)
        self.log_.info("on_modify_order_reject: {ep} {clid} {rej_code} '{rej_detail}'".format(ep=exchange.name, clid=client_id, rej_code=reject_code.name, rej_detail=reject_reason))
apply_new_order_ack(client_id, exchange_id, price, leaves_qty)[source]

To be invoked upon reception of a NewOrderAck msg from the exchange (e.g. use it in IStrategy.on_new_order_ack callback method of the strat). No-op if the client_id does not exist. An example is provided below (could check it in demo strat as well).

Parameters
  • client_id (str) – client_id (internal identification) of the order for which the NewOrderAck has been received.

  • exchange_id (str) – exchange_id for this order.

  • price (Price) – price for this order.

  • leaves_qty (Qty) – leave quantity (number of shares still open for execution) in this order.

Return type

None

Example

# An common usage for this method (Check demo strat for more details):

class DemoStrat():
    def __init__(self, cfg):  # or no cfg
        ...
    ......
    def on_new_order_ack(self, exchange: Exchange, client_id: str, exchange_id: str, instrument_id: int, side: Side, price: Price, qty: Qty, leaves_qty: Qty, order_type: OrderType, tif: TIF, exec_instr: ExecInstructions):
        order = self._order_mgr_.get_order_with_client_id(client_id)
        if order:
            self._order_mgr_.apply_new_order_ack(client_id=client_id, exchange_id=exchange_id, price=price, leaves_qty=qty)
            self.log_.info("on_new_order_ack: {ep} {clid} {side} {qty} @ {price}" .format(ep=exchange.name, side=side.name, clid=client_id, qty=qty, price=price))
apply_new_order_reject(client_id)[source]

To be invoked upon reception of a NewOrderReject msg from the exchange(e.g. use it in IStrategy.on_new_order_reject callback method of the strat). No-op if the client_id does not exist. Example is provided below, check demo strat for more details.

Parameters

client_id (str) – client_id of the order for which the NewOrderReject has been received.

Return type

None

Example

# An common usage for this method (Check demo strat for more details):

class DemoStrat():
    def __init__(self, cfg):  # or no cfg
        ...
    ......
    def on_new_order_reject(self, exchange: Exchange, client_id: str, reject_code: GtwRejectCode, reject_reason: str):
        self._order_mgr_.apply_new_order_reject(client_id=client_id)
        self.log_.info("on_new_order_reject: {ep} {clid} {rej_code} '{rej_detail}'".format(ep=exchange.name, clid=client_id, rej_code=reject_code.name, rej_detail=reject_reason))
apply_order_cancelled(client_id)[source]

To be invoked upon reception of a OrderCancelled msg from the exchange (e.g. use it in IStrategy.on_order_cancelled` callback method of the strat). No-op if the client_id does not exist.

Parameters

client_id (str) – client_id of the order for which the OrderCancelled has been received.

Return type

None

Example

class DemoStrat():
    def __init__(self, cfg):  # or no cfg
        ...
    ......
    def on_order_cancelled(self, exchange: Exchange, client_id: str, unsolicited: bool, engine_ts: int, recv_ts: int, cancel_code: CancelCode, cancel_reason: str):
        self._order_mgr_.apply_order_cancelled(client_id=client_id)
        self.log_.info("on_order_cancelled: {ep} {clid} {clx_code} '{clx_reason}' transac_ts={ex_ts} recv_ts={rx_ts}".format(ep=exchange.name, clid=client_id, clx_code=cancel_code.name, clx_reason=cancel_reason, ex_ts=engine_ts, rx_ts=recv_ts))))
apply_order_execution(client_id, fill_qty, nullable_leaves_qty)[source]

To be invoked upon reception of a OrderExecution msg from the exchange (e.g. use it in IStrategy.on_order_execution` callback method of the strat). No-op if the client_id does not exist. An example is provided below (could check it in demo strat as well).

Parameters
  • client_id (str) – client_id of the order for which the fill has been received.

  • fill_qty (Qty) – fill quantity of this order.

  • nullable_leaves_qty (Qty) – quantity left as provided by the exchange. Could be None.

Returns

  • typing.Optional[bool] (None if the order doesn’t exist. If the order exists, True if the order has been fully)

  • executed, False otherwise.

Example

An example usage of this method is as follows. Use order mgr to confirm the order is fully executed in order to start calculating the realized PNL, etc. Check demo strat for more details.

class DemoStrat():
    def __init__(self, cfg):  # or no cfg
        ...
    ......
    def on_order_execution(self, exchange: Exchange, client_id: str, side: Side, price: Price, fill_qty: Qty, leaves_qty: Qty, exec_ts: int, recv_ts: int):
        if leaves_qty and leaves_qty.is_null():
            leaves_qty = None
        order = self._order_mgr_.get_order_with_client_id(client_id)
        if order:
            is_fully_executed = self._order_mgr_.apply_order_execution(client_id=client_id, fill_qty=fill_qty, nullable_leaves_qty=leaves_qty)
            if is_fully_executed is not None:
                # could start to calculate PNL, etc. (check demo strat for more details)
                ......
check_for_stale_orders(now_ts)[source]

Check if there are stale orders and returns them in a list. An order is considered stale if it is left in a ‘pending state’ for more than 10 seconds. More specifically if a prepare_new, prepare_modify, prepare_cancel is left unanswered, or if an order is not effectively cancelled after its cancel request has been acked.

Parameters

now_ts (int) – current timestamp in milliseconds.

Returns

typing.List[:obj:`.Order`]

Return type

list of stale orders.

get_all_orders()[source]

Get a list of all orders currently managed by this instance. Includes all orders expect those that have been fully executed or cancelled, and have no pending requests.

Returns

typing.List[:obj:`.Order`]

Return type

list of all orders managed by this instance.

get_num_open_orders()[source]

Get the number of open orders on both side. Note that a pending-new order for which the NewOrderAck has not been received is not considered to be an open order.

Returns

int

Return type

number of open orders.

get_num_open_orders_on_side(side)[source]

Get the number of open orders on the given side. Note that a pending-new order for which the NewOrderAck has not been received is not considered to be an open order.

Parameters

side (Side) – side for which the number of orders is requested.

Returns

int

Return type

number of open orders on the corresponding side.

get_open_orders()[source]

Get a list of all the open orders. Note that a pending-new order for which the NewOrderAck has not been received is not considered to be an open order.

Returns

typing.List[:obj:`.Order`]

Return type

list of open orders.

get_open_orders_on_side(side)[source]

Get a list of all the open orders on the provided side. Note that a pending-new order for which the NewOrderAck has not been received is not considered to be an open order.

Parameters

side (Side) – side for which the list of orders is requested.

Returns

typing.List[:obj:`.Order`]

Return type

list of open orders (Order objects) for the corresponding side.

get_order_with_client_id(client_id)[source]

Get an order provided its client_id.

Parameters

client_id (str) – client_id (internal identification) of the order to retrieve.

Returns

:obj:`.Order`

Return type

order with matching client_id, None if not found.

get_order_with_exchange_id(exchange_id)[source]

Get an order provided its exchange_id.

Parameters

exchange_id (str) – exchange_id (external identification) of the order to retrieve.

Returns

:obj:`.Order`

Return type

order with matching exchange_id, None if not found.

prepare_cancel_order(client_id)[source]

Prepare the cancellation of an order given its client_id. Return True if the order can be cancelled, meaning no other request is pending. An example is provided below (could check it in demo strat as well).

Parameters

client_id (str) – client_id of the order to be cancelled.

Returns

bool

Return type

True if the order can be cancelled, meaning no other request is pending, False otherwise.

Example

class DemoStrat():
    def __init__(self, cfg):  # or no cfg
        ...
    ......
    def CancelOrder(self, order: Order, instrument_id: int, reason = 'cancel_order'):
        order_id = order.get_client_id()
        if order.is_new_pending() or order.is_modify_pending() or order.is_cancel_pending():
            self.log_.info("Cannot cacenl Order: " + str(order_id) + " in " + order_state + " state")
            return
        ready_to_send_cancel = self._order_mgr_.prepare_cancel_order(client_id=order_id)
        if ready_to_send_cancel:
            self.env_.send_cancel_order(exchange=self._exch_, instrument_id=instrument_id, client_ord_id=order_id)
            self.env_.publish_telegram("Sent a cancel: " + str(order_id) + " reason:" + reason)

Then use CancelOrder() method to cancel the orders safely in the strat.

prepare_modify_order(client_id, new_price, new_qty)[source]

Prepare the modification of an order given the provided parameters. Return true if the modification can be done, meaning the order has NO other pending request. This method should be used before sending modify order vid trading environment. An example is provided below.

Parameters
  • client_id (str) – client_id of the order.

  • new_price (Price) – new price of the order.

  • new_qty (Qty) – new price of the order.

Returns

bool

Return type

True if the order can be modified, False otherwise.

Example

class DemoStrat():
    def __init__(self, cfg):  # or no cfg
        ...
    ......
    def ModifyOrder(self, client_id: str, side: Side, price: float, qty: Qty, reason: str, exch: Exchange):
        # may need to format price and qty first. Check demo strat for more details
        can_be_modified = self._order_mgr_.prepare_modify_order(client_id=client_id, new_price=price, new_qty=qty)
        if can_be_modified:
            self.env_.send_modify_order(exchange=exch, client_ord_id=client_id, new_price=price, new_qty=qty)

Then use ModifyOrder() method to send modify orders and keep track of order status inside the strat

prepare_new_order(instrument_id, side, price, qty, order_type, tif, exec_instr, nullable_attachment)[source]

To be called before sending an order. If no cancel-all is pending, prepare a new Order instance based on the given parameters. The created order will have a uniquely generated client_id. Example usage is provided below.

Parameters
  • instrument_id (int) – product of this order, represented via its ID, e.g. 30000000000 represents BTC-USD inverse perpetual in Bybit.

  • side (Side) – side of this order, Side.Buy or Side.Sell.

  • price (Price) – price of this order.

  • qty (Qty) – quantity of this order.

  • order_type (OrderType) – type of this order, OrderType.Unset or OrderType.Limit (limit order).

  • tif (TIF) – time in force, TIF.Unset, TIF.GTC (Good to Cancel), TIF.IOC (Execute immediately, even partially, the rest is cancelled).

  • exec_instr (ExecInstructions) – execution instruction, ExecInstructions.Unset or ExecInstructions.PostOnly

  • nullable_attachment (Optional[object]) – an any-type object researchers want to keep track of in the lifecycle of this order. It would be attached to this order. Could be None.

Return type

None if a cancel-all is pending. Otherwise, it will return a valid Order instance.

Example

# An common usage for this method (Check demo strat for more details):

class DemoStrat():
    def __init__(self, cfg):  # or no cfg
        ...
    ......
    def SendOrder(self, side: Side, price: float, qty: Qty, reason: str, exch: Exchange, instrument_id: int, exec_instr=ExecInstructions.Unset, ord_type=OrderType.Limit, tif=TIF.GTC, close_position: bool = False):
        # may need to round up/down the price and qty first. Check demo strat for more details
        order = self._order_mgr_.prepare_new_order(instrument_id=instrument_id, side=side, price=price, qty=qty, exec_instr=exec_instr, order_type=OrderType.Limit, tif=TIF.GTC, nullable_attachment=reason)
        if order:
            new_order_clid = order.get_client_id()
            self.env_.send_new_order(client_id=new_order_clid, exchange=exch, instrument_id=instrument_id, side=side, price=price, qty=qty, exec_instr=exec_instr, ord_type, time_in_force=tif, close_position=close_position)
    ......

Then use this SendOrder() method to send the orders into the trading system, and keep track of this order inside the strat.