@oracle-agent/oracle 0.24.1 → 0.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/skills/chain/SKILL.md +34 -0
- package/dist/assets/skills/chain-defi-ecosystem-analysis/SKILL.md +181 -0
- package/dist/assets/skills/chain-ecosystem-gap-analysis/SKILL.md +162 -0
- package/dist/assets/skills/cross-chain-twap-execution/SKILL.md +125 -0
- package/dist/assets/skills/defi-protocol-pmf-assessment/SKILL.md +332 -0
- package/dist/assets/skills/evm-contract-research.md +8 -3
- package/dist/assets/skills/multi-venue-prepare-only-ranking/SKILL.md +93 -0
- package/dist/assets/skills/oracle-access-control/SKILL.md +71 -0
- package/dist/assets/skills/oracle-action-arming/SKILL.md +99 -0
- package/dist/assets/skills/oracle-airdrop-calculator/SKILL.md +111 -0
- package/dist/assets/skills/oracle-desk-product/SKILL.md +341 -0
- package/dist/assets/skills/oracle-evm/SKILL.md +55 -0
- package/dist/assets/skills/oracle-harness/SKILL.md +41 -0
- package/dist/assets/skills/oracle-mcp-install/SKILL.md +140 -0
- package/dist/assets/skills/oracle-multichain-convert/SKILL.md +87 -0
- package/dist/assets/skills/oracle-native-harness/SKILL.md +32 -0
- package/dist/assets/skills/oracle-ownership-gate/SKILL.md +42 -0
- package/dist/assets/skills/oracle-public-product-ux/SKILL.md +114 -0
- package/dist/assets/skills/oracle-tailscale/SKILL.md +32 -0
- package/dist/assets/skills/oracle-thin-client/SKILL.md +47 -0
- package/dist/assets/skills/perp-venue-funding-research/SKILL.md +161 -0
- package/dist/assets/skills/polymarket/SKILL.md +160 -0
- package/dist/assets/skills/protocol-api-key-integration/SKILL.md +141 -0
- package/dist/assets/skills/self-custodial-onchain-execution/SKILL.md +1284 -0
- package/dist/assets/skills/setup/SKILL.md +40 -0
- package/dist/assets/skills/stable-launch-ops/SKILL.md +89 -0
- package/dist/assets/skills/trade-loop-circuit-breaker/SKILL.md +441 -0
- package/dist/assets/skills/venue-capability-boundaries/SKILL.md +32 -0
- package/dist/bin/desk-server.mjs +16 -16
- package/dist/bin/oracle-data-mcp.mjs +1 -1
- package/dist/bin/oracle-equities.mjs +1 -1
- package/dist/bin/oracle-init.mjs +9 -9
- package/dist/cli/commands/bootstrap.mjs +1 -1
- package/dist/cli/commands/chat.mjs +78 -77
- package/dist/cli/commands/doctor.mjs +8 -6
- package/dist/cli/commands/eval.mjs +1 -1
- package/dist/cli/commands/harness.mjs +6 -6
- package/dist/cli/commands/model.mjs +82 -81
- package/dist/cli/commands/receipt.mjs +5 -0
- package/dist/cli/commands/setup.mjs +1 -1
- package/dist/cli/commands/venues.mjs +3 -0
- package/dist/cli/commands/watch.mjs +16 -0
- package/dist/equities/index.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: setup
|
|
3
|
+
description: "Use when the user types /setup or wants to connect telegram, discord, slack, or another messaging platform to oracle."
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
> Oracle native tools: `oracle_cli` (read/prepare), `vault_status`, `signer_status`, `signer_execute` (needs human confirmationNonce), `skill_load`. No generic shell. No fleet SSH.
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# /setup
|
|
12
|
+
|
|
13
|
+
Configure messaging for the oracle profile. Secrets stay local.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# menu + status (never prints tokens)
|
|
17
|
+
oracle setup status
|
|
18
|
+
|
|
19
|
+
# open full hermes wizard
|
|
20
|
+
oracle setup messaging
|
|
21
|
+
|
|
22
|
+
# platform shortcuts
|
|
23
|
+
oracle setup telegram
|
|
24
|
+
oracle setup discord
|
|
25
|
+
oracle setup slack
|
|
26
|
+
oracle setup whatsapp
|
|
27
|
+
|
|
28
|
+
# gateway control
|
|
29
|
+
oracle setup gateway status
|
|
30
|
+
oracle setup gateway restart
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
If the user passed args after `/setup`, forward them:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
oracle setup {{arg1}} {{arg2}} {{arg3}}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
If no args, show `oracle setup status`.
|
|
40
|
+
Never echo bot tokens, app tokens, or passwords back into chat.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stable-launch-ops
|
|
3
|
+
description: "Use for Stable Launch chain 988 deploy or testnet asks."
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
> Oracle native tools: `oracle_cli` (read/prepare), `vault_status`, `signer_status`, `signer_execute` (needs human confirmationNonce), `skill_load`. No generic shell. No fleet SSH.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Stable Launch ops (chain 988)
|
|
11
|
+
|
|
12
|
+
Repos: `~/projects/stable-launch` (contracts) · `stable-launch-web` · `stable-launch-indexer` · `stable-agent` (personal signals, never bundled into public web).
|
|
13
|
+
|
|
14
|
+
Board: Obsidian `Projects/Stable Launch Board.md` · preview `https://stablelaunch.demi.la`
|
|
15
|
+
|
|
16
|
+
## Chain fact (do not invent a testnet)
|
|
17
|
+
|
|
18
|
+
- Product chain is **Stable Mainnet chainId 988** only in this stack.
|
|
19
|
+
- Addresses file: `stable-launch/addresses/988.json` (null until factory GO).
|
|
20
|
+
- Public RPC: `https://rpc.stable.xyz` (pruned ~156k blocks — indexer needs **archive** paid RPC for first backfill).
|
|
21
|
+
- There is **no** separate Stable public testnet config in-repo. INTERFACE.md "testnet first" means **anvil-988 fork rehearsal**, not a second public network.
|
|
22
|
+
|
|
23
|
+
When DEMI asks **"should we deploy on testnet to test?"**:
|
|
24
|
+
|
|
25
|
+
| Path | Do it? | Why |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| Traffic stack (paid RPC + indexer host + CF secrets/DNS) | **Yes** | Proves launch-day read path; no factory GO |
|
|
28
|
+
| Frontend edge cutover off demi-poly | **Yes** | demi-poly is forbidden for launch frontend/indexer |
|
|
29
|
+
| Physical wallet QA on 988 | **Yes** | Cannot verify headlessly |
|
|
30
|
+
| Persistent **anvil fork of 988** + point web/indexer at it | **Yes** | Full E2E, zero mainnet spend |
|
|
31
|
+
| Factory `forge script --broadcast` on live 988 | **No** without explicit GO | Real USDT0 gas + irreversible keeper wiring |
|
|
32
|
+
| Invented external "Stable testnet" deploy | **No** | Not in config; fork already covers contract risk |
|
|
33
|
+
|
|
34
|
+
## Already proven (do not re-litigate)
|
|
35
|
+
|
|
36
|
+
- Foundry suite green; dual red-team APPROVE (Opus + Sol)
|
|
37
|
+
- Full lifecycle on live-RPC anvil-988 fork: deploy→launch→buy→graduate→v3 LP→swap→buyback/burn
|
|
38
|
+
- Deploy without code-bearing `STABLE_KEEPER` reverts as designed
|
|
39
|
+
- LI.FI bridge Base USDC→USDT0 on 988 quoted live
|
|
40
|
+
- Indexer-first radar path + k6 load scripts exist
|
|
41
|
+
|
|
42
|
+
## Hard GO gates before live factory
|
|
43
|
+
|
|
44
|
+
From board / `docs/MAINNET_GO_RUNBOOK.md`:
|
|
45
|
+
|
|
46
|
+
1. Recoverable **Safe** as `STABLE_KEEPER` on 988 (`cast codesize` > 0) — not an EOA
|
|
47
|
+
2. Funded **dedicated** deployer key (USDT0 gas) — never the chat/hot wallet
|
|
48
|
+
3. Paid `STABLE_RPC_PRIMARY` / `FAILOVER` (archive primary)
|
|
49
|
+
4. Indexer host on Fly/Railway/Render — **never demi-poly**
|
|
50
|
+
5. DNS / CF custom-domain bind for `stablelaunch.demi.la` (see DNS trap)
|
|
51
|
+
6. Explicit DEMI **GO** for broadcast
|
|
52
|
+
|
|
53
|
+
### CF / edge (do not re-mint tokens blindly)
|
|
54
|
+
|
|
55
|
+
- Same CF **account** as HL Media (users see hostnames only).
|
|
56
|
+
- Web repo already has CF secrets + green deploys (2026-07-25).
|
|
57
|
+
- Live: `https://stable-launch-web.christophergervais92.workers.dev` (account-derived sub — never guess `demi-hl`).
|
|
58
|
+
- Still need: `STABLE_RPC_*`, `INDEXER_URL`, factory `NEXT_PUBLIC_*` after GO.
|
|
59
|
+
- Vault "cfat_ expired" may be stale after green CI.
|
|
60
|
+
|
|
61
|
+
### Indexer Fly prep
|
|
62
|
+
|
|
63
|
+
- `fly.toml` in `stable-launch-indexer` (lax, volume `/data`). Empty factory → `live:false` radar OK.
|
|
64
|
+
- See indexer README + `docs/PRE_LIVE_OPS.md`.
|
|
65
|
+
|
|
66
|
+
### DNS trap — never blind-CNAME to workers.dev
|
|
67
|
+
|
|
68
|
+
`stablelaunch.demi.la` A→demi-poly works. CNAME to workers.dev **without** CF zone + Worker domain bind → **403** on custom Host (probed). Keep VPS A + edge-QA on workers.dev, or CF zone audit → NS → bind. Recipe: `references/dns-cf-workers-trap-2026-07-25.md`. GoDaddy API 401 → mint fresh key before API DNS edits.
|
|
69
|
+
|
|
70
|
+
## Forbidden
|
|
71
|
+
|
|
72
|
+
- Launch frontend, indexer, or RPC proxy on demi-poly
|
|
73
|
+
- Bundling `stable-agent` into public `stable-launch-web`
|
|
74
|
+
- Mainnet broadcast "just to test" when fork rehearsal already passed
|
|
75
|
+
- Deploy from the Oracle chat hot wallet
|
|
76
|
+
|
|
77
|
+
## Safe next steps (default recommendation order)
|
|
78
|
+
|
|
79
|
+
1. Confirm CF green; set archive RPC secrets; Fly indexer empty-factory + `INDEXER_URL`
|
|
80
|
+
2. Wallet physical QA on preview and/or workers.dev
|
|
81
|
+
3. Keeper Safe + dedicated deployer
|
|
82
|
+
4. CF zone path for custom domain (or leave VPS A)
|
|
83
|
+
5. Only then: `docs/MAINNET_GO_RUNBOOK.md`
|
|
84
|
+
|
|
85
|
+
## Pointers
|
|
86
|
+
|
|
87
|
+
- Status: `stable-launch/STATUS.md`
|
|
88
|
+
- Runbooks: `docs/MAINNET_GO_RUNBOOK.md`, `docs/KEEPER_RUNBOOK.md`, `docs/LAUNCH_TRAFFIC.md`, `docs/PRE_LIVE_OPS.md`
|
|
89
|
+
- Notes: `references/testnet-decision-2026-07-25.md`, `references/dns-cf-workers-trap-2026-07-25.md`
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: trade-loop-circuit-breaker
|
|
3
|
+
description: Apply mandatory guardrails to any autonomous or agentic live-money trading loop. Use before touching the Polymarket bot, building a new scanner/execution dashboard, enabling wallet/CEX orders, responding to a drawdown, or resuming after a halt. Makes the "anything touching real capital is surgical, backed-up, verified, and GO-gated" invariant executable.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> Oracle native tools: `oracle_cli` (read/prepare), `vault_status`, `signer_status`, `signer_execute` (needs human confirmationNonce), `skill_load`. No generic shell. No fleet SSH.
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Trade-Loop Circuit Breaker
|
|
10
|
+
|
|
11
|
+
Any runaway loop or unverified execution path is existential when it can move real capital. This skill gates Polymarket operations and new agentic trading products. It does NOT change strategy; it controls when execution is allowed.
|
|
12
|
+
|
|
13
|
+
## Context
|
|
14
|
+
- Live sports arb bot. VPS `demi-poly`, path `/root/polymarket-arbitrage-bot`.
|
|
15
|
+
- Has a daily loss limit that HALTS the bot (has fired before — 14.2% daily loss → manual reset needed).
|
|
16
|
+
- pm2-managed process map; extensive cron pipeline (reconcile, clv, snapshots, retrain, shadow, rf-advisory).
|
|
17
|
+
- See vault: Polymarket Bot Operational Reference, Demi Revert Runbook, Demi Bot Architecture.
|
|
18
|
+
|
|
19
|
+
## Before ANY change to the live loop
|
|
20
|
+
1. **Back up first.** Snapshot the file(s) and the relevant DB/state before editing. Note the exact revert command (see Demi Revert Runbook).
|
|
21
|
+
2. **Surgical only.** Touch exactly what the task requires. No "while I'm here" cleanup in the money path.
|
|
22
|
+
3. **GO-gate.** Hard-to-reverse actions (deploy to live loop, reset after halt, change position sizing, drop/alter state) require explicit DEMI confirmation. Do not self-approve.
|
|
23
|
+
|
|
24
|
+
## Circuit breakers on any autonomous trading/execution loop
|
|
25
|
+
- **Retry ceiling:** max 3 attempts on a failing external call (Polymarket API, RPC). Then stop, don't hammer.
|
|
26
|
+
- **Spend/exposure cap:** respect the daily loss limit; never code around a halt. A halt is a signal, not an obstacle.
|
|
27
|
+
- **Backoff + jitter** on API timeouts — cascading timeouts are a known cost/failure path.
|
|
28
|
+
- **Kill-switch check** before resume: verify why it halted before clearing it. Reset ≠ diagnosis.
|
|
29
|
+
- **Signer separation:** automatic execution may use only a separate capped hot wallet/session key/smart wallet. Never DEMI's main seed/private key.
|
|
30
|
+
- **Two-step deploy boundary:** shipping executor code and securely installing a dedicated signer are not the same as enabling broadcasts. A signer may be installed root-only in an unfunded, execution-off state after explicit installation approval so its derived address and status path can be verified. Keep `agent:execute` blocked, capital at zero, and asset allowlists empty until a separate explicit DEMI GO enables the capped signer. Never expose or print the key during either stage.
|
|
31
|
+
- **Policy proof:** after every deploy, pull the live policy and state `execution.enabled`, `backendSigner`, `liveExecute`, and whether `agent:execute` is blocked before claiming auto-exec status.
|
|
32
|
+
|
|
33
|
+
## Resume-after-halt sequence
|
|
34
|
+
1. Read the halt reason (loss limit? API? reconcile mismatch?). Don't clear blindly.
|
|
35
|
+
2. Reconcile positions (`reconcile_positions.py --apply`) — confirm on-chain vs recorded state matches.
|
|
36
|
+
3. Confirm the condition that tripped it has actually cleared.
|
|
37
|
+
4. GO-gate the resume with DEMI. Report the halt reason + current exposure in the ask.
|
|
38
|
+
5. Resume, then watch the first cycle before walking away.
|
|
39
|
+
|
|
40
|
+
## Escalation message format
|
|
41
|
+
When a breaker trips or a halt needs a human: state the trigger, current exposure/PnL, what you've verified, and the specific decision needed — one message, no burying the ask.
|
|
42
|
+
|
|
43
|
+
## Evaluating a NEW venue / market before wiring it into the bot (HIP-4 pattern)
|
|
44
|
+
When DEMI says "make the bot also trade <new venue/market> (e.g. HIP-4)", the
|
|
45
|
+
change is a NEW EDGE that shares infra — NOT the existing edge pointed at a new
|
|
46
|
+
venue. Pressure-test the premise before writing a line.
|
|
47
|
+
|
|
48
|
+
**STEP 0 (do this BEFORE proposing any build): inventory what already exists in
|
|
49
|
+
the repo.** A prior session — or DEMI — may have already built most of the venue
|
|
50
|
+
stack, gated off. One grep saves a duplicate build: `git log --oneline | grep -i
|
|
51
|
+
<venue>`, `ls strategies/ lib/ | grep -i <venue>`, `grep -rn "<VENUE>\|exec-client"
|
|
52
|
+
.env server/api.js`, and `pm2 logs demi-server --lines 300 --nostream | grep -i
|
|
53
|
+
<venue>` (is it FIRING right now?). This exact miss happened with HIP-4: the FULL
|
|
54
|
+
stack (exec client, divergence monitor, strategy, TP guard, per-user creds path)
|
|
55
|
+
was already committed and live-trading for one pilot wallet — nearly rebuilt a
|
|
56
|
+
duplicate. Also re-scope "for others": if the bot is already a MULTI-USER
|
|
57
|
+
per-wallet system, "trade X for everyone" = per-user opt-in (own wallet/key/
|
|
58
|
+
settings), NOT DEMI custodying others' funds — don't reflexively push back on
|
|
59
|
+
custody grounds before checking the architecture. Full worked example (what
|
|
60
|
+
existed, the pilot-gate→per-user conversion, deploy discipline, open GO-gates):
|
|
61
|
+
`references/hip4-already-built-per-user-conversion.md`.
|
|
62
|
+
|
|
63
|
+
If the venue is genuinely greenfield, THEN pressure-test as below:
|
|
64
|
+
1. **Pull real liquidity first, argue from numbers not vibes.** Get the actual
|
|
65
|
+
book depth on the new venue (for HIP-4: `outcomeMeta` + `l2Book "#<encoding>"`,
|
|
66
|
+
see the `hyperliquid` skill). Thin/favorite-concentrated books cap strategy
|
|
67
|
+
size to the top handful of markets — model that before proposing trades.
|
|
68
|
+
2. **Cross-venue arb hinges on RESOLUTION-SOURCE PARITY.** If the new venue's
|
|
69
|
+
oracle can resolve an event differently than the leg you're hedging against
|
|
70
|
+
(e.g. HL HIP-4 oracle vs Polymarket UMA), the "arb" is a naked directional bet
|
|
71
|
+
with BOTH legs able to lose. Verify identical resolution criteria before ever
|
|
72
|
+
calling it an arb.
|
|
73
|
+
3. **Separate the two builds and push back on the risky one.** "Trade X for
|
|
74
|
+
others" is ambiguous: (a) extend our own trading to a new venue = engineering;
|
|
75
|
+
(b) trade other people's capital = custody + trust + reg surface → push back
|
|
76
|
+
hard, do not wire third-party capital through our signer on day one.
|
|
77
|
+
4. **Shadow/paper FIRST, always.** A new edge goes through this circuit breaker:
|
|
78
|
+
build a read module + a shadow-mode spread scanner that LOGS would-be trades
|
|
79
|
+
without executing, collect a few days of data to prove the edge is real and
|
|
80
|
+
capturable, THEN GO-gate live with capital caps. Never hot-wire directional
|
|
81
|
+
trades on a new venue into the live money loop on introduction day.
|
|
82
|
+
5. Confirm the live bot's current state (pm2 status + halt-file check) before
|
|
83
|
+
proposing any change, so the new-venue work doesn't collide with a halt.
|
|
84
|
+
|
|
85
|
+
Worked example (HIP-4 outcome markets: endpoint probe, coin encoding, WC-2026
|
|
86
|
+
liquidity table, both-vs-illiquid analysis): `references/hip4-venue-evaluation.md`.
|
|
87
|
+
|
|
88
|
+
## Converting a pilot-gated venue strategy to FULL per-user (the "trade X for everyone" ask)
|
|
89
|
+
When DEMI says "make HIP-4 trade for others / everyone, like the PM bot," and STEP 0 finds
|
|
90
|
+
the venue stack already exists but gated to ONE pilot wallet (`HIP4_MANUAL_WALLETS`), the
|
|
91
|
+
work is a GATE CONVERSION, not a build. The PM bot is already multi-user per-wallet, and the
|
|
92
|
+
HIP-4 exec path was built per-user-ready — verify these before promising a rewrite:
|
|
93
|
+
- **The exec client already takes a key as a PARAMETER**, not a hardcoded wallet
|
|
94
|
+
(`createClient({ agentPrivateKey })`). The `_clients` map is `wallet -> client`. Per-user
|
|
95
|
+
creds already decrypt per-wallet (`auth.decryptCredentials(wallet, 'hl-credentials.json')`).
|
|
96
|
+
The onboarding endpoint (`POST /api/hip4/credentials`) + the encrypted-file whitelist in
|
|
97
|
+
`server/auth.js` already exist. So "per-user" is mostly ALREADY THERE.
|
|
98
|
+
- **What actually blocks it = two gate sites**, both single lines: (1) the strategy's
|
|
99
|
+
`pilotWallets().includes(wallet)` check in `execute()`, (2) the API's `requireHip4Pilot`
|
|
100
|
+
middleware on `/hip4/order` + `/hip4/cancel`. Convert BOTH to `optedIn || isPilot` where
|
|
101
|
+
`optedIn = bot.settings.trading.hip4Enabled === true` (mirror PM's `settings.trading.mode==='live'`
|
|
102
|
+
opt-in). Keep the pilot allowlist as a backward-compat OR so the pilot keeps trading.
|
|
103
|
+
- **Per-user sizing/caps**: global env `STAKE`/`MAX_POSITIONS` become per-user reads from
|
|
104
|
+
`bot.settings.trading.positionSizing.maxPerTradeUsd` + `settings.risk.maxConcurrentPositions`,
|
|
105
|
+
with the env value as fallback. Clamp stake to the HL 10.1 USDC min-notional floor.
|
|
106
|
+
- **A UI toggle is required for real parity** — the API accepts the setting but the dashboard
|
|
107
|
+
page (`src/pages/HIP4.jsx`) needs a control writing `settings.trading.hip4Enabled` via
|
|
108
|
+
`/api/settings` (copy the Settings.jsx save pattern). Without it, opt-in is a manual JSON flip.
|
|
109
|
+
- **THE FUNDING FACT (state it every time — it's the #1 user question):** HIP-4 outcome markets
|
|
110
|
+
settle in **USDC on the user's HYPERLIQUID SPOT wallet** — NOT Polymarket USDC, NOT an HL perps
|
|
111
|
+
balance. `getSpotState(wallet)` reads the spot `USDC` balance; the strategy skips if `usdcFree < stake`.
|
|
112
|
+
So a user needs: funded HL spot USDC + their own HL agent/API-wallet key stored + the toggle on.
|
|
113
|
+
PM's Polygon-USDC-on-the-CLOB and HIP-4's HL-native-spot-USDC are two SEPARATE funding pools;
|
|
114
|
+
a user can trade HIP-4 with zero Polymarket balance.
|
|
115
|
+
- **Merging is DORMANT — "won't trade unless they sign" is TRUE.** After merge, the gate ladder in
|
|
116
|
+
`execute()` is: `HIP4_ARB_LIVE=1` (global, already on) -> opted-in-OR-pilot -> **hl-credentials.json
|
|
117
|
+
exists with agentPrivateKey (the "sign" — hard stop if missing)** -> USDC>=stake -> position room ->
|
|
118
|
+
fresh-edge recheck. A user who does nothing stays invisible; only their explicit toggle+key opens
|
|
119
|
+
execution. State this to reassure DEMI a merge doesn't auto-expose anyone. (The pilot wallet keeps
|
|
120
|
+
trading regardless of merge — merge only ADDS the opt-in path.)
|
|
121
|
+
- **RISK-PROFILE GAP to flag before scaling capital:** the TP guard rests a GTC sell at 0.90 (env
|
|
122
|
+
`HIP4_TAKE_PROFIT_PRICE`) on a 60s poll — that is a take-profit-or-bust exit, NOT risk management.
|
|
123
|
+
There is NO stop-loss, NO mid-range/partial TP, NO time exit. Each position is ~all-or-nothing to
|
|
124
|
+
match resolution with an early cash-out ramp at 0.90. Fine at $10 pilot size; the single biggest
|
|
125
|
+
gap vs PM's risk discipline before bigger stakes or other users' capital.
|
|
126
|
+
|
|
127
|
+
Full worked conversion (exact edits, the CRLF-diff footgun that inflates a 15-line change to
|
|
128
|
+
9600 lines, the sell-direction notifier-label bug, deploy discipline, PnL-before-scale gate):
|
|
129
|
+
`references/hip4-already-built-per-user-conversion.md`.
|
|
130
|
+
|
|
131
|
+
## Adding an independent per-venue START/STOP toggle (the "let users run PM-only / HIP-4-only / both / neither" ask)
|
|
132
|
+
When DEMI says "give Polymarket and HIP-4 separate start/stop" / "different bot
|
|
133
|
+
trigger so people can run either or both," the architecture reality is usually:
|
|
134
|
+
ONE master start/stop loop scans ALL strategies each cycle (PM's `sports-odds` +
|
|
135
|
+
HIP-4's `hip4-arb` in the same `registry.scanAll`), so it's NOT two bots — it's one
|
|
136
|
+
loop with per-venue CONTENT switches. Build them as **OFF-only gates that default ON**:
|
|
137
|
+
- **Gate site = right after the strategy scan, before execution**, in the loop's
|
|
138
|
+
`_runScan` (`lib/trading-loop.js`, just after `setStrategyOpportunities`). Filter the
|
|
139
|
+
opportunity array by the venue tag: HIP-4 opps carry `venue:'hyperliquid'`, PM opps
|
|
140
|
+
don't. `polymarketEnabled===false` → keep only `venue==='hyperliquid'`;
|
|
141
|
+
`hip4Enabled===false` → drop `venue==='hyperliquid'`.
|
|
142
|
+
- **OFF-only, default ON is the safest class of money-path edit** — read the flag as
|
|
143
|
+
`!== false` (undefined = on) so behavior is byte-identical unless a user EXPLICITLY
|
|
144
|
+
disables a venue. The gate can only ever DROP opportunities, never create one, so it
|
|
145
|
+
cannot cause a trade — only prevent one. This is why it's shippable without a shadow
|
|
146
|
+
cycle (unlike a NEW edge, which must paper-first). Keep the asymmetry deliberate: PM
|
|
147
|
+
defaults ON (preserves every existing user), HIP-4 defaults OFF (still needs key + opt-in).
|
|
148
|
+
- **No restart needed** — `bot.settings` hot-reloads via an `fs.watch` on the user's
|
|
149
|
+
`settings.json` (debounced), so a dashboard toggle takes effect NEXT scan. Mirror the
|
|
150
|
+
existing per-venue opt-in read (`bot.settings.trading.hip4Enabled`) rather than baking
|
|
151
|
+
a value into the loop constructor.
|
|
152
|
+
- **UI = a second card, not a second power button.** Keep the master Start/Stop as the
|
|
153
|
+
engine power; add a "Venue Controls" card of two independent ON/OFF toggles under it,
|
|
154
|
+
each writing `settings.trading.{polymarketEnabled,hip4Enabled}` via the page's existing
|
|
155
|
+
`/api/settings` save pattern. Add both flags to the frontend `DEFAULT_SETTINGS.trading`
|
|
156
|
+
(PM:true, HIP-4:false).
|
|
157
|
+
- **VERIFY BY EXTRACTING THE FILTER INTO A UNIT TEST, not by trusting the default path.**
|
|
158
|
+
Copy the exact gate function into a throwaway `node` script and assert all four states
|
|
159
|
+
(both / PM-only / HIP-4-only / neither) plus the `undefined=on` case against a fixture
|
|
160
|
+
opp array. Default-on means the live log shows NO drop message on a normal deploy (correct,
|
|
161
|
+
not a failure) — so the standalone test is the real proof the OFF paths work, since prod
|
|
162
|
+
won't exercise them until a user flips a switch. Then confirm the loop is still scanning
|
|
163
|
+
live post-restart.
|
|
164
|
+
Full worked recipe (exact gate code, the venue-tag fact, the 6-case unit test, the master-loop-
|
|
165
|
+
vs-venue-content design decision, the "each venue as its own standalone bot" bigger-change note):
|
|
166
|
+
`references/per-venue-enable-toggle.md`.
|
|
167
|
+
|
|
168
|
+
## Wiring a NEW strategy into the SHARED learning loop (the "both bots self-learn" ask)
|
|
169
|
+
When DEMI says "wire X into learning / both strategies should self-learn / have the
|
|
170
|
+
agent take the trades so it self-learns," separate the two ideas he's fusing —
|
|
171
|
+
they point OPPOSITE directions:
|
|
172
|
+
- **"Agent takes the trades" = NO.** An LLM/agent in the hot execution path is wrong
|
|
173
|
+
for a deterministic arb: (1) latency — an arb edge lives seconds, an LLM round-trip
|
|
174
|
+
is 1-10s, so by the time it "decides" the edge is gone; (2) the edge is arithmetic
|
|
175
|
+
(book price vs consensus, a subtraction), nothing to reason about, a model only adds
|
|
176
|
+
hallucination + jitter; (3) money-path invariant — a non-deterministic process with
|
|
177
|
+
signing authority violates the circuit breaker. Execution stays deterministic and fast.
|
|
178
|
+
- **"It self-learns" = YES, but as the COLD layer beside the loop, not inside it.**
|
|
179
|
+
The right shape: Execution (hot, deterministic, ms) writes closed-trade outcomes →
|
|
180
|
+
Learning (cold, async, minutes) digests them → Params (thresholds, sizing, market
|
|
181
|
+
whitelist) the loop reads next scan. The agent's job is tuning PARAMETERS from the
|
|
182
|
+
feedback, never placing ORDERS.
|
|
183
|
+
- **The real gap is usually plumbing, not intelligence.** The PM bot already has the
|
|
184
|
+
full learning loop (`lib/trade-feedback.js` scores closed trades → LLM filter gets
|
|
185
|
+
more selective; `bayesian-updater.js`, `edge-scorer.js`, `clv-cutoff.js`). Grep the
|
|
186
|
+
learning modules for the new strategy/venue name — if it's ZERO, the new strategy
|
|
187
|
+
feeds nothing back and "it self-learns" is simply false for it. Wiring it in ≠ adding
|
|
188
|
+
an agent; it's connecting the recorder.
|
|
189
|
+
- **The hard part for an on-chain venue (HIP-4): positions VANISH on close.** HIP-4
|
|
190
|
+
positions are on-chain spot coins (`+N` balances) that disappear from state when they
|
|
191
|
+
TP or settle — nothing writes a closed-trade record, so `trade-feedback` (which globs
|
|
192
|
+
`data/users/*/portfolio*.json` for records with `realizedPnl`) sees zero history. The
|
|
193
|
+
fix is a small RECORDER + RECONCILER, mirroring PM's own `backfillRealizedPnl`
|
|
194
|
+
(sell-fills matched against buys): (1) `recordEntry` at fill time into a
|
|
195
|
+
`portfolio-hip4.json` in the EXACT shape trade-feedback already reads (`strategy`,
|
|
196
|
+
`realizedPnl:null` while open, `entryCost`, `edgePercent`, `question`, timestamps) —
|
|
197
|
+
no reader-side wiring needed, the glob picks it up; (2) a read-only `reconcile` (pulls
|
|
198
|
+
`userFills` + `spotState`, NO signing) that books `realizedPnl = sellProceeds − entryCost`
|
|
199
|
+
when the coin's balance is gone, and `−entryCost` for coins that settled worthless with
|
|
200
|
+
no sell fill; (3) call reconcile on an existing per-wallet poll you already run (the TP
|
|
201
|
+
guard's 60s loop is ideal — it already iterates the same wallets).
|
|
202
|
+
- **Never let the ledger block the fill.** Wrap `recordEntry`/`reconcile` in try/catch —
|
|
203
|
+
a learning-ledger write failure must NEVER fail an executed trade or halt the guard.
|
|
204
|
+
- **VERIFY with a real end-to-end unit test against the actual modules**, mocking only the
|
|
205
|
+
network reads. Record a winner + a loser, mock a TP sell-fill + a worthless-settle, run
|
|
206
|
+
reconcile, then call `trade-feedback.getStrategyFeedback()` and assert the new strategy
|
|
207
|
+
bucket now shows wins/losses/totalPnl (exact arithmetic). Then confirm the live read
|
|
208
|
+
endpoint (`userFills`) is real against a funded wallet — a mock passing proves logic,
|
|
209
|
+
the live call proves the endpoint + fill shape (`dir:'Buy'/'Sell'`, `side:'B'/'A'`).
|
|
210
|
+
- **Set the honest expectation:** the ledger only captures fills AFTER deploy, and can't
|
|
211
|
+
learn until positions round-trip. If the venue has zero realized PnL, the plumbing is
|
|
212
|
+
correct-but-empty — build it BEFORE trades close (capture in place) but don't claim it's
|
|
213
|
+
"learning" until real closed trades exist.
|
|
214
|
+
Full worked recipe (the 4-file wiring, the ledger module, getUserFills read-only method,
|
|
215
|
+
the on-chain-position reconciliation math, the agent-cold-layer-not-hot-path decision, the
|
|
216
|
+
end-to-end test): `references/shared-learning-loop-wiring.md`.
|
|
217
|
+
|
|
218
|
+
### The bot alerts ITSELF on round-trips — not Hermes (DEMI preference, corrected)
|
|
219
|
+
When the reconciler detects a close and DEMI wants to be pinged, the alert must come from
|
|
220
|
+
**the bot's own Notifier**, not a Hermes cron/watch. DEMI's exact steer: "telegram bot should
|
|
221
|
+
be able to alert not you." The mechanics fall out naturally because the reconcile runs INSIDE
|
|
222
|
+
the TP guard, which already holds a `notifier`:
|
|
223
|
+
- **Make `reconcile` RETURN the closed trades** (`{ closed, stillOpen, closedTrades }`), then the
|
|
224
|
+
guard (which owns the notifier) fires one `notifier.sendTelegram(...)` per round-trip. Same
|
|
225
|
+
channel as the fill/TP alerts — same infra DEMI already reads. No new watcher, no Hermes in the loop.
|
|
226
|
+
- **Wrap the alert send in try/catch** — a Telegram failure must never fail the guard cycle
|
|
227
|
+
(same non-blocking rule as the ledger writes).
|
|
228
|
+
- **Sign-before-`$` formatting:** a negative PnL rendered as `${pnl>=0?'+':''}$${pnl}` prints the
|
|
229
|
+
ugly `$-10.00`. Use `const sign = pnl>=0?'+':'-'` then `${sign}$${Math.abs(pnl).toFixed(2)}` →
|
|
230
|
+
`-$10.00`. Caught this only because the end-to-end test asserted the exact rendered string —
|
|
231
|
+
format bugs are invisible until you assert the literal output.
|
|
232
|
+
- The notifier auto-silences on staging (`STAGING==='1'` chokepoint), so a shadow deploy won't spam.
|
|
233
|
+
|
|
234
|
+
## When a new venue "won't fire" — the SHARED-GUARD cross-venue mismatch (the "shouldn't it be trading like my main wallet?" ask)
|
|
235
|
+
After wiring a new venue (HIP-4) into the shared loop, DEMI will ask "shouldn't it
|
|
236
|
+
be firing?" and the honest answer is often **it's a real bug, not "waiting"** — the
|
|
237
|
+
new venue's opportunities are being BLOCKED by the shared safety guard applying the
|
|
238
|
+
WRONG venue's rules. Diagnose from the live log FIRST, then fix venue-aware:
|
|
239
|
+
- **Read the block reasons before theorizing.** `pm2 logs demi-server --lines 300
|
|
240
|
+
--nostream | grep -iE 'guard blocked <venue>|<strat>.*skip'` then pipe through
|
|
241
|
+
`sed`/`sort`/`uniq -c` to rank reasons. Separate the CORRECT skips (strategy's own
|
|
242
|
+
rules — e.g. HIP-4's `already holding a side of outcome NNN (one position per
|
|
243
|
+
outcome)`, or `not opted in`) from the WRONG blocks (the shared PM guard).
|
|
244
|
+
- **The mismatch class: the shared `safety-guard.js checkTrade()` judges the new
|
|
245
|
+
venue by Polymarket's bankroll + market rules.** HIP-4 opps carry
|
|
246
|
+
`sport:'soccer'` + `liquidity:$8-14` (real HL book depth) and get slammed by FIVE
|
|
247
|
+
PM-specific gates that measure the wrong thing: (1) position-size cap sized off PM
|
|
248
|
+
equity (`Position $10.5 > max $1.47` — tiny + fluctuating because the PM portfolio
|
|
249
|
+
read is near-empty / stale from a dead RPC), (2) total-exposure %, (3) 20% cash
|
|
250
|
+
reserve, (4) the sport whitelist (`mlb,nba,nhl,ufc,esports` — soccer isn't in it,
|
|
251
|
+
so `Sport not whitelisted: 'soccer'` blocks EVERY WC outcome even after fixing
|
|
252
|
+
size/liquidity), (5) the `soccer:2000` dead-market liquidity floor (thin HIP-4
|
|
253
|
+
books can't clear it, and the `clobDepth>=size*1.5` bypass fails on $8 depth).
|
|
254
|
+
- **Fix = make the guard venue-aware; bypass ONLY the PM-portfolio/PM-market gates
|
|
255
|
+
for `opportunity.venue==='hyperliquid'`.** Define `const isHyperliquid =
|
|
256
|
+
opportunity.venue === 'hyperliquid'` once at the top of the size-cap block, then
|
|
257
|
+
wrap each of the 5 gates with `if (!isHyperliquid && <original condition>)`. The
|
|
258
|
+
new venue is already gated correctly by its OWN `execute()`-time checks against
|
|
259
|
+
ITS bankroll (per-user stake, `usdcFree>=stake`, live `ask*askSz>=stake` depth,
|
|
260
|
+
`PHANTOM_CAP` edge ceiling, fresh-edge recheck, per-outcome maxPositions) — the PM
|
|
261
|
+
floor is redundant AND wrong. **KEEP the account-level, venue-agnostic gates
|
|
262
|
+
applied to both** (halt, daily-loss, hourly/daily rate limits,
|
|
263
|
+
`LIVE_TRADING_CONFIRMED` file). Don't blanket-skip the whole guard.
|
|
264
|
+
- **This is loosening a live safety gate → circuit-breaker discipline applies.**
|
|
265
|
+
GO-gate it. But note it's LOW-risk to ship direct once you've proven the strategy's
|
|
266
|
+
own depth/phantom/fresh-edge gates are STRICTER than the PM floor being removed —
|
|
267
|
+
the bypass can only let a HIP-4 opp reach ITS OWN gates, not skip them.
|
|
268
|
+
- **The counterintuitive answer to "will it trade LESS?": no, MORE.** Removing the
|
|
269
|
+
wrong blocks UNBLOCKS firing. What still (correctly) caps volume is structural and
|
|
270
|
+
stays: one-position-per-outcome + hold-to-resolution. So the venue is episodic by
|
|
271
|
+
design — it fires on a NEW un-held divergence or when a held position closes, not
|
|
272
|
+
high-frequency. If every current opp maps to an already-held outcome, "not firing
|
|
273
|
+
right now" is the per-outcome rule working, NOT a block.
|
|
274
|
+
- **VERIFY the guard change with a unit test against the REAL safety-guard module:**
|
|
275
|
+
a HL opp must PASS all 5 gates; a PM opp with the same params must STILL be blocked
|
|
276
|
+
by each (proves you didn't punch a hole in the Polymarket path). Assert the exact
|
|
277
|
+
block reasons (`Position $X > max`, `Sport not whitelisted`, `Illiquid`). Then
|
|
278
|
+
restart and confirm the `guard blocked <venue>` lines are GONE from the live log.
|
|
279
|
+
- **Ground-truth the pilot state before declaring "working":** pull `getSpotState`
|
|
280
|
+
for the pilot — free USDC (has capital?), held outcome coins (`+N` balances), and
|
|
281
|
+
compare held-outcome-IDs vs the live outcome universe (`outcomeMeta`). That tells
|
|
282
|
+
you definitively whether "not firing" = blocked (bug) vs = every opp is an
|
|
283
|
+
already-held outcome (correct) vs = no un-held divergence exists right now.
|
|
284
|
+
Full worked recipe (the 5 gates + exact wrap edits, the venue-tag routing, the
|
|
285
|
+
real-module PM-still-blocked unit test, the pilot ground-truth probe):
|
|
286
|
+
`references/venue-aware-safety-guard.md`.
|
|
287
|
+
|
|
288
|
+
## PR hygiene when another agent is active on the same trading repo
|
|
289
|
+
When a live trading PR was assembled while another agent pushed to `origin/main`, treat
|
|
290
|
+
"mergeable" as necessary but not sufficient. Clean the PR branch before asking DEMI or
|
|
291
|
+
the peer agent to review it:
|
|
292
|
+
- Strip accidental backup/scratch files from broad `git add -A` commits (`*.bak*`,
|
|
293
|
+
timestamped recovery files, unreferenced analysis scripts, generated build dirt).
|
|
294
|
+
- Fix CRLF/LF phantom rewrites before push: restore the file from `origin/main`, reapply
|
|
295
|
+
only the real code changes, and verify the **normal** diff is reviewable (not just
|
|
296
|
+
`git diff -w`). A `server/api.js` `+4855/-4800` phantom diff hid a real `+57/-2` endpoint
|
|
297
|
+
change — do not leave that for reviewers.
|
|
298
|
+
- Re-run syntax, focused money-path probes, and frontend build on the CLEANED branch;
|
|
299
|
+
reset build artifacts after the build.
|
|
300
|
+
- Push a PR branch and comment with exactly what hygiene changed + verification results;
|
|
301
|
+
don't push directly to `main` while a peer agent is live unless DEMI explicitly chooses it.
|
|
302
|
+
Full cleanup recipe and HIP-4 PR #222 example: `references/pr-hygiene-active-peer-merge.md`.
|
|
303
|
+
|
|
304
|
+
## Deploying a strategy-file change to the LIVE bot (pm2, not deploy.sh)
|
|
305
|
+
- Prod runs under **pm2 `demi-server`** (not the docker `deploy.sh`). A strategy/API change needs
|
|
306
|
+
a `pm2 restart demi-server --update-env` to load; a dashboard change needs `npm run build`
|
|
307
|
+
(rebuilds `dist/`, no restart). The restart briefly interrupts the sports loop (~seconds; it
|
|
308
|
+
resumes on boot — routine, dozens of prior restarts) and reloads code from disk.
|
|
309
|
+
- **Staging is a SEPARATE checkout** (`/root/polymarket-arbitrage-bot-staging`, `SHADOW_GATES=1`,
|
|
310
|
+
NO signing keys → physically cannot move money) and can lag prod by many commits — it may not even
|
|
311
|
+
have the venue code yet. To shadow-test on staging, `git fetch <prod-path> main` + checkout the
|
|
312
|
+
feature branch there first. A shadow gate test can prove new gate LOGIC fires (opted-in wallet
|
|
313
|
+
passes, non-opted skips) even with no keys — the execute() path stops at the creds check, which is
|
|
314
|
+
exactly the "past the gate" proof you want.
|
|
315
|
+
- **CRLF footgun:** the repo's JS files are CRLF; a python string-writer normalizes to LF and git
|
|
316
|
+
then shows EVERY line changed (a 15-line edit -> 9600-line phantom diff). Detect with
|
|
317
|
+
`git diff -w --stat` (real change size) and restore endings with a python
|
|
318
|
+
`.replace(b'\r\n',b'\n').replace(b'\n',b'\r\n')` pass before committing, so `git blame`/review stay clean.
|
|
319
|
+
- **REMOTE-PATCH MECHANICS — prefer pull-local → fuzzy `patch` tool → scp-back over remote string-writers.**
|
|
320
|
+
Editing VPS files by `scp`ing a python string-writer script (or worse, a `ssh ... python3 << 'PYEOF'`
|
|
321
|
+
heredoc) is brittle and burned two attempts this session: (1) a `raw.replace()` anchor silently
|
|
322
|
+
no-ops when the on-disk text differs by a whitespace/CRLF hair from your assumed string, and the
|
|
323
|
+
script's own `assert count==1` then throws AFTER a partial edit — leaving you unsure what changed;
|
|
324
|
+
(2) template-literal backticks + `${...}` + emoji `\u` escapes inside a remote heredoc get mangled
|
|
325
|
+
by the shell before python ever sees them (`unexpected EOF while looking for matching quote`). The
|
|
326
|
+
reliable path: `scp demi-poly:<path> /tmp/f.local.js`, apply the `patch` tool (fuzzy match tolerates
|
|
327
|
+
minor whitespace drift, auto-lints, returns a real diff), then `scp` it back and `node -c` on the VPS.
|
|
328
|
+
Idempotency-guard any string-writer you DO keep with an early `if MARKER in raw: skip` so a re-run
|
|
329
|
+
can't double-apply. And when a unit test embeds a REPLICA of the code under test (an inline
|
|
330
|
+
`buildAlert`), fixing the real file leaves the replica stale → the test fails on format you already
|
|
331
|
+
fixed; point the test at the real module, or assert against the deployed source text, so it can't drift.
|
|
332
|
+
- **Protect uncommitted prod hotfixes:** prod's working tree often carries live-edited tracked files
|
|
333
|
+
not on any branch (e.g. `gpu-client.js`, `trading-loop.js`, `loop-manager.js`). Snapshot them
|
|
334
|
+
before any branch op, stage ONLY your files, and prefer `git branch -f main <feature>` (ref update,
|
|
335
|
+
worktree untouched) over a checkout that could clobber them.
|
|
336
|
+
- **Verify the deploy from the LIVE log, not the restart exit code:** after restart, grep
|
|
337
|
+
`demi-server-out.log` for the NEW gate message (e.g. the per-user skip reason) to prove the new
|
|
338
|
+
code path is the one running — a green pm2 status only means the process booted.
|
|
339
|
+
- **MERGING when ANOTHER AGENT is active in the same repo (the "the other agent already pushed to
|
|
340
|
+
main" situation).** DEMI runs a dual-machine + multi-agent setup, so `origin/main` can move under
|
|
341
|
+
you mid-session. Deploy discipline here shipped by moving the LOCAL `main` ref (`git branch -f`) on
|
|
342
|
+
the prod worktree WITHOUT pushing — so local `main` and `origin/main` DIVERGE (local ahead with your
|
|
343
|
+
N commits, origin ahead with the peer's push). Reconcile safely:
|
|
344
|
+
1. **`git fetch origin` and read the divergence BEFORE touching anything:** `git rev-list
|
|
345
|
+
--left-right --count origin/main...main` (are you ahead/behind/both?), `git diff --name-only main
|
|
346
|
+
origin/main` (conflict surface — overlapping files = real risk, not a clean FF). NEVER force-push
|
|
347
|
+
over a branch a peer just pushed to.
|
|
348
|
+
2. **Merge in an ISOLATED `git worktree`, never the running prod checkout:** `git worktree add -f
|
|
349
|
+
/root/_merge <yourtip>` then `git merge origin/main` there. Prod's checkout + its uncommitted
|
|
350
|
+
hotfixes stay untouched. Symlink `node_modules` from prod so the build works.
|
|
351
|
+
3. **"Clean auto-merge" ≠ correct — re-verify on the MERGED tree.** git's ort strategy resolves
|
|
352
|
+
non-overlapping regions of the same file silently; that's conflict-free, not proven-correct. Re-run
|
|
353
|
+
the money-path unit tests (venue-guard: HL passes / PM still blocked), rebuild the frontend, and
|
|
354
|
+
`node -c` the money-path files ON the merged worktree before trusting it.
|
|
355
|
+
4. **Confirm it's a true fast-forward (peer's work preserved as a parent, no history rewrite):**
|
|
356
|
+
`git merge-base --is-ancestor origin/main <mergetip>` → if origin/main is an ancestor, the push is
|
|
357
|
+
a clean FF and the peer's commit survives as a merge parent. If NOT an ancestor, you'd need a force
|
|
358
|
+
— STOP and reconsider.
|
|
359
|
+
5. **DEMI's preference under an active peer: PR, don't push straight to main.** Even when the git is a
|
|
360
|
+
clean FF and everything's verified, when a SECOND agent is actively working the repo DEMI chose
|
|
361
|
+
"open a PR so the other agent can review before it lands" over "push to main now." Push your merge
|
|
362
|
+
to a feature branch, open the PR into main (call out the commits that OVERLAP the peer's domain in
|
|
363
|
+
the PR body), and leave the merge to human/peer approval. Rationale: force-landing onto a branch a
|
|
364
|
+
peer just touched clobbers their mental model even when the bytes reconcile. Offer the choice
|
|
365
|
+
(PR / push-direct / hold) rather than auto-deciding — but lead with PR when a peer is live.
|
|
366
|
+
6. **DEMI-DELIVERY / TELEGRAM: send a real message to prove a claim, don't just format it.** When the
|
|
367
|
+
deliverable is "the bot texts him," the not-yet-verified piece is DELIVERY, not formatting. Construct
|
|
368
|
+
the actual Notifier and do ONE real labeled `sendTelegram` test (assert `ok===true`) — a unit test
|
|
369
|
+
of the string proves layout, only a live send proves the channel works. (`dotenv`/`node_modules`
|
|
370
|
+
resolve from the REPO dir, so run the probe from inside the repo, not `/tmp`.)
|
|
371
|
+
|
|
372
|
+
## Diagnosing "why isn't the bot trading?" — DON'T THEORIZE, QUERY GROUND TRUTH (the five-wrong-turns lesson)
|
|
373
|
+
When DEMI asks "why haven't we been trading" / "shouldn't it be on" / "did the money go
|
|
374
|
+
back to X", the failure mode is answering from CONFIG or INFERENCE and being wrong. One
|
|
375
|
+
session produced FIVE consecutive wrong conclusions on a live-money bot, each corrected only
|
|
376
|
+
by pulling actual live state: (halted→wrong) → (capital-starved→wrong, wallet had funds) →
|
|
377
|
+
(zombie loop→wrong, loop had 16k scans) → (session-state stopped→wrong, marked running) →
|
|
378
|
+
(finally: the strategy evaluates the WRONG wallet). Every wrong turn was a guess from a file;
|
|
379
|
+
every correction came from a live query. The rule: **for any "why isn't it doing X" on the
|
|
380
|
+
bot, the FIRST move is a ground-truth pull, not a hypothesis.** State confidence honestly and
|
|
381
|
+
walk back cleanly when the data contradicts you — do NOT defend a prior guess.
|
|
382
|
+
|
|
383
|
+
The ground-truth checklist, run these BEFORE proposing any cause:
|
|
384
|
+
- **Live venue balance, not the config or the vault claim.** HL spot USDC via the HL info API
|
|
385
|
+
(`spotClearinghouseState` for spot USDC + held `+N` outcome coins; `clearinghouseState` for
|
|
386
|
+
perps/withdrawable). Polymarket via the proxy wallet on-chain. The vault's P0 line and the
|
|
387
|
+
bot's cached portfolio can BOTH be a month stale — I asserted "capital starved" when the
|
|
388
|
+
wallet had $173 free because I inferred drawdown from losing trades instead of querying.
|
|
389
|
+
- **Which wallet is the strategy actually evaluating?** `pm2 logs demi-server --lines 300
|
|
390
|
+
--nostream | grep -iE '<strat>' | grep -oE 'wallet 0x[a-f0-9]{6}' | sort | uniq -c`. The
|
|
391
|
+
loop scans opps globally then each running wallet-loop tries to EXECUTE them with its OWN
|
|
392
|
+
address. If the wallet with the key/funds/allowlist entry isn't in that list, it's never
|
|
393
|
+
evaluated — and the OTHER wallets correctly skip ("not opted in and not a pilot"). This was
|
|
394
|
+
the actual root cause and it was invisible until I counted wallets in the log.
|
|
395
|
+
- **Is the loop even instantiated for that wallet?** `data/user-session-state.json`: a wallet
|
|
396
|
+
needs `startedAt && !stoppedAt` to auto-resume on restart. But "marked running" ≠ "actually
|
|
397
|
+
scanning" — cross-check against real scan activity in the log. `POST /api/bot/start` (auth via
|
|
398
|
+
a minted `lo_` API key, see below) reinstantiates a dead loop; its response reveals `scanCount`
|
|
399
|
+
+ `lastScanAt` which tells you if the loop was live all along.
|
|
400
|
+
- **What is it skipping on, RIGHT NOW?** The skip lines are the answer, read them literally:
|
|
401
|
+
`ask depth $8.00 < stake $10.5` (thin book, untradeable at min stake — NOT a bug),
|
|
402
|
+
`already holding a side of outcome NNN` (per-outcome cap working), `edgeThreshold 0.03` floor
|
|
403
|
+
(a 2.2% edge is below the gate, correct). Most "it's dead" is the safety gates + thin liquidity
|
|
404
|
+
working as designed, not a fault. Distinguish "correctly quiet" from "wrongly blocked."
|
|
405
|
+
|
|
406
|
+
Minting an API key to hit an authed bot endpoint from the VPS (no browser): keys are stored as
|
|
407
|
+
SHA-256 hashes in `data/api-keys.json` so you can't recover one, but `lib/api-auth.js`'s
|
|
408
|
+
`generateKey(wallet)` mints a fresh one (auto-revokes the wallet's prior key, clean + reversible):
|
|
409
|
+
`KEY=$(node -e "console.log(require('./lib/api-auth').generateKey('0xWALLET'))")` then
|
|
410
|
+
`curl -s -X POST http://localhost:<port>/api/bot/start -H "X-API-Key: $KEY"`. Note the `/api`
|
|
411
|
+
prefix (bare `/bot/start` 404s). Get the port from `ss -tlnp | grep node`, not a guess.
|
|
412
|
+
|
|
413
|
+
The "money moved from HL back to Polygon" class of question is usually a FALSE PREMISE: HL spot
|
|
414
|
+
USDC and Polymarket Polygon USDC are SEPARATE pools that never auto-transfer. "It went back" is
|
|
415
|
+
almost always "the HL pile was traded down / is separate and untouched," not a sweep. Pull both
|
|
416
|
+
balances and the HL trade ledger (`portfolio-hip4.json`) before accepting the premise.
|
|
417
|
+
|
|
418
|
+
Fix "always be trading" the RIGHT way: it's rarely a flag flip. If capital + key + allowlist are
|
|
419
|
+
present and it's still quiet, the cause is structural (wrong-wallet routing, thin books, per-outcome
|
|
420
|
+
cap, edge floor) — and "loosen the gates to force fills" makes it trade WORSE (slippage into $2-8
|
|
421
|
+
books = the exact losing pattern). Recommend the surgical routing/visibility fix, not gate-loosening,
|
|
422
|
+
and GO-gate anything that re-arms live execution. Full worked investigation (the five wrong turns,
|
|
423
|
+
every query that corrected them, the HL API calls, the wallet-routing root cause):
|
|
424
|
+
`references/why-isnt-the-bot-trading.md`.
|
|
425
|
+
|
|
426
|
+
## Building a public scanner + wallet/CEX execution product
|
|
427
|
+
|
|
428
|
+
A scanner dashboard that can buy, sell, place exits, or delegate authority to agents is a NEW live-money loop, even when its default mode is paper. Load and follow `references/agentic-scanner-execution-gate.md` before implementation. It covers canonical onchain discovery vs promoted feeds, Rabby as signer rather than router, unsupported honeypot-chain semantics, fail-closed slippage, authenticated preview binding, real kill switches, honest exit-policy states, persistent shadow-agent rollout, third-party chart embed boundaries, draggable TP/SL architecture, and DEMI's required Fable → Sol → Sonnet → Opus build/review sequence.
|
|
429
|
+
|
|
430
|
+
**UI truthfulness gate for Quick Buy, APE, TP, and SL:** Quick Buy and APE may only select a market and preset size; they do not bypass quote freshness, route verification, round-trip/sell simulation, policy limits, or wallet confirmation. An APE button is an aggressive preset, not permission for blind broadcast. TP/SL controls may be called active or automated only after a durable background executor has been verified to monitor prices and submit protected exits. If the backend only stores policies, label the UI and API response `DRAFT`, return `executable:false`, and state that every sell still needs a fresh quote, simulation, and wallet signature. A polished control that implies nonexistent automation is a money-path defect.
|
|
431
|
+
|
|
432
|
+
When the product moves from scanner/paper mode to public self-custodial wallet swaps or NFT mints, also load `references/public-self-custodial-scanner-execution.md`. It defines public-wallet vs operator/admin authority, wallet-connect prominence, the honest execution ladder, verified-ABI mint simulation, unsupported-chain coverage labels, action-first trading UI hierarchy, and the mandatory post-integration security gate covering process-isolated signers, principal-relative round-trip loss, enforceable mint freshness or simulation-only fallback, per-user watchlist isolation, complete-vs-partial portfolio valuation, and fail-closed data labels.
|
|
433
|
+
|
|
434
|
+
## Pitfalls
|
|
435
|
+
- Treating wallet signature verification as account authorization → any stranger can create a wallet and become an operator; bind signed addresses to a server-owned operator allowlist/enrollment and separate admin authority.
|
|
436
|
+
- Treating `CONTROLS_READY=1` or `LIVE_ENABLED=0` as a security boundary → flags do not implement holdings/PnL/exposure controls and can be flipped; compile live submission out of incomplete preview builds.
|
|
437
|
+
- Treating a halt as a bug to work around → it's a capital-protection feature. Never bypass.
|
|
438
|
+
- Answering "why isn't it trading" from config/vault/inference instead of a live ground-truth pull → you WILL be wrong; five times in one session. Query first, theorize never.
|
|
439
|
+
- Resetting without reconciling → you can resume into a wrong-state position.
|
|
440
|
+
- Editing the live loop without a backup + revert command in hand → violates the core invariant.
|
|
441
|
+
- Assuming pm2 "online" means healthy → check heartbeat + recent trade/reconcile logs.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: venue-capability-boundaries
|
|
3
|
+
description: Check what each venue supports before routing user requests.
|
|
4
|
+
category: trading
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
> Oracle native tools: `oracle_cli` (read/prepare), `vault_status`, `signer_status`, `signer_execute` (needs human confirmationNonce), `skill_load`. No generic shell. No fleet SSH.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# Venue capability boundaries
|
|
11
|
+
|
|
12
|
+
Users ask for things venues don't support. Cross-reference before routing.
|
|
13
|
+
|
|
14
|
+
## Hyperliquid HIP-4
|
|
15
|
+
|
|
16
|
+
BTC daily binaries only (2026-08). No geopolitical/event markets on mainnet.
|
|
17
|
+
→ Event bets → Polymarket, not HL.
|
|
18
|
+
|
|
19
|
+
## Hyperliquid HIP-3 builder dexs
|
|
20
|
+
|
|
21
|
+
Perps only. No spot equities. `xyz:NVDA` is a perp.
|
|
22
|
+
→ "spot NVDA on TradeXYZ" must clarify: HIP-3 has no spot.
|
|
23
|
+
|
|
24
|
+
## TradeXYZ (xyz)
|
|
25
|
+
|
|
26
|
+
96+ assets on HL, all perps: `xyz:NVDA`, `xyz:TSLA`, `xyz:AAPL`, `xyz:SPX`.
|
|
27
|
+
|
|
28
|
+
## Oracle fees
|
|
29
|
+
|
|
30
|
+
Swaps: 10 bps. Bridges: +10 bps. HL builder codes: cap 10 bps perp / 1% spot.
|
|
31
|
+
|
|
32
|
+
Live HL builder dex data + HIP-4 docs: `references/hip4-and-builder-limits.md`.
|