Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ result.plot()

signal values are target weights. they are auto-normalised to sum to 1. rows in signals trigger a rebalance on that date; dates without a row hold the existing positions.

Current fill semantics are same-bar close fills: if a row appears in `signals`
for a given date, crossengine rebalances on that date using the close from the
same row in `prices`. That makes the engine easy to reason about for close-based
portfolio schedules, but it should not be interpreted as next-bar execution.

### hold without rebalancing (STAY)

STAY freezes the share count, not the weight. price movement causes the weight to drift naturally. no trades are generated for STAY assets.
Expand Down
5 changes: 5 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ result = backtest(

**returns:** `BacktestResult`

Current execution semantics are same-bar close fills: when a date is present in
`signals`, the rebalance is applied on that same date using the corresponding
close from `prices`. This is a close-fill model, not an implicit next-bar-open
execution model.

### `STAY`

sentinel value for "freeze share count, let weight drift." use in signals DataFrame:
Expand Down
4 changes: 4 additions & 0 deletions src/crossengine/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ def run(self) -> tuple[list[dict], list[dict]]:
self._process_pending(bar, date, trade_log)

if date in self.signals.index:
# Current semantics: signal rows rebalance on the same bar using
# that bar's close price. This is intentional today, but users
# should treat it as a close-fill model rather than next-bar
# execution until execution_delay lands.
self._rebalance(bar, date, trade_log)

chronicle.append(self.portfolio.snapshot(bar["prices"], date))
Expand Down
Loading