Skip to content

Commit 849e2a1

Browse files
authored
1.107.1
See release notes in RELEASE.md
2 parents 9e2924e + 22c8bd2 commit 849e2a1

File tree

12 files changed

+545
-166
lines changed

12 files changed

+545
-166
lines changed

CHANGES.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
## NautilusTrader 1.107.0 Beta - Release Notes
2+
3+
The main thrust of this release is to refine some subtleties relating to order
4+
matching and amendment behaviour for improved realism. This involved a fairly substantial refactoring
5+
of `SimulatedExchange` to manage its complexity, and support extending the order types.
6+
7+
The `post_only` flag for LIMIT orders now results in the expected behaviour regarding
8+
when a marketable limit order will become a liquidity `TAKER` during order placement
9+
and amendment.
10+
11+
Test coverage was moderately increased.
12+
13+
### Breaking Changes
14+
None
15+
16+
### Enhancements
17+
- Refactored `SimulatedExchange` order matching and amendment logic.
18+
- Add `risk` sub-package to group risk components.
19+
20+
### Fixes
21+
- `StopLimitOrder` triggering behaviour.
22+
- All flake8 warnings.
23+
24+
## NautilusTrader 1.106.0 Beta - Release Notes
25+
26+
The main thrust of this release is to introduce the Interactive Brokers
27+
integration, and begin adding platform capabilities to support this effort.
28+
29+
### Breaking Changes
30+
- `from_serializable_string` methods changed to `from_serializable_str`.
31+
32+
### Enhancements
33+
- Scaffold Interactive Brokers integration in `adapters/ib`.
34+
- Add the `Future` instrument type.
35+
- Add the `StopLimitOrder` order type.
36+
- Add the `Data` and `DataType` types to support custom data handling.
37+
- Add the `Security` identifier types initial implementation to support extending the platforms capabilities.
38+
39+
### Fixes
40+
- `BracketOrder` correctness.
41+
- CCXT precision parsing bug.
42+
- Some log formatting.

RELEASE.md

+8-32
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,18 @@
1-
# NautilusTrader 1.107.0 Beta Release Notes
1+
## NautilusTrader 1.107.1 Beta - Release Notes
22

3-
The main thrust of this release is to refine some subtleties relating to order
4-
matching and amendment behaviour for improved realism. This involved a fairly substantial refactoring
5-
of `SimulatedExchange` to manage its complexity, and support extending the order types.
3+
This is a patch release which applies various fixes and refactorings.
64

7-
The `post_only` flag for LIMIT orders now results in the expected behaviour regarding
8-
when a marketable limit order will become a liquidity `TAKER` during order placement
9-
and amendment.
10-
11-
Test coverage was moderately increased.
5+
The behaviour of the `StopLimitOrder` continued to be fixed and refined.
6+
`SimulatedExchange` was refactored further to reduce complexity.
127

138
### Breaking Changes
149
None
1510

1611
### Enhancements
17-
- Refactored `SimulatedExchange` order matching and amendment logic.
18-
- Add `risk` sub-package to group risk components.
12+
None
1913

2014
### Fixes
15+
- `TRIGGERED` states in order FSM.
2116
- `StopLimitOrder` triggering behaviour.
22-
- All flake8 warnings.
23-
24-
# NautilusTrader 1.106.0 Beta Release Notes
25-
26-
The main thrust of this release is to introduce the Interactive Brokers
27-
integration, and begin adding platform capabilities to support this effort.
28-
29-
### Breaking Changes
30-
- `from_serializable_string` methods changed to `from_serializable_str`.
31-
32-
### Enhancements
33-
- Scaffold Interactive Brokers integration in `adapters/ib`.
34-
- Add the `Future` instrument type.
35-
- Add the `StopLimitOrder` order type.
36-
- Add the `Data` and `DataType` types to support custom data handling.
37-
- Add the `Security` identifier types initial implementation to support extending the platforms capabilities.
38-
39-
### Fixes
40-
- `BracketOrder` correctness.
41-
- CCXT precision parsing bug.
42-
- Some log formatting.
17+
- `OrderFactory.stop_limit` missing `post_only` and `hidden`.
18+
- `Order` and `StopLimitOrder` `__repr__` string (duplicate id).

docs/source/api_reference/backtest.rst

+20-12
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,28 @@ Backtest
33

44
.. automodule:: nautilus_trader.backtest
55

6+
Data Client
7+
-----------
68

7-
Config
8-
------
9+
.. automodule:: nautilus_trader.backtest.data_client
10+
:show-inheritance:
11+
:inherited-members:
12+
:members:
13+
:member-order: bysource
14+
15+
Data Container
16+
--------------
917

10-
.. automodule:: nautilus_trader.backtest.config
18+
.. automodule:: nautilus_trader.backtest.data_container
1119
:show-inheritance:
1220
:inherited-members:
1321
:members:
1422
:member-order: bysource
1523

16-
Data
17-
----
24+
Data Producer
25+
-------------
1826

19-
.. automodule:: nautilus_trader.backtest.data
27+
.. automodule:: nautilus_trader.backtest.data_producer
2028
:show-inheritance:
2129
:inherited-members:
2230
:members:
@@ -58,19 +66,19 @@ Loaders
5866
:members:
5967
:member-order: bysource
6068

61-
Logging
62-
-------
69+
Models
70+
------
6371

64-
.. automodule:: nautilus_trader.backtest.logging
72+
.. automodule:: nautilus_trader.backtest.models
6573
:show-inheritance:
6674
:inherited-members:
6775
:members:
6876
:member-order: bysource
6977

70-
Models
71-
------
78+
Modules
79+
-------
7280

73-
.. automodule:: nautilus_trader.backtest.models
81+
.. automodule:: nautilus_trader.backtest.modules
7482
:show-inheritance:
7583
:inherited-members:
7684
:members:

nautilus_trader/backtest/exchange.pyx

+38-49
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ from nautilus_trader.model.c_enums.liquidity_side cimport LiquiditySide
2727
from nautilus_trader.model.c_enums.oms_type cimport OMSType
2828
from nautilus_trader.model.c_enums.order_side cimport OrderSide
2929
from nautilus_trader.model.c_enums.order_side cimport OrderSideParser
30-
from nautilus_trader.model.c_enums.order_state cimport OrderState
3130
from nautilus_trader.model.c_enums.order_type cimport OrderType
3231
from nautilus_trader.model.c_enums.price_type cimport PriceType
3332
from nautilus_trader.model.commands cimport AmendOrder
@@ -499,10 +498,6 @@ cdef class SimulatedExchange:
499498
self.exec_client.handle_event(accepted)
500499

501500
cdef inline void _reject_order(self, Order order, str reason) except *:
502-
if order.state_c() != OrderState.SUBMITTED:
503-
self._log.error(f"Cannot reject order: state was {order.state_string_c()}.")
504-
return
505-
506501
# Generate event
507502
cdef OrderRejected rejected = OrderRejected(
508503
self.exec_client.account_id,
@@ -537,17 +532,8 @@ cdef class SimulatedExchange:
537532
)
538533
return # Cannot amend order
539534

540-
cdef Price bid = self._market_bids.get(order.symbol)
541-
cdef Price ask = self._market_asks.get(order.symbol)
542-
543-
# Check market exists
544-
if bid is None or ask is None: # Market not initialized
545-
self._cancel_reject(
546-
cl_ord_id,
547-
"amend order",
548-
f"no market for {order.symbol}",
549-
)
550-
return # Cannot amend order
535+
cdef Price bid = self._market_bids[order.symbol] # Market must exist
536+
cdef Price ask = self._market_asks[order.symbol] # Market must exist
551537

552538
if order.type == OrderType.LIMIT:
553539
self._amend_limit_order(order, qty, price, bid, ask)
@@ -689,7 +675,7 @@ cdef class SimulatedExchange:
689675
raise RuntimeError(f"Invalid order type")
690676

691677
cdef inline void _process_market_order(self, MarketOrder order, Price bid, Price ask) except *:
692-
self._accept_order(order) # Some exchanges just immediately fill
678+
self._accept_order(order)
693679

694680
# Immediately fill marketable order
695681
self._fill_order(
@@ -703,7 +689,7 @@ cdef class SimulatedExchange:
703689
if self._is_limit_marketable(order.side, order.price, bid, ask):
704690
self._reject_order(
705691
order,
706-
f"{OrderSideParser.to_str(order.side)} POST_ONLY LIMIT order "
692+
f"POST_ONLY LIMIT {OrderSideParser.to_str(order.side)} order "
707693
f"limit px of {order.price} would have been a TAKER: bid={bid}, ask={ask}",
708694
)
709695
return # Invalid price
@@ -722,8 +708,8 @@ cdef class SimulatedExchange:
722708
if self._is_stop_marketable(order.side, order.price, bid, ask):
723709
self._reject_order(
724710
order,
725-
f"{OrderSideParser.to_str(order.side)} STOP order "
726-
f"px of {order.price} was in the market: bid={bid}, ask={ask}",
711+
f"STOP {OrderSideParser.to_str(order.side)} order "
712+
f"stop px of {order.price} was in the market: bid={bid}, ask={ask}",
727713
)
728714
return # Invalid price
729715

@@ -735,8 +721,8 @@ cdef class SimulatedExchange:
735721
if self._is_stop_marketable(order.side, order.trigger, bid, ask):
736722
self._reject_order(
737723
order,
738-
f"{OrderSideParser.to_str(order.side)} STOP_LIMIT order "
739-
f"trigger px of {order.trigger} was in the market: bid={bid}, ask={ask}",
724+
f"STOP_LIMIT {OrderSideParser.to_str(order.side)} order "
725+
f"trigger stop px of {order.trigger} was in the market: bid={bid}, ask={ask}",
740726
)
741727
return # Invalid price
742728

@@ -758,8 +744,8 @@ cdef class SimulatedExchange:
758744
self._cancel_reject(
759745
order.cl_ord_id,
760746
"amend order",
761-
f"{OrderSideParser.to_str(order.side)} POST_ONLY LIMIT order "
762-
f"amended limit px of {price} would have been TAKER: bid={bid}, ask={ask}",
747+
f"POST_ONLY LIMIT {OrderSideParser.to_str(order.side)} order "
748+
f"amended limit px of {price} would have been a TAKER: bid={bid}, ask={ask}",
763749
)
764750
return # Cannot amend order
765751
else:
@@ -784,7 +770,7 @@ cdef class SimulatedExchange:
784770
self._cancel_reject(
785771
order.cl_ord_id,
786772
"amend order",
787-
f"{OrderSideParser.to_str(order.side)} STOP order "
773+
f"STOP {OrderSideParser.to_str(order.side)} order "
788774
f"amended stop px of {price} was in the market: bid={bid}, ask={ask}",
789775
)
790776
return # Cannot amend order
@@ -806,7 +792,7 @@ cdef class SimulatedExchange:
806792
self._cancel_reject(
807793
order.cl_ord_id,
808794
"amend order",
809-
f"{OrderSideParser.to_str(order.side)} STOP_LIMIT order "
795+
f"STOP_LIMIT {OrderSideParser.to_str(order.side)} order "
810796
f"amended stop px trigger of {price} was in the market: bid={bid}, ask={ask}",
811797
)
812798
return # Cannot amend order
@@ -819,8 +805,8 @@ cdef class SimulatedExchange:
819805
self._cancel_reject(
820806
order.cl_ord_id,
821807
"amend order",
822-
f"{OrderSideParser.to_str(order.side)} POST_ONLY LIMIT order "
823-
f"amended limit px of {price} would have been TAKER: bid={bid}, ask={ask}",
808+
f"POST_ONLY LIMIT {OrderSideParser.to_str(order.side)} order "
809+
f"amended limit px of {price} would have been a TAKER: bid={bid}, ask={ask}",
824810
)
825811
return # Cannot amend order
826812
else:
@@ -873,33 +859,36 @@ cdef class SimulatedExchange:
873859
self._fill_order(
874860
order,
875861
self._fill_price_stop(order.symbol, order.side, order.price),
876-
LiquiditySide.TAKER,
862+
LiquiditySide.TAKER, # Triggered stop places market order
877863
)
878864

879865
cdef inline void _match_stop_limit_order(self, StopLimitOrder order, Price bid, Price ask) except *:
880-
if not order.is_triggered:
866+
if order.is_triggered:
867+
if self._is_limit_matched(order.side, order.price, bid, ask):
868+
self._fill_order(
869+
order,
870+
order.price, # Price is 'guaranteed' (negative slippage not currently modeled)
871+
LiquiditySide.MAKER, # Providing liquidity
872+
)
873+
else: # Order not triggered
881874
if self._is_stop_triggered(order.side, order.trigger, bid, ask):
882875
self._trigger_order(order)
883876

884-
# Check for immediate fill
885-
if self._is_limit_marketable(order.side, order.price, bid, ask):
886-
if order.is_post_only: # Would be liquidity taker
887-
del self._working_orders[order.cl_ord_id] # Remove order from working orders
888-
self._reject_order(order, "post-only order would have been TAKER")
889-
else:
890-
self._fill_order(
891-
order,
892-
self._fill_price_taker(order.symbol, order.side, bid, ask),
893-
LiquiditySide.TAKER, # Immediate fill takes liquidity
894-
)
895-
return # Triggered, rejected or filled
896-
897-
if self._is_limit_matched(order.side, order.price, bid, ask):
898-
self._fill_order(
899-
order,
900-
order.price, # Price is 'guaranteed' (negative slippage not currently modeled)
901-
LiquiditySide.MAKER, # Providing liquidity
902-
)
877+
# Check for immediate fill
878+
if self._is_limit_marketable(order.side, order.price, bid, ask):
879+
if order.is_post_only: # Would be liquidity taker
880+
del self._working_orders[order.cl_ord_id] # Remove order from working orders
881+
self._reject_order(
882+
order,
883+
f"POST_ONLY LIMIT {OrderSideParser.to_str(order.side)} order "
884+
f"limit px of {order.price} would have been a TAKER: bid={bid}, ask={ask}",
885+
)
886+
else:
887+
self._fill_order(
888+
order,
889+
self._fill_price_taker(order.symbol, order.side, bid, ask),
890+
LiquiditySide.TAKER, # Immediate fill takes liquidity
891+
)
903892

904893
cdef inline bint _is_limit_marketable(self, OrderSide side, Price order_price, Price bid, Price ask) except *:
905894
if side == OrderSide.BUY:

nautilus_trader/common/factories.pxd

+2
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,9 @@ cdef class OrderFactory:
8989
Price trigger,
9090
TimeInForce time_in_force=*,
9191
datetime expire_time=*,
92+
bint post_only=*,
9293
bint reduce_only=*,
94+
bint hidden=*,
9395
)
9496

9597
cpdef BracketOrder bracket(

0 commit comments

Comments
 (0)