Skip to content

Commit e13b080

Browse files
authored
Release 1.173.0
See release notes.
2 parents 7b5e5d3 + ebf5040 commit e13b080

28 files changed

+383
-301
lines changed

.github/workflows/release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ jobs:
210210
fail-fast: false
211211
matrix:
212212
arch: [x64]
213-
os: [ubuntu-latest, macos-latest, windows-latest]
213+
os: [ubuntu-20.04, ubuntu-latest, macos-latest, macos-13, windows-latest]
214214
python-version: ["3.9", "3.10", "3.11"]
215215
name: publish-wheels - Python ${{ matrix.python-version }} (${{ matrix.arch }} ${{ matrix.os }})
216216
runs-on: ${{ matrix.os }}

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ logs/
4040
venv*/
4141

4242
.DS_Store
43-
null-ls*
43+
.null-ls*
4444
PERF.JSON
4545
output.json
4646
*dask-worker-space*

.pre-commit-config.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ repos:
8484
exclude: "docs/_pygments/monokai.py"
8585

8686
- repo: https://github.com/charliermarsh/ruff-pre-commit
87-
rev: v0.0.263
87+
rev: v0.0.264
8888
hooks:
8989
- id: ruff
9090
args: ["--fix"]

RELEASES.md

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
# NautilusTrader 1.173.0 Beta
2+
3+
Released on 5th May 2023 (UTC).
4+
5+
### Breaking Changes
6+
None
7+
8+
### Enhancements
9+
None
10+
11+
### Fixes
12+
- Fixed `BacktestEngine` processing of venue(s) message queue based off time event `ts_init`
13+
- Fixed `Position.signed_decimal_qty` (incorrect format precision in f-string), thanks for reporting @rsmb7z
14+
- Fixed trailing stop type order updates for `reduce_only` instruction, thanks for reporting @Otlk
15+
- Fixed updating of active execution algorithm orders (events weren't being cached)
16+
- Fixed condition check for applying pending events (do not apply to orders at `INITIALIZED` status)
17+
18+
---
19+
120
# NautilusTrader 1.172.0 Beta
221

322
Released on 30th April 2023 (UTC).
@@ -9,7 +28,7 @@ Released on 30th April 2023 (UTC).
928
- Renamed `Future` instrument to `FuturesContract` (avoids ambiguity)
1029
- Renamed `Option` instrument to `OptionsContract` (avoids ambiguity and naming conflicts in Rust)
1130
- Reinstate hours and minutes time component for default order and position identifiers (easier debugging, less collisions)
12-
- Setting time alerts for in the past or current time will generate an immediate `TimeEvent` (rather than being invalid).
31+
- Setting time alerts for in the past or current time will generate an immediate `TimeEvent` (rather than being invalid)
1332

1433
### Enhancements
1534
- Added new DataFusion Rust parquet data catalog backend (yet to be integrated into Python)
@@ -21,7 +40,7 @@ Released on 30th April 2023 (UTC).
2140
- Build out `ExecAlgorithm` base class for implementing 'first class' executon algorithms
2241
- Rewired execution for improved flow flexibility between emulated orders, execution algorithms and the `RiskEngine`
2342
- Improved handling for `OrderEmulator` updating of contingency orders from execution algorithms
24-
- Define public API for instruments can now import directly from `nautilus_trader.model.instruments` (denest namespace)
43+
- Define public API for instruments, can now import directly from `nautilus_trader.model.instruments` (denest namespace)
2544
- Define public API for orders, can now import directly from `nautilus_trader.model.orders` (denest namespace)
2645
- Define public API for order book, can now import directly from `nautilus_trader.model.orderbook` (denest namespace)
2746
- Now stripping debug symbols after build (reduced binary sizes)

nautilus_core/Cargo.lock

+10-10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nautilus_trader/adapters/binance/common/execution.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,10 @@ async def generate_position_status_reports(
507507
async def _submit_order(self, command: SubmitOrder) -> None:
508508
order: Order = command.order
509509

510+
if order.is_closed:
511+
self.log.warning(f"Cannot submit already closed order {command.order}.")
512+
return
513+
510514
# Check validity
511515
self._check_order_validity(order)
512516
self._log.debug(f"Submitting {order}.")
@@ -531,7 +535,7 @@ async def _submit_order(self, command: SubmitOrder) -> None:
531535
except KeyError:
532536
raise RuntimeError(f"unsupported order type, was {order.order_type}")
533537

534-
def _check_order_validity(self, order: Order):
538+
def _check_order_validity(self, order: Order) -> None:
535539
# Implement in child class
536540
raise NotImplementedError
537541

nautilus_trader/adapters/binance/futures/execution.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ async def _get_binance_active_position_symbols(
207207

208208
# -- COMMAND HANDLERS -------------------------------------------------------------------------
209209

210-
def _check_order_validity(self, order: Order):
210+
def _check_order_validity(self, order: Order) -> None:
211211
# Check order type valid
212212
if order.order_type not in self._futures_enum_parser.futures_valid_order_types:
213213
self._log.error(

nautilus_trader/adapters/binance/spot/enums.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from nautilus_trader.adapters.binance.common.enums import BinanceOrderType
2121
from nautilus_trader.model.enums import OrderType
2222
from nautilus_trader.model.enums import TimeInForce
23+
from nautilus_trader.model.enums import order_type_to_str
2324
from nautilus_trader.model.orders import Order
2425

2526

@@ -128,5 +129,5 @@ def parse_internal_order_type(self, order: Order) -> BinanceOrderType:
128129
return BinanceOrderType.TAKE_PROFIT_LIMIT
129130
else:
130131
raise RuntimeError( # pragma: no cover (design-time error)
131-
f"invalid or unsupported `OrderType`, was {order.order_type}", # pragma: no cover
132+
f"invalid or unsupported `OrderType`, was {order_type_to_str(order.order_type)}", # pragma: no cover
132133
)

nautilus_trader/adapters/binance/spot/execution.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ async def _get_binance_active_position_symbols(
172172

173173
# -- COMMAND HANDLERS -------------------------------------------------------------------------
174174

175-
def _check_order_validity(self, order: Order):
175+
def _check_order_validity(self, order: Order) -> None:
176176
# Check order type valid
177177
if order.order_type not in self._spot_enum_parser.spot_valid_order_types:
178178
self._log.error(

nautilus_trader/analysis/reporter.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def generate_positions_report(positions: list[Position]) -> pd.DataFrame:
9797
if not positions:
9898
return pd.DataFrame()
9999

100-
positions = [p.to_dict() for p in positions if p.is_closed]
100+
positions = [p.to_dict() for p in positions]
101101
if not positions:
102102
return pd.DataFrame()
103103

nautilus_trader/backtest/engine.pyx

+8
Original file line numberDiff line numberDiff line change
@@ -1077,10 +1077,12 @@ cdef class BacktestEngine:
10771077
cdef:
10781078
uint64_t i
10791079
uint64_t ts_event_init
1080+
uint64_t ts_last_init = 0
10801081
TimeEventHandler_t raw_handler
10811082
TimeEvent event
10821083
TestClock clock
10831084
object callback
1085+
SimulatedExchange exchange
10841086
for i in range(raw_handler_vec.len):
10851087
raw_handler = <TimeEventHandler_t>raw_handlers[i]
10861088
ts_event_init = raw_handler.event.ts_init
@@ -1094,6 +1096,12 @@ cdef class BacktestEngine:
10941096
callback = <object>raw_handler.callback_ptr
10951097
callback(event)
10961098

1099+
if ts_event_init != ts_last_init:
1100+
# Process exchange messages
1101+
ts_last_init = ts_event_init
1102+
for exchange in self._venues.values():
1103+
exchange.process(ts_event_init)
1104+
10971105
def _log_pre_run(self):
10981106
log_memory(self._log)
10991107

nautilus_trader/backtest/matching_engine.pxd

+2-2
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,8 @@ cdef class OrderMatchingEngine:
151151
cdef void _update_limit_order(self, Order order, Quantity qty, Price price)
152152
cdef void _update_stop_market_order(self, StopMarketOrder order, Quantity qty, Price trigger_price)
153153
cdef void _update_stop_limit_order(self, StopLimitOrder order, Quantity qty, Price price, Price trigger_price)
154-
cdef void _update_market_if_touched_order(self, MarketIfTouchedOrder order, Quantity qty, Price trigger_price)
155-
cdef void _update_limit_if_touched_order(self, LimitIfTouchedOrder order, Quantity qty, Price price, Price trigger_price)
154+
cdef void _update_market_if_touched_order(self, Order order, Quantity qty, Price trigger_price)
155+
cdef void _update_limit_if_touched_order(self, Order order, Quantity qty, Price price, Price trigger_price)
156156
cdef void _update_trailing_stop_order(self, Order order)
157157

158158
# -- ORDER PROCESSING -----------------------------------------------------------------------------

nautilus_trader/backtest/matching_engine.pyx

+12-2
Original file line numberDiff line numberDiff line change
@@ -987,7 +987,7 @@ cdef class OrderMatchingEngine:
987987

988988
cdef void _update_market_if_touched_order(
989989
self,
990-
MarketIfTouchedOrder order,
990+
Order order,
991991
Quantity qty,
992992
Price trigger_price,
993993
):
@@ -1010,7 +1010,7 @@ cdef class OrderMatchingEngine:
10101010

10111011
cdef void _update_limit_if_touched_order(
10121012
self,
1013-
LimitIfTouchedOrder order,
1013+
Order order,
10141014
Quantity qty,
10151015
Price price,
10161016
Price trigger_price,
@@ -1794,6 +1794,16 @@ cdef class OrderMatchingEngine:
17941794
if trigger_price is None:
17951795
trigger_price = order.trigger_price
17961796
self._update_limit_if_touched_order(order, qty, price, trigger_price)
1797+
elif order.order_type == OrderType.TRAILING_STOP_MARKET:
1798+
if trigger_price is None:
1799+
trigger_price = order.trigger_price
1800+
self._update_market_if_touched_order(order, qty, trigger_price)
1801+
elif order.order_type == OrderType.TRAILING_STOP_LIMIT:
1802+
if price is None:
1803+
price = order.price
1804+
if trigger_price is None:
1805+
trigger_price = order.trigger_price
1806+
self._update_limit_if_touched_order(order, qty, price, trigger_price)
17971807
else:
17981808
raise ValueError(
17991809
f"invalid `OrderType` was {order.order_type}") # pragma: no cover (design-time error)

nautilus_trader/execution/algorithm.pyx

+9-11
Original file line numberDiff line numberDiff line change
@@ -381,8 +381,6 @@ cdef class ExecAlgorithm(Actor):
381381
382382
Raises
383383
------
384-
ValueError
385-
If `primary.status` is not ``INITIALIZED``.
386384
ValueError
387385
If `primary.exec_algorithm_id` is not equal to `self.id`.
388386
ValueError
@@ -393,7 +391,6 @@ cdef class ExecAlgorithm(Actor):
393391
"""
394392
Condition.not_none(primary, "primary")
395393
Condition.not_none(quantity, "quantity")
396-
Condition.equal(primary.status, OrderStatus.INITIALIZED, "primary.status", "order_status")
397394
Condition.equal(primary.exec_algorithm_id, self.id, "primary.exec_algorithm_id", "id")
398395

399396
if reduce_primary:
@@ -468,8 +465,6 @@ cdef class ExecAlgorithm(Actor):
468465
469466
Raises
470467
------
471-
ValueError
472-
If `primary.status` is not ``INITIALIZED``.
473468
ValueError
474469
If `primary.exec_algorithm_id` is not equal to `self.id`.
475470
ValueError
@@ -482,7 +477,6 @@ cdef class ExecAlgorithm(Actor):
482477
"""
483478
Condition.not_none(primary, "primary")
484479
Condition.not_none(quantity, "quantity")
485-
Condition.equal(primary.status, OrderStatus.INITIALIZED, "primary.status", "order_status")
486480
Condition.equal(primary.exec_algorithm_id, self.id, "primary.exec_algorithm_id", "id")
487481

488482
if reduce_primary:
@@ -556,8 +550,6 @@ cdef class ExecAlgorithm(Actor):
556550
557551
Raises
558552
------
559-
ValueError
560-
If `primary.status` is not ``INITIALIZED``.
561553
ValueError
562554
If `primary.exec_algorithm_id` is not equal to `self.id`.
563555
ValueError
@@ -570,7 +562,6 @@ cdef class ExecAlgorithm(Actor):
570562
"""
571563
Condition.not_none(primary, "primary")
572564
Condition.not_none(quantity, "quantity")
573-
Condition.equal(primary.status, OrderStatus.INITIALIZED, "primary.status", "order_status")
574565
Condition.equal(primary.exec_algorithm_id, self.id, "primary.exec_algorithm_id", "id")
575566

576567
if reduce_primary:
@@ -679,6 +670,13 @@ cdef class ExecAlgorithm(Actor):
679670

680671
# Handle primary (original) order
681672
primary_command = self.cache.load_submit_order_command(order.client_order_id)
673+
cdef Order cached_order = self.cache.order(order.client_order_id)
674+
if cached_order.order_type != order.order_type:
675+
self.cache.add_order(order, primary_command.position_id, override=True)
676+
677+
# Replace commands order with transformed order
678+
primary_command.order = order
679+
682680
Condition.equal(order.strategy_id, primary_command.strategy_id, "order.strategy_id", "primary_command.strategy_id")
683681
if primary_command is None:
684682
self._log.error(
@@ -779,7 +777,7 @@ cdef class ExecAlgorithm(Actor):
779777
return # Cannot send command
780778

781779
cdef OrderPendingUpdate event
782-
if not order.is_emulated_c():
780+
if order.status != OrderStatus.INITIALIZED and not order.is_emulated_c():
783781
# Generate and apply event
784782
event = self._generate_order_pending_update(order)
785783
try:
@@ -942,7 +940,7 @@ cdef class ExecAlgorithm(Actor):
942940
return # Cannot send command
943941

944942
cdef OrderPendingCancel event
945-
if not order.is_emulated_c():
943+
if order.status != OrderStatus.INITIALIZED and not order.is_emulated_c():
946944
# Generate and apply event
947945
event = self._generate_order_pending_cancel(order)
948946
try:

nautilus_trader/execution/emulator.pyx

+12-10
Original file line numberDiff line numberDiff line change
@@ -466,16 +466,18 @@ cdef class OrderEmulator(Actor):
466466
PositionId position_id,
467467
ClientId client_id,
468468
):
469-
cdef SubmitOrder submit = SubmitOrder(
470-
trader_id=order.trader_id,
471-
strategy_id=order.strategy_id,
472-
order=order,
473-
position_id=position_id,
474-
client_id=client_id,
475-
command_id=UUID4(),
476-
ts_init=self.clock.timestamp_ns(),
477-
)
478-
self.cache.add_submit_order_command(submit)
469+
cdef SubmitOrder submit = self.cache.load_submit_order_command(order.client_order_id)
470+
if submit is None:
471+
submit = SubmitOrder(
472+
trader_id=order.trader_id,
473+
strategy_id=order.strategy_id,
474+
order=order,
475+
position_id=position_id,
476+
client_id=client_id,
477+
command_id=UUID4(),
478+
ts_init=self.clock.timestamp_ns(),
479+
)
480+
self.cache.add_submit_order_command(submit)
479481

480482
if order.emulation_trigger == TriggerType.NO_TRIGGER:
481483
if order.exec_algorithm_id is not None:

nautilus_trader/infrastructure/cache.pyx

+2-1
Original file line numberDiff line numberDiff line change
@@ -780,7 +780,8 @@ cdef class RedisCacheDatabase(CacheDatabase):
780780

781781
# Check data integrity of reply
782782
if reply > 1: # Reply = The length of the list after the push operation
783-
self._log.warning(
783+
# Dropped the log level to debug as this is expected for transformed orders
784+
self._log.debug(
784785
f"The {repr(order.client_order_id)} already existed and was appended to.",
785786
)
786787

0 commit comments

Comments
 (0)