From fe7b8a585824799e82718b158b6ae7c41ffdd2c8 Mon Sep 17 00:00:00 2001 From: Chris Sellers Date: Sun, 29 Oct 2023 16:09:41 +1100 Subject: [PATCH] Update docs --- docs/concepts/orders.md | 25 ++++ docs/concepts/strategies.md | 171 +++++++++++++++++++------- nautilus_trader/execution/manager.pyx | 2 +- 3 files changed, 153 insertions(+), 45 deletions(-) diff --git a/docs/concepts/orders.md b/docs/concepts/orders.md index bcf7a3bf419d..b5c6d2bbb27e 100644 --- a/docs/concepts/orders.md +++ b/docs/concepts/orders.md @@ -34,6 +34,31 @@ is not available, then the system will not submit the order and an error will be a clear explanatory message. ``` +### Terminology + +- An order is **aggressive** if the type is `MARKET` or if its executing like a `MARKET` order (taking liquidity). +- An order is **passive** if the type is not `MARKET` (providing liquidity). +- An order is **active local** if it is still within the local system boundary in one of the following three (non-terminal) status: + - `INITIALIZED` + - `EMULATED` + - `RELEASED` +- An order is **in-flight** when in one of the following status: + - `SUBMITTED` + - `PENDING_UPDATE` + - `PENDING_CANCEL` +- An order is **open** when in one of the following (non-terminal) status: + - `ACCEPTED` + - `TRIGGERED` + - `PENDING_UPDATE` + - `PENDING_CANCEL` + - `PARTIALLY_FILLED` +- An order is **closed** when in one of the following (terminal) status: + - `DENIED` + - `REJECTED` + - `CANCELED` + - `EXPIRED` + - `FILLED` + ## Execution Instructions Certain exchanges allow a trader to specify conditions and restrictions on diff --git a/docs/concepts/strategies.md b/docs/concepts/strategies.md index 6a8ce60e0e77..8833ab9392bc 100644 --- a/docs/concepts/strategies.md +++ b/docs/concepts/strategies.md @@ -219,21 +219,22 @@ Strategies have access to a comprehensive `Clock` which provides a number of met different timestamps, as well as setting time alerts or timers. ```{note} -See the `Clock` [API reference](../api_reference/common.md#Clock) for a complete list of available methods. +See the `Clock` [API reference](../api_reference/common.md) for a complete list of available methods. ``` #### Current timestamps While there are multiple ways to obtain current timestamps, here are two commonly used methods as examples: -**UTC Timestamp:** This method returns a timezone-aware (UTC) timestamp: +To get the current UTC timestamp as a tz-aware `pd.Timestamp`: ```python import pandas as pd + now: pd.Timestamp = self.clock.utc_now() ``` -**Unix Nanoseconds:** This method provides the current timestamp in nanoseconds since the UNIX epoch: +To get the current UTC timestamp as nanoseconds since the UNIX epoch: ```python unix_nanos: int = self.clock.timestamp_ns() ``` @@ -338,7 +339,7 @@ metrics and statistics. Refer to the `PortfolioAnalyzer` in the [API Reference](../api_reference/analysis.md) for a complete description of all available methods. -```{tip} +```{note} Also see the [Porfolio statistics](../concepts/advanced/portfolio_statistics.md) guide. ``` @@ -349,19 +350,21 @@ tailored for algorithmic trading. These commands are essential for executing str and ensuring seamless interaction with various trading venues. In the following sections, we will delve into the specifics of each command and its use cases. +```{tip} +The [Execution](../concepts/execution.md) guide explains the flow through the system, and can be helpful to read in conjunction with the below. +``` + #### Submitting orders An `OrderFactory` is provided on the base class for every `Strategy` as a convenience, reducing the amount of boilerplate required to create different `Order` objects (although these objects can still be initialized directly with the `Order.__init__(...)` constructor if the trader prefers). -The component an order flows to when submitted for execution depends on the following: - -- If an `emulation_trigger` is specified, the order will _firstly_ be sent to the `OrderEmulator` -- If an `exec_algorithm_id` is specified (with no `emulation_trigger`), the order will _firstly_ be sent to the relevant `ExecAlgorithm` (assuming it exists and has been registered correctly) -- Otherwise, the order will _firstly_ be sent to the `RiskEngine` +The component a `SubmitOrder` or `SubmitOrderList` command will flow to for execution depends on the following: -The following examples show method implementations for a `Strategy`. +- If an `emulation_trigger` is specified, the command will _firstly_ be sent to the `OrderEmulator` +- If an `exec_algorithm_id` is specified (with no `emulation_trigger`), the command will _firstly_ be sent to the relevant `ExecAlgorithm` +- Otherwise, the command will _firstly_ be sent to the `RiskEngine` This example submits a `LIMIT` BUY order for emulation (see [OrderEmulator](advanced/emulated_orders.md)): ```python @@ -369,19 +372,20 @@ from nautilus_trader.model.enums import OrderSide from nautilus_trader.model.enums import TriggerType from nautilus_trader.model.orders import LimitOrder - def buy(self) -> None: - """ - Users simple buy method (example). - """ - order: LimitOrder = self.order_factory.limit( - instrument_id=self.instrument_id, - order_side=OrderSide.BUY, - quantity=self.instrument.make_qty(self.trade_size), - price=self.instrument.make_price(5000.00), - emulation_trigger=TriggerType.LAST_TRADE, - ) - self.submit_order(order) +def buy(self) -> None: + """ + Users simple buy method (example). + """ + order: LimitOrder = self.order_factory.limit( + instrument_id=self.instrument_id, + order_side=OrderSide.BUY, + quantity=self.instrument.make_qty(self.trade_size), + price=self.instrument.make_price(5000.00), + emulation_trigger=TriggerType.LAST_TRADE, + ) + + self.submit_order(order) ``` ```{note} @@ -394,34 +398,100 @@ from nautilus_trader.model.enums import OrderSide from nautilus_trader.model.enums import TimeInForce from nautilus_trader.model.identifiers import ExecAlgorithmId - def buy(self) -> None: - """ - Users simple buy method (example). - """ - order: MarketOrder = self.order_factory.market( - instrument_id=self.instrument_id, - order_side=OrderSide.BUY, - quantity=self.instrument.make_qty(self.trade_size), - time_in_force=TimeInForce.FOK, - exec_algorithm_id=ExecAlgorithmId("TWAP"), - exec_algorithm_params={"horizon_secs": 20, "interval_secs": 2.5}, - ) - self.submit_order(order) +def buy(self) -> None: + """ + Users simple buy method (example). + """ + order: MarketOrder = self.order_factory.market( + instrument_id=self.instrument_id, + order_side=OrderSide.BUY, + quantity=self.instrument.make_qty(self.trade_size), + time_in_force=TimeInForce.FOK, + exec_algorithm_id=ExecAlgorithmId("TWAP"), + exec_algorithm_params={"horizon_secs": 20, "interval_secs": 2.5}, + ) + + self.submit_order(order) ``` -#### Managed GTD expiry +#### Canceling orders -It's possible for the strategy to manage expiry for orders with a time in force of GTD (_Good 'till Date_). -This may be desirable if the exchange/broker does not support this time in force option, or for any -reason you prefer the strategy to manage this. +Orders can be canceled individually, as a batch, or all orders for an instrument (with an optional side filter). -To use this option, pass `manage_gtd_expiry=True` to your `StrategyConfig`. When an order is submitted with -a time in force of GTD, the strategy will automatically start an internal time alert. -Once the internal GTD time alert is reached, the order will be canceled (if not already closed). +If the order is already *closed* or already pending cancel, then a warning will be logged. -Some venues (such as Binance Futures) support the GTD time in force, so to avoid conflicts when using -`managed_gtd_expiry` you should set `use_gtd=False` for your execution client config. +If the order is currently *open* then the status will become `PENDING_CANCEL`. + +The component a `CancelOrder`, `CancelAllOrders` or `BatchCancelOrders` command will flow to for execution depends on the following: + +- If the order is currently emulated, the command will _firstly_ be sent to the `OrderEmulator` +- If an `exec_algorithm_id` is specified (with no `emulation_trigger`), and the order is still active within the local system, the command will _firstly_ be sent to the relevant `ExecAlgorithm` +- Otherwise, the order will _firstly_ be sent to the `ExecutionEngine` + +```{note} +Any managed GTD timer will also be canceled after the command has left the strategy. +``` + +The following shows how to cancel an individual order: +```python + +self.cancel_order(order) + +``` + +The following shows how to cancel a batch of orders: +```python +from nautilus_trader.model import Order + + +my_order_list: list[Order] = [order1, order2, order3] +self.cancel_orders(my_order_list) + +``` + +The following shows how to cancel all orders: + +```python + +self.cancel_all_orders() + +``` + +#### Modifying orders + +Orders can be modified individually when emulated, or *open* on a venue (if supported). + +If the order is already *closed* or already pending cancel, then a warning will be logged. +If the order is currently *open* then the status will become `PENDING_UPDATE`. + +```{warning} +At least one value must differ from the original order for the command to be valid. +``` + +The component a `ModifyOrder` command will flow to for execution depends on the following: + +- If the order is currently emulated, the command will _firstly_ be sent to the `OrderEmulator` +- Otherwise, the order will _firstly_ be sent to the `RiskEngine` + +```{note} +Once an order is under the control of an execution algorithm, it cannot be directly modified by a strategy (only canceled). +``` + +The following shows how to modify the size of `LIMIT` BUY order currently *open* on a venue: +```python +from nautilus_trader.model import Quantity + + +new_quantity: Quantity = Quantity.from_int(5) +self.modify_order(order, new_quantity) + +``` + +```{note} +The price and trigger price can also be modified (when emulated or supported by a venue). + +``` ## Configuration @@ -482,6 +552,19 @@ Even though it often makes sense to define a strategy which will trade a single instrument. The number of instruments a single strategy can work with is only limited by machine resources. ``` +### Managed GTD expiry + +It's possible for the strategy to manage expiry for orders with a time in force of GTD (_Good 'till Date_). +This may be desirable if the exchange/broker does not support this time in force option, or for any +reason you prefer the strategy to manage this. + +To use this option, pass `manage_gtd_expiry=True` to your `StrategyConfig`. When an order is submitted with +a time in force of GTD, the strategy will automatically start an internal time alert. +Once the internal GTD time alert is reached, the order will be canceled (if not already *closed*). + +Some venues (such as Binance Futures) support the GTD time in force, so to avoid conflicts when using +`managed_gtd_expiry` you should set `use_gtd=False` for your execution client config. + ### Multiple strategies If you intend running multiple instances of the same strategy, with different diff --git a/nautilus_trader/execution/manager.pyx b/nautilus_trader/execution/manager.pyx index 3896ead82bf9..da6902e2840c 100644 --- a/nautilus_trader/execution/manager.pyx +++ b/nautilus_trader/execution/manager.pyx @@ -271,7 +271,7 @@ cdef class OrderManager: Parameters ---------- order : Order - The order the check. + The order to check. Returns -------