Add PAPER trading mode + fix partial-fill close orphaning positions - #1
Open
Freecey wants to merge 3 commits into
Open
Add PAPER trading mode + fix partial-fill close orphaning positions#1Freecey wants to merge 3 commits into
Freecey wants to merge 3 commits into
Conversation
New mode between OFF and LIVE: the full pipeline (scan, TA, AI research, risk gates, DSL exits) runs unchanged against live data, but every authenticated exchange call routes to a simulated book (client/paper_engine.py): - fills at the live L2 touch + paper_slippage_bps, taker fees at paper_fee_bps, position netting with realized PnL - reduce-only clamps to the live size (never flips), pre-trade margin check - virtual SL/TP triggers evaluated against live mids on every account read - state persisted atomically to .paper-state.json across restarts - fetch_account_state returns the virtual book in HL's exact clearinghouseState shape, so gates/DSL/dashboard need no changes - no HYPERLIQUID_* env vars required (resolve_user_address -> "paper") Loop startup now logs the real configured mode (was hardcoded "LIVE"). Covered by tests/test_paper.py (14 offline tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lience - close_position_market: an IOC reduce-only close can PARTIALLY fill on a thin book; the code deregistered the DSL tracker on ok=True without checking totalSz, orphaning the residual position with no stop/floor/ timeout. Now reconciles totalSz vs requested, retries the residual up to 3x (fresh size + price each pass), keeps the tracker and returns ok=False + partial details if any size remains (next DSL tick re-fires). Realized PnL uses the size-weighted fill price across all fills. - rehydrate_from_exchange: synthesized trackers stamped entry_time=now, re-arming hard_timeout_minutes from scratch on every restart. Now looks up the real open time from the account's fill history (most recent open-from-flat fill), falling back to now when unavailable. - backup SL/TP triggers: placement is retried once (typical failure is a transient meta-cache/429 flake) and a persistent SL failure now emits an error event to the session feed instead of only a log line. - gate inputs: open-position notional now uses HL's positionValue (live mark) instead of size x the stale analysis entry price. - place_hl_trigger_order callers pass is_long_position= explicitly (the positional is_buy only worked because entry direction == position side). - maybe_execute no longer requires HYPERLIQUID_PRIVATE_KEY in PAPER mode. Covered by tests/test_close_and_rehydrate_fixes.py (6 regression tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The FastAPI server (dashboard + operator endpoints that place/close real trades behind a single static token) listened on 0.0.0.0 unconditionally. Default is now 127.0.0.1, overridable via HERMES_HOST. Containerized deploys keep their behavior: HERMES_HOST=0.0.0.0 is set in the Dockerfile, fly.toml and the k8s configmap, where the platform provides the network isolation. Dockerfile also pins HERMES_PAPER_STATE_FILE onto the /data volume alongside the other state files. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merged
Julian-dev28
added a commit
that referenced
this pull request
Jul 12, 2026
…lidated Pre-registered honest implementations of all 7 visible '25/25 leaderboard' strategy families, our data (90 coins daily + 40 coins hourly 208d), net 25bps+funding, frozen thresholds, matched nulls >=2000, Bonferroni 0.0056: 6 REFUTED, 1 UNDERSAMPLED (dominance_transition, n=20). Best null p anywhere: 0.215. Their #1 (claimed Sharpe 7.07 / 2,311%) earns -0.57%/trade OOS here and loses to random same-coin timing in 2000/2000 resamples; their claimed n for dominance signals is arithmetically impossible (9-21 fires in 5.5y). W-Q verdict upgrades: presumptively overfit -> empirically contradicted. Nothing ships, no recorder owed. 12MB hourly cache gitignored. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Julian-dev28
added a commit
that referenced
this pull request
Jul 22, 2026
…piece #1 W-MATH2: we run ~2.87 effective bets, not 15; the 4 xyz-short books are ~1.28 bets held four ways (mean pairwise corr 0.83), and 7 xyz shorts stopped TOGETHER for -$8.59 on 07-21 exactly as the correlation predicts. W-MATH3 independently promoted a per-sector short cap as THE fix (not momentum timing). - xyz_short_concentration_gate: caps concurrent DISTINCT xyz-equity short names (<=3) AND their combined notional (<=25% equity). Name cap is the hard control; notional cap is best-effort (skipped if positions carry no value field, so a missing field never wrongly blocks). Only gates NEW xyz-equity shorts. - wired into eval_all_gates NOT carveout-exempt — the whole point is the correlated BOOK cluster, so strategy books obey it too. - config: max_xyz_short_names=3, max_xyz_short_notional_pct=0.25 (W-MATH2's number; was ~7 names / ~124% equity on overnight). - 4 tests (name cap, notional cap, ignores longs/crypto/missing-value, wired + not-carveout-exempt). 27 book-path + wiring tests green, no collision. Honest caveat (W-MATH2): nothing live is edge-proven yet (n<8); this cap is risk-control, and the allocation is variance-driven until books hit n>=8 (~1-2wk). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
PsychoLogicAu
referenced
this pull request
in PsychoLogicAu/hermes-trader
Aug 20, 2026
…nked) 7e63882 dedented the TA-filter + LLM research + execute block out of the for perception in results: loop, so only the last (lowest-ranked) trigger was ever researched per scan. The top-ranked trigger was silently skipped all cycle - e.g. LINK ranked #1 for 34 straight scans (scores 35-45) but was first researched only when ACE briefly out-scored it at the top of LINK's +8.8% spike, producing the 2026-08-15 late-chase entry. Re-indent the block back into the loop so all confirmed triggers are researched per cycle in score order. sleep/progress timestamp stay outside the loop (once per cycle). Restores pre-7e63882 structure plus the newer PASS/Chronos logging.
Julian-dev28
added a commit
that referenced
this pull request
Sep 4, 2026
… account
TWO PIECES: the quant research the operator asked for, and the config change to
let the account actually trade.
── RESEARCH ────────────────────────────────────────────────────────────────
regime_read.py first, deliberately before any hypothesis. Picking the strategy
and then looking at the tape is how you end up testing the one that worked in
the regime you remember. It says: index going nowhere (median +0.63%), 7.08%
cross-sectional dispersion, funding positive on 86% of coins at +11%
annualised, 47% of coins pinned at the funding baseline.
H1 — short the most crowded funding. REFUTED: -0.285%/trade, IC -0.006,
bootstrap p 0.98. The decile table said why, and it is the useful part: on D10
the carry leg paid +0.245% exactly as theory predicts while the price leg ran
-0.323% against it. In this tape high funding marks momentum, not
crowding-to-revert, so "fade the crowd" fights the direction that pays.
H2 — does 3-day cross-sectional momentum continue? No. The ladder came out
monotone in the opposite direction, bottom decile +0.24% over the next 24h
against top decile -1.89%.
H3 — so test the reversal on its own terms, rather than reading it out of a
table built to answer a different question. H2's p-value tested "does momentum
pay", which is the wrong tail for a reversal, and quoting -1.89% from a run
whose stated threshold was never met would be fitting the story to the output.
short the top decile of 3d return, 24h hold, net of funding and 25bps:
+1.373%/trade, win 57%, n=5106, IC +0.0835, bootstrap p 0.0000
(clustered on entry snapshot, because 300 coins in one snapshot share one
market and are nowhere near 300 independent draws)
controls behave: shorting the BOTTOM decile loses -0.772%, shorting
everything is -0.158%. The edge is specifically in the winners.
OI cut, as pre-registered: squeeze (OI fell) +1.953% vs new money +1.061%.
A rally on falling OI has no new buyers behind it and gives more back.
liquidity: most liquid half +1.132%, least liquid +1.615% — it survives in
the names where 25bps is not a fiction, which is the check that usually kills
a spread this size.
at a $5M volume floor it holds at +0.968%; at 48h it improves to +1.871%.
one of four time quartiles is negative (-0.355%). Reported, not hidden.
Costs are quoted from the short's side throughout and never by negating a long,
which would silently turn a 25bps cost into a 25bps credit.
This is a candidate, not a book. It needs a forward shadow ledger before any
capital, same as everything else here.
── SIZING ──────────────────────────────────────────────────────────────────
The operator asked to lift the $88.89 floor for a $12.94 account. The switch
already existed (min_tradable_equity_usd, precedence #1) and is now set, but
lifting it alone does nothing: there are two gates, and every book carried $20
notional at 1x against $12.94 of equity, so the margin check rejected the trade
anyway. "I flipped the switch and nothing happened" is otherwise
indistinguishable from a broken executor, so there is a test for it.
$11 is the only notional that works. Above HL's MIN_ORDER_USD of $10.50, below
the $11.65 of usable margin left after the 10% min_available_margin_pct.
max_concurrent drops to 1 because exactly one position fits; leaving it at 20
would have the executor believe it had room it does not have.
Also: max_daily_loss_usd was -$100 on a $12.94 account, so the kill switch
could never fire. It was decorative. Now -$4, roughly 30% of equity.
Risk per trade is $1.65 at the 15% stop, 13% of the account. That is inherent
to the account size and not something sizing can engineer away. The derived
$88.89 floor exists because below it the first book to fire consumes the budget
and the rest sit margin-blocked (W-FUND1); with max_concurrent 1 that is now
the explicit design rather than a surprise.
Config carries a note saying what to restore when the account is funded.
Gate suite: 1338 passed, 14 deselected.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014UBv8io41CVxP9d8JM6koM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three changes in three commits (happy to split into separate PRs if you prefer):
1.
feat(paper): PAPER mode — simulated fills against live market pricesThere is currently no step between
OFF(observe only) andLIVE(real money). This adds"mode": "PAPER": the full pipeline (scan → TA filter → AI research → risk gates → DSL exits) runs unchanged against live market data, but every authenticated exchange call routes to a simulated book (client/paper_engine.py):paper_slippage_bps, taker fees atpaper_fee_bps.paper-state.json(survives daemon restarts, same pattern as.dsl-state.json)fetch_account_statereturns the virtual book in HL's exactclearinghouseStateshape, so the gates, DSL engine, dashboard andstatus.pywork without modificationHYPERLIQUID_*env vars — useful for new users evaluating a config before funding a walletKnown approximations are documented in the module docstring and README: orders always fill in full (no book-depth exhaustion) and triggers fill at their trigger price (real markets gap) — so paper results are slightly optimistic by construction.
2.
fix(exits): partial-fill close could orphan a position with no stopThe important one. In
close_position_market, the DSL tracker was deregistered as soon as the reduce-only IOC returnedok=True, without checkingtotalSzagainst the requested size. On a thin book, a partial fill left the residual position live on the exchange with no stop, no floor, no timeout — exactly the failure mode this engine exists to prevent.Now:
totalSzis reconciled against the requested size, the residual is retried up to 3× (fresh size and price each pass), and if any size still remains the tracker is kept and the call returnsok=Falsewithpartial/remaining_szdetails so the next DSL tick re-fires the close. Realized PnL uses the size-weighted fill price across all fills.Also in this commit:
rehydrate_from_exchangestamped synthesized trackers withentry_time=now, re-arminghard_timeout_minutesfrom scratch on every restart (a 2h50m-old position got a fresh 3h lease). It now looks up the real open time from the account's fill history (most recent open-from-flat fill), falling back tonowwhen unavailable.errorevent to the session feed instead of only a log line — the position is otherwise running with the 60s DSL loop as its only stop and nobody knows.positionValue(live mark) instead of|szi| ×the stale analysis entry price.place_hl_trigger_ordercallers passis_long_position=as an explicit keyword — the positionalis_buyonly worked because entry direction happens to equal position side.3.
fix(security): bind the API server to localhost by defaultThe FastAPI server (whose operator endpoints place/close real trades behind a single static token) listened on
0.0.0.0unconditionally. Default is now127.0.0.1, overridable viaHERMES_HOST; the Dockerfile,fly.tomland the k8s configmap setHERMES_HOST=0.0.0.0so containerized deploys behave exactly as before.Testing
tests/test_paper.py(14 tests: fills, netting, reduce-only clamp, margin check, trigger firing, persistence, exchange-layer routing) +tests/test_close_and_rehydrate_fixes.py(6 regression tests: partial close keeps the tracker, residual retry, weighted fill price, dust handling, open-time from fill history, API-failure fallback).🤖 Generated with Claude Code