Download tests/test_minute_engine.py from bryceweiner/fly-trader: direct link, hf CLI and curl.
- Browser
- Download file 11.9 kB
-
https://huggingface.co/bryceweiner/fly-trader/resolve/main/tests/test_minute_engine.py
- Command line
-
hf download hf://bryceweiner/fly-trader/tests/test_minute_engine.py
-
curl -L -o test_minute_engine.py https://huggingface.co/bryceweiner/fly-trader/resolve/main/tests/test_minute_engine.py
11.9 kB
| """The minute engine: readiness on the stream, minute aggregation, the full feature vector built once per mint whatever | |
| the number of books, the training eligibility rules, and each book trading in its own savepoint.""" | |
| import json | |
| import math | |
| import time | |
| from datetime import datetime, timedelta, timezone | |
| import numpy as np | |
| from fly_trader.agent import minute_engine as me | |
| from fly_trader.db.connection import transaction | |
| from fly_trader.train.decisions import X_COLS | |
| def _engine(books=()): | |
| return me.MinuteEngine(list(books)) | |
| def test_ready_through_waits_for_the_stream(db_conn): | |
| s = _engine() | |
| m0 = math.floor(time.time() / 60) * 60 - 60 | |
| with db_conn.cursor() as cur: | |
| cur.execute("INSERT INTO ui_settings (key, value) VALUES ('pumpstream_status', %s) ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value", | |
| (json.dumps({"flushed_through": datetime.fromtimestamp(m0 - 60, timezone.utc).isoformat()}),)) | |
| db_conn.commit() | |
| assert s.ready_through(m0 + 60) == m0 # minute m0 is not written yet: it waits, however late | |
| with db_conn.cursor() as cur: | |
| cur.execute("UPDATE ui_settings SET value = %s WHERE key = 'pumpstream_status'", (json.dumps({"flushed_through": datetime.fromtimestamp(m0, timezone.utc).isoformat()}),)) | |
| db_conn.commit() | |
| assert s.ready_through(m0 + 60) == m0 + 60 | |
| def test_scale_break_blocks_the_mint_like_training(db_conn): | |
| s = _engine(); t = 1_800_000_000.0 | |
| a = {"open": 1.0, "high": 1.0, "low": 1.0, "close": 1.0, "buy": 1.0, "sell": 0.0, "nb": 1, "ns": 0, "n_traders": 1, "resq": 50.0, "pool": "P", "program_label": "Pump.fun Amm"} | |
| assert s._features(db_conn, "Bkpump", a, t)[1]["broken"] is False | |
| assert s._features(db_conn, "Bkpump", {**a, "close": 80.0}, t + 60)[1]["broken"] is True | |
| assert s._features(db_conn, "Bkpump", a, t + 120)[1]["broken"] is True # from that minute on, as in decisions.build | |
| def test_aggregate_and_full_feature_vector(db_conn): | |
| s = _engine() | |
| m0 = datetime(2026, 9, 1, 12, 0, tzinfo=timezone.utc) | |
| with db_conn.cursor() as cur: | |
| cur.execute("DELETE FROM pump_minutes WHERE mint = 'Zpump'") | |
| cur.execute("INSERT INTO pump_minutes (mint, ts, pool_id, open, high, low, close, buy_sol, sell_sol, n_buys, n_sells, n_traders, resq_sol) VALUES " | |
| "('Zpump', %s, 'POOL', 1.0, 1.2, 0.9, 1.1, 6.0, 2.0, 3, 1, 4, 55.0)", (m0,)) | |
| db_conn.commit() | |
| agg = s._aggregate(db_conn, m0, m0.replace(minute=1)) | |
| assert list(agg) == ["Zpump"] and agg["Zpump"]["resq"] == 55.0 and agg["Zpump"]["n_traders"] == 4 | |
| x, info = s._features(db_conn, "Zpump", agg["Zpump"], m0.timestamp() + 60) | |
| assert x.shape == (len(X_COLS),) and np.isfinite(x).all() | |
| assert info["resq"] == 55.0 and info["price"] == 1.1 and info["open"] == 1.0 and info["ec"] > 0 | |
| assert x[X_COLS.index("n_trades_1m")] == 4.0 and x[X_COLS.index("traders_15m")] == 4.0 | |
| assert x[X_COLS.index("meta_known")] == 0.0 and x[X_COLS.index("age_known")] == 0.0 | |
| def test_graduation_time_comes_from_corpus_meta(db_conn): | |
| s = _engine() | |
| g = datetime(2026, 9, 1, 10, 0, tzinfo=timezone.utc) | |
| with db_conn.cursor() as cur: | |
| cur.execute("DELETE FROM corpus_meta WHERE mint = 'Ypump'") | |
| cur.execute("INSERT INTO corpus_meta (mint, graduated_at, ttg_min, dev_sol) VALUES ('Ypump', %s, 45.0, 1.5)", (g,)) | |
| db_conn.commit() | |
| a = {"open": 1.0, "high": 1.0, "low": 1.0, "close": 1.0, "buy": 6.0, "sell": 0.0, "nb": 2, "ns": 0, "n_traders": 2, "resq": 60.0, "pool": "P", "program_label": "Pump.fun Amm"} | |
| x, info = s._features(db_conn, "Ypump", a, g.timestamp() + 2 * 3600) | |
| assert info["age_h"] == 2.0 | |
| assert x[X_COLS.index("age_known")] == 1.0 and x[X_COLS.index("meta_known")] == 1.0 | |
| assert x[X_COLS.index("ttg_min")] == 45.0 and x[X_COLS.index("dev_sol")] == 1.5 | |
| with db_conn.cursor() as cur: | |
| cur.execute("DELETE FROM corpus_meta WHERE mint = 'Ypump'") | |
| db_conn.commit() | |
| class _Book: | |
| def __init__(self, name, fail=False): | |
| self.name, self.fail, self.done, self.seen = name, fail, False, None | |
| def on_bars(self, t, bars): | |
| self.bars = bars | |
| def on_minute(self, ctx): | |
| ctx.conn.execute("INSERT INTO events (level, source, message) VALUES ('info', 'engine_test', %s)", (self.name,)) | |
| self.seen = (ctx.X.shape, list(ctx.mints)); self.lag = ctx.lag_s | |
| if self.fail: | |
| raise RuntimeError("boom") | |
| return {"ok": True} | |
| def open_mints(self, conn): | |
| return set() | |
| def finish(self): | |
| pass | |
| class _LiveBook(_Book): | |
| """A book that trades live money: its order goes out, committed, in the live phase.""" | |
| LIVE = True | |
| def __init__(self, name, calls): | |
| super().__init__(name); self.calls = calls | |
| def on_minute(self, ctx): | |
| self.calls.append(("live", self.name)) | |
| ctx.conn.execute("INSERT INTO events (level, source, message) VALUES ('info', 'engine_test', 'live order')") | |
| return {"live": True} | |
| def after_minute(self, ctx): | |
| self.calls.append(("after", self.name)) | |
| return {"ok": True} | |
| class _PaperBook(_Book): | |
| def __init__(self, name, calls): | |
| super().__init__(name); self.calls = calls | |
| def on_minute(self, ctx): | |
| self.calls.append(("paper", self.name)) | |
| with transaction() as other: # another connection: the live phase is committed already | |
| self.saw = other.execute("SELECT count(*) AS n FROM events WHERE source = 'engine_test' AND message = 'live order'").fetchone()["n"] | |
| return {"ok": True} | |
| def test_live_books_trade_and_commit_before_any_paper_book(db_conn, monkeypatch): | |
| """Live money first: the live book's minute runs and commits before any other book's, whatever the books' order; | |
| its paper trades, learning and bookkeeping (after_minute) come after the paper books.""" | |
| m0 = datetime(2026, 9, 3, 12, 0, tzinfo=timezone.utc); calls = [] | |
| with transaction() as conn: | |
| conn.execute("DELETE FROM events WHERE source = 'engine_test'") | |
| paper, live = _PaperBook("selector", calls), _LiveBook("fly", calls) | |
| e = _engine([paper, live]); monkeypatch.setattr(e, "stream_fresh", lambda m0_epoch: True) | |
| out = e.run_minute(m0.timestamp() + 60) | |
| with transaction() as conn: | |
| conn.execute("DELETE FROM events WHERE source = 'engine_test'") | |
| assert calls == [("live", "fly"), ("paper", "selector"), ("after", "fly")] and paper.saw == 1 | |
| assert out["fly"] == {"ok": True} and out["selector"] == {"ok": True} | |
| def test_every_chains_live_phase_runs_before_any_chains_paper_phase(monkeypatch): | |
| """Live money first across chains: older minutes are caught up whole, then every chain's newest minute's live phase, | |
| and only then any chain's paper phase.""" | |
| calls, engines = [], [] | |
| for name in ("sol", "rh"): | |
| e = _engine([_Book(f"book_{name}")]); e.last_minute = 600.0 | |
| monkeypatch.setattr(e, "ready_through", lambda m1: 720.0) | |
| monkeypatch.setattr(e, "run_minute", lambda m, trade=True, n=name: calls.append((n, "whole", m)) or {}) | |
| monkeypatch.setattr(e, "live_phase", lambda m, trade=True, n=name: calls.append((n, "live", m)) or (None, {"minute": m})) | |
| monkeypatch.setattr(e, "paper_phase", lambda ctx, out, n=name: calls.append((n, "paper")) or out) | |
| engines.append(e) | |
| ran = me.run_ready(engines, 720.0) | |
| assert calls == [("sol", "whole", 660.0), ("rh", "whole", 660.0), ("sol", "live", 720.0), ("rh", "live", 720.0), ("sol", "paper"), ("rh", "paper")] | |
| assert [(m, st) for _, m, st in ran] == [(720, {"minute": 720.0})] * 2 and all(e.last_minute == 720 for e in engines) | |
| assert me.run_ready(engines, 720.0) == [] # nothing new: the loop goes idle | |
| def test_books_share_one_feature_pass_and_trade_in_their_own_savepoints(db_conn, monkeypatch): | |
| m0 = datetime(2026, 9, 2, 12, 0, tzinfo=timezone.utc) | |
| with db_conn.cursor() as cur: | |
| cur.execute("DELETE FROM pump_minutes WHERE mint = 'Wpump'"); cur.execute("DELETE FROM events WHERE source = 'engine_test'") | |
| cur.execute("INSERT INTO pump_minutes (mint, ts, pool_id, open, high, low, close, buy_sol, sell_sol, n_buys, n_sells, n_traders, resq_sol) VALUES " | |
| "('Wpump', %s, 'POOL', 1.0, 1.2, 0.9, 1.1, 6.0, 2.0, 3, 1, 4, 55.0)", (m0,)) | |
| db_conn.commit() | |
| bad, good = _Book("bad", fail=True), _Book("good") | |
| e = _engine([bad, good]); calls = [] | |
| real = e._features | |
| monkeypatch.setattr(e, "_features", lambda *a: calls.append(a[1]) or real(*a)) | |
| monkeypatch.setattr(e, "stream_fresh", lambda m0_epoch: True) | |
| out = e.run_minute(m0.timestamp() + 60) | |
| assert calls == ["Wpump"] # one feature pass per mint, two books | |
| assert good.lag > 86400 # how long after its end the minute was traded (a replayed past one) | |
| assert good.seen == ((1, len(X_COLS)), ["Wpump"]) and good.bars["Wpump"][0] == m0.timestamp() | |
| assert "good" in out and "bad" not in out | |
| with transaction() as conn: | |
| left = [r["message"] for r in conn.execute("SELECT message FROM events WHERE source = 'engine_test'").fetchall()] | |
| conn.execute("DELETE FROM events WHERE source = 'engine_test'"); conn.execute("DELETE FROM pump_minutes WHERE mint = 'Wpump'") | |
| assert left == ["good"] # the failing book's writes were rolled back alone | |
| def test_a_quiet_minute_is_charged_the_mints_last_known_fee(db_conn): | |
| """A held coin that did not trade this minute reports no fee: its sale and its mark pay the last one known (the | |
| engine's own minutes, else the stream's newest row with one; RH: else the pool's measured hook fee), not the cost | |
| model's generic fallback (on RH ~1 % against hook fees of 2.8-5 %).""" | |
| from fly_trader import markets | |
| m0 = datetime(2026, 9, 3, 12, 0, tzinfo=timezone.utc); s = _engine(); rh = me.MinuteEngine(market=markets.RH) | |
| tok, pool = "0x" + "f1" * 20, "0x" + "f2" * 32 | |
| def ctx(engine, fees): | |
| return me.Minute(conn=db_conn, m0=m0, m1=m0, m1_epoch=m0.timestamp(), agg={}, X=np.zeros((0, len(X_COLS)), np.float32), infos=[], mints=[], bars={}, | |
| prices={}, resqs={}, fees=fees, fresh=True, trade=True, engine=engine) | |
| with db_conn.cursor() as cur: | |
| cur.execute("DELETE FROM pump_minutes WHERE mint IN ('Fpump', 'Npump')"); cur.execute("DELETE FROM rh_pools WHERE pool_id = %s", (pool,)) | |
| cur.execute("INSERT INTO pump_minutes (mint, ts, pool_id, close, resq_sol, fee_rate) VALUES ('Fpump', %s, 'P', 1.0, 50.0, 0.0115), ('Fpump', %s, 'P', 1.0, 50.0, NULL)", | |
| (m0, m0 + timedelta(minutes=1))) | |
| cur.execute("INSERT INTO rh_pools (pool_id, token, currency0, currency1, is_pons, hook_fee) VALUES (%s, %s, %s, %s, true, 0.029)", (pool, tok, "0x" + "00" * 20, tok)) | |
| db_conn.commit() | |
| try: | |
| c = ctx(s, {"Gpump": 0.01, "Fpump": None}) | |
| assert c.fee("Gpump") == 0.01 and c.fee("Fpump") == 0.0115 and c.fee("Npump") is None | |
| a = {"open": 1.0, "high": 1.0, "low": 1.0, "close": 1.0, "buy": 1.0, "sell": 0.0, "nb": 1, "ns": 0, "n_traders": 1, "resq": 50.0, "pool": "P", | |
| "program_label": "Pump.fun Amm", "fee_rate": 0.0095} | |
| s._features(db_conn, "Fpump", a, m0.timestamp() + 180) | |
| assert c.fee("Fpump") == 0.0095 # the engine's own newer minute | |
| assert ctx(rh, {}).fee(tok) == 0.029 and c.cost_label("Fpump", "Pump.fun Amm") == "Pump.fun Amm" | |
| finally: | |
| with db_conn.cursor() as cur: | |
| cur.execute("DELETE FROM pump_minutes WHERE mint IN ('Fpump', 'Npump')"); cur.execute("DELETE FROM rh_pools WHERE pool_id = %s", (pool,)) | |
| db_conn.commit() | |