Skip to content

keel v0.3.1

Choose a tag to compare

@github-actions github-actions released this 29 Jul 00:00
· 342 commits to main since this release
deb8fa7

Built from deb8fa7. Version binds to this hash:
keel --version reports keel 0.3.1+deb8fa7e978d [release].

Install

Download all wheels from this release into one directory, then install the
keel_trader wheel by path:

pip install --find-links . ./keel_trader-0.3.1-py3-none-any.whl
keel --version

⚠️ Never install by bare name. The distribution is keel-trader; the name
keel on PyPI belongs to an unrelated project, so pip install keel fetches
someone else's package. A build reporting DIRTY or [checkout] is not this
release and must not be run against live funds.

Configure

config.yaml is attached to this release: the production config, in
auto_trade.mode: confirm — keel previews every order and waits for your
approval. Drop it beside the install (or run keel init-config --live), put
your CDP key in a git-ignored .env, then:

keel migrate     # existing database: apply schema migrations
keel init        # fresh deployment: write config + seed candidate rules

Seeded rules start as candidate and trade nothing until you promote them.

Other changes

fix(turtle): drop the daily bar only when it is really forming (#155)

The live agent was deciding on a daily bar up to 48h old — a full day of lag on every Turtle
breakout entry and channel exit.

TurtleBreakout.detect()/exit_signal() dropped the last ONE_DAY bar whenever an ONE_HOUR
key was present. That is the right guard for the account simulator: sim.portfolio_sim iterates
hourly and hands the rule a daily series whose last bar is the current, still-forming day, whose
stored OHLC is the completed day — consuming it intraday is lookahead.

But the live agent (agent.run_once) passes ONE_HOUR too, and data.market_feed persists only
CLOSED candles, so its newest daily bar has already closed. The presence of ONE_HOUR was
standing in for "this is the sim", and in the live path it silently threw away a completed day.

Observed on the paper-forward deployment on 2026-07-28: the newest stored daily bar was
2026-07-27, and the rule was deciding on 2026-07-26.

The fix

Decide by where the hourly series sits, not by whether it exists: the newest daily bar is still
forming unless the newest hourly bar opens at or after that day's close.

This leaves the sim's behaviour provably unchanged. portfolio_sim slices its daily series with
bisect_right(daily_ts, t) against the current hourly bar t, so the last daily bar always
contains that hourly bar — the new condition is never true there. In the live agent it is true
from the first full hour of the next UTC day.

Against the live paper database this moves the decision bar from 2026-07-26 to the newest closed
day, 2026-07-27.

Tests

Three new tests in TestCompletedDailyBarIsUsedInTheLiveAgentPath, written first and watched fail:

  • a breakout on the newest closed daily bar fires (was None)
  • an exit on the newest closed daily bar fires (was False)
  • the account-sim shape still drops its forming bar — the regression guard for the sim

No behaviour change for the edge backtester, which passes no ONE_HOUR key at all.