import typing
from typing import Optional
from mlthon.basics.defs import OrderType, Side, ExecInstructions, TIF
from mlthon.basics import utils
from mlthon.basics.price import Price
from mlthon.basics.qty import Qty
from mlthon.order.order import Order, _Order
[docs]class OrderMgr:
"""
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.
"""
# Duration after which an order with a pending request is considered stale
__STALENESS_THRESHOLD_MS = 20 * 1000
def __init__(self, client_id_prefix: str):
"""OrderMgr constructor. Note that an OrderMgr instance is assumed to be associated to a single exchange.
Parameters
----------
client_id_prefix: str
the client_id of all orders prepared by this order manager will be prefixed with this
value. This value must be less than 8 characters long.
"""
if len(client_id_prefix) > 8:
raise RuntimeError("Client id prefix must be less than 8 chars long!")
self._client_id_prefix_ = client_id_prefix
self._next_client_id_ = utils.get_now_ts()
self._pending_new_orders_dict_ = {}
self._pending_modify_orders_dict_ = {}
self._pending_cancel_orders_dict_ = {}
self._orders_dict_ = {}
self._orders_dict_by_exch_id_ = {}
self._cancel_all_ts_ = 0
self._open_orders_by_side_ = [[], []] # _orders_by_side_[0] = Buy orders, _orders_by_side_[1] = Sell orders
[docs] def add_open_order(self, client_id: str, exchange_id: str, instrument_id: int, side: Side, price: Price, qty: Qty,
order_type: OrderType, tif: TIF, exec_instr: ExecInstructions,
nullable_attachment: Optional[object]):
"""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: :obj:`.Side`
side of this order, Side.Buy or Side.Sell.
price: :obj:`.Price`
price of this order.
qty: :obj:`.Qty`
quantity of this order.
order_type: :obj:`.OrderType`
type of this order, OrderType.Unset or OrderType.Limit (limit order).
tif: :obj:`.TIF`
time in force, TIF.Unset, TIF.GTC (Good to Cancel), TIF.IOC (Execute immediately, even partially, the rest
is cancelled).
exec_instr: :obj:`.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.
Returns
-------
None
"""
if self._orders_dict_.get(client_id) is not None:
raise RuntimeError("Trying to add order with duplicate client_id: '" + client_id + "'")
if self._orders_dict_by_exch_id_.get(exchange_id) is not None:
raise RuntimeError("Trying to add order with duplicate exchange_id: '" + exchange_id + "'")
order = _Order(client_id, instrument_id, side, price, qty, order_type, tif, exec_instr, nullable_attachment)
order.on_new_ack(exchange_id, price, qty)
self._orders_dict_[client_id] = order
self._orders_dict_by_exch_id_[exchange_id] = order
self._open_orders_by_side_[side.value].append(order)
[docs] def prepare_new_order(self, instrument_id: int, side: Side, price: Price, qty: Qty, order_type: OrderType,
tif: TIF, exec_instr: ExecInstructions, nullable_attachment: Optional[object]) -> Optional[Order]:
"""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: :obj:`.Side`
side of this order, Side.Buy or Side.Sell.
price: :obj:`.Price`
price of this order.
qty: :obj:`.Qty`
quantity of this order.
order_type: :obj:`.OrderType`
type of this order, OrderType.Unset or OrderType.Limit (limit order).
tif: :obj:`.TIF`
time in force, TIF.Unset, TIF.GTC (Good to Cancel), TIF.IOC (Execute immediately, even partially, the rest
is cancelled).
exec_instr: :obj:`.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.
Returns
-------
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):
.. code-block::
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.
"""
if self._cancel_all_ts_ > 0:
return None
client_id = self._client_id_prefix_ + "-" + str(self._next_client_id_)
self._next_client_id_ += 1
order = _Order(client_id, instrument_id, side, price, qty, order_type, tif, exec_instr, nullable_attachment)
self._orders_dict_[client_id] = order
self._pending_new_orders_dict_[client_id] = order
self._open_orders_by_side_[side.value].append(order)
return order
[docs] def apply_new_order_ack(self, client_id: str, exchange_id: str, price: Price, leaves_qty: Qty):
"""To be invoked upon reception of a NewOrderAck msg from the exchange (e.g. use it in
:obj:`.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: :obj:`.Price`
price for this order.
leaves_qty: :obj:`.Qty`
leave quantity (number of shares still open for execution) in this order.
Returns
-------
None
Example
-------
# An common usage for this method (Check demo strat for more details):
.. code-block::
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))
"""
order = self._orders_dict_.get(client_id, None)
if order:
if self._orders_dict_by_exch_id_.get(exchange_id) is not None:
raise RuntimeError("Trying to add order with duplicate exchange_id: '" + exchange_id + "'")
self._pending_new_orders_dict_.pop(client_id)
# TODO: remove this once the framework synthesizes an order ack before an early execution, as it may
# happen on bybit. As a result, the order maybe fully executed (and marked for deletion) when the ack comes
# in
if not order.is_marked_for_deletion():
self._orders_dict_by_exch_id_[exchange_id] = order
order.on_new_ack(exchange_id, price, leaves_qty)
else:
self._orders_dict_.pop(client_id)
self._open_orders_by_side_[order.get_side().value].remove(order)
order.on_new_ack(exchange_id, price, Qty.zero())
if order.has_any_pending_request():
raise RuntimeError("")
[docs] def apply_new_order_reject(self, client_id: str):
"""To be invoked upon reception of a NewOrderReject msg from the exchange(e.g. use it in
:obj:`.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.
Returns
-------
None
Example
-------
# An common usage for this method (Check demo strat for more details):
.. code-block::
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))
"""
if client_id in self._orders_dict_.keys():
order = self._orders_dict_.pop(client_id)
order.on_new_reject()
self._pending_new_orders_dict_.pop(client_id)
self._open_orders_by_side_[order.get_side().value].remove(order)
[docs] def prepare_modify_order(self, client_id: str, new_price: typing.Optional[Price],
new_qty: typing.Optional[Qty]) -> bool:
"""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: :obj:`.Price`
new price of the order.
new_qty: :obj:`.Qty`
new price of the order.
Returns
-------
bool: True if the order can be modified, False otherwise.
Example
-------
.. code-block::
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
"""
order = self.__get_order_with_client_id(client_id)
if self._cancel_all_ts_ == 0 and order.prepare_modify(new_price, new_qty):
self._pending_modify_orders_dict_[client_id] = order
return True
else:
return False
[docs] def apply_modify_order_ack(self, client_id: str, leaves_qty: Qty):
"""To be invoked upon reception of a ModifyOrderAck msg from the exchange (e.g. use it in
:obj:`.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: :obj:`.Qty`
leaves_qty: quantity left as provided by the exchange.
Returns
-------
None
Example
-------
.. code-block::
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))
"""
order = self._orders_dict_.get(client_id, None)
if order:
order.on_modify_ack(leaves_qty)
self._pending_modify_orders_dict_.pop(client_id)
if order.is_marked_for_deletion() and not order.has_any_pending_request():
self.__remove_order(order)
[docs] def apply_modify_order_reject(self, client_id: str):
"""To be invoked upon reception of a ModifyOrderReject msg from the exchange (e.g. use it in
:obj:`.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.
Returns
-------
None
Example
-------
.. code-block::
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))
"""
if client_id in self._orders_dict_.keys():
order = self.__get_order_with_client_id(client_id)
order.on_modify_reject()
self._pending_modify_orders_dict_.pop(client_id)
if order.is_marked_for_deletion() and not order.has_any_pending_request():
self.__remove_order(order)
[docs] def prepare_cancel_order(self, client_id: str) -> bool:
"""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: True if the order can be cancelled, meaning no other request is pending, False otherwise.
Example
-------
.. code-block::
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.
"""
order = self.__get_order_with_client_id(client_id)
if not order.has_any_pending_request() and self._cancel_all_ts_ == 0:
order.prepare_cancel()
self._pending_cancel_orders_dict_[client_id] = order
return True
else:
return False
[docs] def apply_cancel_order_ack(self, client_id: str):
"""To be invoked upon reception of a CancelOrderAck msg from the exchange (e.g. use it in
:obj:`.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.
Returns
-------
None
Example
-------
.. code-block::
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))
"""
order = self._orders_dict_.get(client_id, None)
if order:
order.on_cancel_ack()
self._pending_cancel_orders_dict_.pop(client_id)
if order.is_marked_for_deletion() and not order.has_any_pending_request() and order.is_cancelled():
self.__remove_order(order)
[docs] def apply_cancel_order_reject(self, client_id):
"""To be invoked upon reception of a CancelOrderReject msg from the exchange (e.g. use it in
:obj:`.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.
Returns
-------
None
Example
-------
.. code-block::
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))
"""
order = self._orders_dict_.get(client_id, None)
if order:
order.on_cancel_reject()
self._pending_cancel_orders_dict_.pop(client_id)
if order.is_marked_for_deletion() and not order.has_any_pending_request():
self.__remove_order(order)
[docs] def apply_order_cancelled(self, client_id):
"""To be invoked upon reception of a OrderCancelled msg from the exchange (e.g. use it in
:obj:`.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.
Returns
-------
None
Example
-------
.. code-block::
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))))
"""
order = self._orders_dict_.get(client_id, None)
if order:
order.on_cancelled()
if order.has_any_pending_request():
order.mark_for_deletion()
if not order.is_new_pending(): # On bybit, cancellation may come before ack. TODO: remove this when the framework synthesizes the ack
self._open_orders_by_side_[order.get_side().value].remove(order) # The order is no longer open
else:
self.__remove_order(order)
[docs] def apply_order_execution(self, client_id: str, fill_qty: Qty, nullable_leaves_qty: typing.Optional[Qty]) -> bool:
"""To be invoked upon reception of a OrderExecution msg from the exchange (e.g. use it in
:obj:`.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: :obj:`.Qty`
fill quantity of this order.
nullable_leaves_qty: :obj:`.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.
.. code-block::
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)
......
"""
order = self._orders_dict_.get(client_id, None)
if order is None:
return None
leaves_qty = order.on_execution(fill_qty, nullable_leaves_qty)
assert(leaves_qty >= Qty.zero())
if leaves_qty == Qty.zero():
if order.has_any_pending_request():
order.mark_for_deletion()
if not order.is_new_pending(): # On bybit, execution may come before ack. TODO: remove this when the framework synthesizes the ack
self._open_orders_by_side_[order.get_side().value].remove(order) # The order is no longer open
if not order.has_any_pending_request():
self.__remove_order(order)
else:
self.__remove_order(order)
return True # Fully executed
return False # Not fully executed
def __get_order_with_client_id(self, client_id: str) -> _Order:
order = self._orders_dict_.get(client_id)
if not order:
raise RuntimeError("Could not find order with client id: '" + client_id + "'!")
return order
def __remove_order(self, order: _Order):
client_id = order.get_client_id()
assert(order.has_any_pending_request() is False)
assert(self._pending_new_orders_dict_.get(client_id) is None)
assert(self._pending_modify_orders_dict_.get(client_id) is None)
assert(self._pending_cancel_orders_dict_.get(client_id) is None)
self._orders_dict_.pop(client_id)
self._orders_dict_by_exch_id_.pop(order.get_exchange_id())
if not order.is_marked_for_deletion():
self._open_orders_by_side_[order.get_side().value].remove(order)
[docs] def get_order_with_client_id(self, client_id: str) -> Optional[Order]:
"""Get an order provided its client_id.
Parameters
----------
client_id: str
client_id (internal identification) of the order to retrieve.
Returns
-------
:obj:`.Order`: order with matching client_id, None if not found.
"""
return self._orders_dict_.get(client_id, None)
[docs] def get_order_with_exchange_id(self, exchange_id: str) -> Optional[Order]:
"""Get an order provided its exchange_id.
Parameters
----------
exchange_id: str
exchange_id (external identification) of the order to retrieve.
Returns
-------
:obj:`.Order`: order with matching exchange_id, None if not found.
"""
return self._orders_dict_by_exch_id_.get(exchange_id, None)
[docs] def get_open_orders_on_side(self, side: Side) -> [typing.List[Order]]:
"""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: :obj:`.Side`
side for which the list of orders is requested.
Returns
-------
typing.List[:obj:`.Order`]: list of open orders (:obj:`.Order` objects) for the corresponding side.
"""
return self._open_orders_by_side_[side.value]
[docs] def get_open_orders(self) -> [typing.List[Order]]:
"""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`]: list of open orders.
"""
return self._open_orders_by_side_[Side.Buy.value] + self._open_orders_by_side_[Side.Sell.value]
[docs] def get_all_orders(self) -> [typing.List[Order]]:
"""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`]: list of all orders managed by this instance.
"""
all_orders = []
for order in self._orders_dict_.values():
all_orders.append(order)
return all_orders
[docs] def get_num_open_orders_on_side(self, side: Side) -> int:
"""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: :obj:`.Side`
side for which the number of orders is requested.
Returns
-------
int: number of open orders on the corresponding side.
"""
return len(self._open_orders_by_side_[side.value])
[docs] def get_num_open_orders(self) -> int:
"""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: number of open orders.
"""
return len(self._open_orders_by_side_[Side.Buy.value] + self._open_orders_by_side_[Side.Sell.value])
[docs] def check_for_stale_orders(self, now_ts: int) -> typing.List[Order]:
"""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`]: list of stale orders.
"""
stale_orders_list = []
for order in self._orders_dict_.values():
if order.is_stale(now_ts, OrderMgr.__STALENESS_THRESHOLD_MS):
stale_orders_list.append(order)
return stale_orders_list