mirror of
https://github.com/manifoldbt/manifoldbt.git
synced 2026-11-02 07:56:11 +00:00
release: v0.28.1
This commit is contained in:
committed by
Jimmy7892
parent
20e12173fe
commit
4c0b303ea3
@@ -534,7 +534,10 @@ paid tiers add scale and depth. See [pricing](https://www.manifoldbt.com/#pricin
|
||||
|
||||
## License
|
||||
|
||||
Apache 2.0 with Commons Clause. The source is available, free to use, modify and
|
||||
self-host. Reselling the software or offering it as a paid hosted service is not
|
||||
permitted. See [LICENSE](https://github.com/manifoldbt/manifoldbt/blob/master/LICENSE)
|
||||
for the full text.
|
||||
Apache 2.0 with Commons Clause, for what this repository holds (the Python
|
||||
package, the examples, the tests and the benchmarks) and for the wheels
|
||||
published on PyPI. The Rust engine inside those wheels is distributed compiled;
|
||||
its source is not published. Use, modification and self-hosting are permitted.
|
||||
Reselling the software or offering it as a paid hosted service is not. See
|
||||
[LICENSE](https://github.com/manifoldbt/manifoldbt/blob/master/LICENSE) for the
|
||||
full text.
|
||||
|
||||
@@ -3288,7 +3288,7 @@ row per event of one order:
|
||||
|---|---|
|
||||
| `A` | it entered the book (the new order of a replacement too, flagged) |
|
||||
| `E` | it executed, at its own price |
|
||||
| `C` | it executed at another price (a cross), printed or not |
|
||||
| `C` | it executed at another price than the one it is shown at (`exec_price`), printed or not |
|
||||
| `X` | part of it was cancelled |
|
||||
| `D` | it left the book: what was left of it |
|
||||
| `U` | it was replaced: it left the book, and the order replacing it follows at the same instant, at the back of its queue |
|
||||
@@ -3343,6 +3343,15 @@ after. Then, event by event:
|
||||
have met ours first: ours is served, as much as it brings, best price
|
||||
then first come among our orders it crosses (`book_cross`).
|
||||
|
||||
An execution at another price than the one the order is shown at (`C`) is read
|
||||
where it traded: that order did not wait in the queue of the level it is shown
|
||||
at (older orders there are often still resting when it executes, a tick away
|
||||
from their price), so it serves none of ours there. It is rule 2 for the
|
||||
orders of ours standing at the price it traded at, that it entered after, and
|
||||
rule 3 for those standing at a worse price. Leaving the level it is shown at,
|
||||
it takes its size out of the volume ahead of the orders of ours that it was
|
||||
shown ahead of.
|
||||
|
||||
Our fills take nothing out of the market's book: its events are replayed as
|
||||
they were. Prints against orders the book does not show (`P`) and crosses
|
||||
serve nothing: they are not in the displayed queue. Everything else is the
|
||||
@@ -3386,6 +3395,12 @@ On the day of 2025-12-09 (AAPL, SPY, QQQ, NVDA, TSLA):
|
||||
book on arrival; with rule 4 added to it, the only differences left are the
|
||||
orders that crossed the spread when they arrived (0.2 to 1.7 % of them),
|
||||
which the venue executes as takers;
|
||||
- on the half session of 2025-11-28 (AAPL), every one of the 5,170 quotes a
|
||||
maker joining the best price had served is backed by the messages of the
|
||||
day: an execution behind it, one at a worse price, or an order of the
|
||||
other side at its price. The 727 executions at another price (`C`) all
|
||||
traded one cent better than the price they are shown at, and 78 % of them
|
||||
with older orders still resting at that shown price;
|
||||
- a maker quoting one unit at the best bid and ask every 100 ms, moved when
|
||||
the best moves, inventory within five units, gives the same posts,
|
||||
cancellations, fills, inventory and cash as the same maker in that replay,
|
||||
@@ -3424,6 +3439,12 @@ later on AAPL, 47 µs on QQQ; 1.3 ms and 0.6 ms at the 90th percentile), or not
|
||||
at all when none does before the quote is cancelled. The aggressor that paid a
|
||||
worse price than ours met ours first: this venue serves it at that execution.
|
||||
|
||||
These figures were taken before an execution at another price (`C`) was read
|
||||
where it traded. Handed to hftbacktest as a fill of the order, such an
|
||||
execution serves the orders behind the level it is shown at, as it served them
|
||||
here; it no longer does here, so a quote served only there can now be one of
|
||||
these.
|
||||
|
||||
With 100 shares a quote, the same quotes are served at the same instants, and
|
||||
1,704 of them on AAPL, 5,425 on QQQ, for another size: an execution behind
|
||||
ours, or an order entering the other side, serves here as much as it takes;
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
|
||||
[project]
|
||||
name = "manifoldbt"
|
||||
version = "0.28.0"
|
||||
version = "0.28.1"
|
||||
description = "Rust-powered backtesting engine for quantitative research"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
|
||||
@@ -70,6 +70,12 @@ def cancel(ms, ref, qty):
|
||||
return _frame(_head(b"X", ms) + struct.pack(">QI", ref, qty))
|
||||
|
||||
|
||||
def execute_at(ms, ref, qty, px):
|
||||
"""An execution at a price other than the one the order is shown at (C)."""
|
||||
return _frame(_head(b"C", ms) + struct.pack(">QIQ", ref, qty, ref) + b"Y"
|
||||
+ struct.pack(">I", int(round(px * 10_000))))
|
||||
|
||||
|
||||
def _store(tmp_path, msgs):
|
||||
path = tmp_path / "day.itch"
|
||||
path.write_bytes(b"".join(msgs))
|
||||
@@ -155,6 +161,29 @@ def test_the_queue_a_run_names_is_the_one_served(tmp_path):
|
||||
assert len(level2.fills_df(backend="pandas")) == 0
|
||||
|
||||
|
||||
def test_an_execution_at_another_price_is_read_where_it_traded(tmp_path):
|
||||
# Order 3 enters at 10.00 behind ours, shown at 10.00, and executes at
|
||||
# 10.01: a price ahead of the whole level. It did not wait in the queue of
|
||||
# 10.00, so its execution does not reach our order, and the 100 shown ahead
|
||||
# of ours are still there. Traded at 9.99 instead, it went through our level.
|
||||
for traded, served in ((10.01, []), (9.99, [50.0])):
|
||||
msgs = [
|
||||
add(0, 1, True, 100, 10.00),
|
||||
add(0, 2, False, 100, 10.01),
|
||||
add(3, 3, True, 40, 10.00),
|
||||
execute_at(4, 3, 40, traded),
|
||||
add(20, 4, True, 10, 9.98),
|
||||
]
|
||||
day = tmp_path / f"at_{traded}"
|
||||
day.mkdir()
|
||||
res, seen = _run_queue(_store(day, msgs))
|
||||
assert seen[4] == 100.0, traded
|
||||
fills = res.fills_df(backend="pandas")
|
||||
assert list(fills["qty"]) == served, traded
|
||||
if served:
|
||||
assert set(fills["channel"]) == {"traverse"}
|
||||
|
||||
|
||||
def test_the_exact_queue_is_refused_on_a_book_stored_by_price():
|
||||
market = bt.sim.Market.from_ladders("X", 0.1, [(0, [(1.0, 1.0)], [(2.0, 1.0)])])
|
||||
with pytest.raises(Exception, match="stored order by order"):
|
||||
|
||||
Reference in New Issue
Block a user