Commit Graph

11 Commits

Author SHA1 Message Date
direkturcrypto 6a2bbfa008 fix(oneshot): remove global position limit — block per-market only via SM state 2026-02-24 15:05:48 +07:00
direkturcrypto 8296c4129b fix(oneshot): add momentum scoring, widen gates to fix rare-entry problem
Root causes identified and fixed:

1. SPREAD_MAX 0.02 → 0.04  (biggest culprit — near-expiry books often have
   0.03 spread, hard gate was blocking all valid entries)

2. tteMax 90s → 150s  (direction is established by TTE=150s on 5m markets;
   previous 90s window was too narrow, skipped the "trend building" phase)

3. Added momentum as scoring factor W_MOMENTUM=0.30  ("follow where odds
   are moving" — midSlope6s from FeatureEngine now drives 30% of entry score)

4. Added SLOPE_CANCEL momentum gate (-0.0020): if dominant side's mid is
   actively falling (reversal risk), block entry regardless of mid level.
   New reason code: SIG_FADING_DOMINANT

5. Revised score weights: MID 45%→35%, IMBALANCE 35%→20%, SPREAD 20%→15%,
   MOMENTUM 0%→30%

6. Score threshold 0.55 → 0.42 (now calibrated for 4-factor scoring)

7. minDominantMid default 0.60 → 0.58, tteMin 20 → 15

8. Spread gate now checks min(up.spread, down.spread) — dominant side only
   needs to be tradeable, not both sides

Updated .env and .env.example defaults to match new parameters.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 14:48:36 +07:00
direkturcrypto 89a7803aa8 feat(oneshot): add auto-redemption via RedeemEngine
When a position's market expires, RedeemEngine automatically polls the
CTF contract and redeems the winning position on-chain — no manual redeem needed.

Flow:
  1. expirePosition() queues the expired position into RedeemEngine
  2. RedeemEngine polls every 30s (ONESHOT_REDEEM_POLL_MS)
  3. Checks Gamma API first, then CTF.payoutDenominator() on-chain
  4. When settled: emits redemption:complete event with final P&L
  5. Orchestrator passes P&L to RiskEngine

DRY_RUN=true: simulates by reading on-chain payouts and logging win/loss
DRY_RUN=false: submits real redeemPositions() tx on Polygon (gasLimit 300k)

Also stores conditionId and negRisk in PositionEngine state so the
expired position has all data needed for redemption without extra lookups.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 14:20:57 +07:00
direkturcrypto 2692694309 fix(oneshot): add global position limit — no new entry while any position is open
Previously the per-market state machine check (sm.is(IDLE)) only blocked
re-entry on the same slug. A fresh market slot (different slug) would get
its own IDLE state machine and could trigger another entry while the previous
market's position was still being held.

Added posEngine.hasAnyPosition() global guard in onSignal so the engine holds
exactly one position at a time across all tracked markets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 14:07:13 +07:00
direkturcrypto bf09d30376 refactor(oneshot): switch from scalper to Dominant Side Hold strategy
Previous behaviour: enter any side with positive momentum, exit at TP (+1 tick),
cycle back to IDLE — causing rapid buy-sell-buy loops on low-probability tokens.

New strategy:
- Enter ONLY the side the market already prices as probable winner (mid >= MIN_DOMINANT_MID)
- Hold position to market expiry; on-chain redeemer settles at $1.00 win / $0.00 loss
- Emergency stop-loss only (absolute mid floor, e.g. 0.20) for catastrophic reversals
- One entry per market slot — no re-entry while POSITION_OPEN

Key changes:
- SignalEngine: detect dominant side (up.mid vs down.mid), require MIN_DOMINANT_MID
  threshold, new scoring weights (mid 45% / imbalance 35% / spread 20%)
- PositionEngine: remove TP, slope-drop, time-reduce exits; add expired handler;
  stop-loss is now an absolute mid floor instead of relative-to-entry ticks
- oneshot.js: expirePosition() clears state without submitting sell orders;
  flattenPosition() only called for emergency stops; update cfg vars
- constants.js: add SIG_NO_DOMINANT, SIG_LOW_DOMINANT, EXIT_EXPIRED reason codes
- .env.example: replace ONESHOT_TP_TICKS with MIN_DOMINANT_MID, STOP_LOSS_MID,
  TTE_MIN, TTE_MAX

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 14:05:44 +07:00
direkturcrypto 707d654749 fix(oneshot): rewrite MarketFeedService market discovery to match working detectors
- Use /markets/slug/{slug} direct endpoint (not /markets?slug=...&limit=1)
- Extract token IDs from clobTokenIds field with JSON string parsing fallback
- Read tick size from market.orderPriceMinTickSize (no separate API call)
- Prioritise endDate (full datetime) over endDateIso (date-only) to fix false expiry
- Slot formula matches sniperDetector/mmDetector exactly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 13:40:09 +07:00
direkturcrypto acf3943f80 fix(oneshot): use endDate instead of endDateIso for market expiry check
The Gamma API returns two date fields:
  endDateIso = "2026-02-24"           ← date only, no time
  endDate    = "2026-02-24T06:35:00Z" ← correct close datetime

_parseEndTs() was preferring endDateIso, which parsed to midnight UTC
and was already in the past by the time any market opened during the day.
Both current and next-slot markets were therefore rejected as "already expired".

Fix: prioritise endDate (full ISO datetime) over endDateIso (date-only).

Co-Authored-By: direkturcrypto <direkturcrypto.x@mail3.me>
2026-02-24 13:33:48 +07:00
direkturcrypto 202bf98fe7 feat(oneshot): add ONESHOT_DEBUG verbose logging mode
Adds a debug flag (ONESHOT_DEBUG=true / --debug / npm run oneshot-debug)
that surfaces the engine's internal decision process at every key step.

Debug output tags
─────────────────
  [DBG:FEED]   — Market discovery: every slug probed, API response status,
                 token IDs extracted, tick size fetched.
                 Throttled poll summary every 10 ticks per market showing
                 bid/ask/spread/mid/depth for both UP and DOWN sides.

  [DBG:GATE]   — Hard gate result every 5 evaluations per market:
                 TTE range, spread width, depth thinness, stale flag.
                 Shows exact gate fail reason or PASS confirmation.

  [DBG:FEAT]   — Feature breakdown for each side every 5 evals:
                 slope, imbalance, spread, retrace raw values plus
                 per-component scores and weighted total.

  [DBG:SCORE]  — Per-side qualify check: score vs threshold,
                 trend confirm flag, and QUALIFY / skip verdict.

  [DBG:SIGNAL] — Always logged (no throttle) when an ENTER signal fires.

  [DBG:HEART]  — 5-second heartbeat: active markets, per-market SM state,
                 dailyPnl, consecLosses, cooldownLeft, halted flag.

New script
──────────
  npm run oneshot-debug  →  DRY_RUN=true ONESHOT_DEBUG=true node src/oneshot.js

Co-Authored-By: direkturcrypto <direkturcrypto.x@mail3.me>
2026-02-24 13:29:26 +07:00
direkturcrypto f074ca9ecb feat(oneshot): add Anti-Flip 5m microstructure execution engine
Introduces a complete, event-driven execution engine for 5-minute
Polymarket UP/DOWN markets, implementing the Anti-Flip strategy spec.

Architecture
────────────
• EventBus          — central pub/sub bus connecting all services
• MarketFeedService — discovers 5m/15m markets via Gamma API, polls
                      CLOB orderbooks every 200–500ms, emits snapshots
• FeatureEngine     — maintains a 15s rolling buffer per market and
                      computes midSlope6s, retrace3s, imbalance, spread,
                      depthTop3 for both UP and DOWN sides
• SignalEngine      — hard gate checks (TTE, spread, depth, stale) then
                      weighted score (imbalance 35%, slope 35%,
                      spread 20%, retrace 10%) + trend confirmation
• ExecutionEngine   — limit-marketable FOK buy, market-sell FOK exit,
                      GTC limit-sell for TP; dry-run short-circuits
• RiskEngine        — consecutive loss cooldown, daily USDC loss cap,
                      session halt; all via explicit canTrade() gate
• PositionEngine    — per-market position state, TP/adverse/slope/time
                      exit evaluation on every snapshot tick
• StateMachine      — explicit state graph with guarded transitions:
                      IDLE → SETUP_READY → ORDER_PENDING → POSITION_OPEN
                      → REDUCE_ONLY → IDLE | COOLDOWN | HALTED
• Telemetry         — structured JSONL logger (data/oneshot_telemetry.jsonl)
                      recording decisions, orders, exits, and transitions

Runtime sequence (per market, per tick)
────────────────────────────────────────
A  Ingest snapshot (MarketFeedService)
B  Build features — rolling slope, retrace, imbalance (FeatureEngine)
C  Hard gate check — TTE [25,120]s, spread ≤ 0.02, depth ≥ minTopSize
D  Score + trend confirm → emit ENTER_LONG / ENTER_SHORT / NO_TRADE
E  Submit FOK limit-marketable at bestAsk
F  Fill handling — full fill / partial (reduce if TTE ≤ 25s) / timeout
G  Position management — TP, adverse (2-tick), slope drop (4s), time exits
H  Risk enforcement — P&L accounting, cooldown, daily halt

New scripts
───────────
  npm run oneshot      — live trading  (DRY_RUN=false)
  npm run oneshot-sim  — simulation    (DRY_RUN=true)
  npm run oneshot-dev  — sim + nodemon

New .env variables
──────────────────
  ONESHOT_ASSETS, ONESHOT_DURATION, ONESHOT_POLL_INTERVAL_MS,
  ONESHOT_BASE_RISK_USDC, ONESHOT_TP_TICKS, ONESHOT_SCORE_THRESHOLD,
  ONESHOT_MIN_TOP_SIZE, ONESHOT_MAX_CONSEC_LOSSES,
  ONESHOT_COOLDOWN_ROUNDS, ONESHOT_DAILY_LOSS_CAP, ONESHOT_FILL_TIMEOUT_MS

Co-Authored-By: direkturcrypto <direkturcrypto.x@mail3.me>
2026-02-24 13:00:19 +07:00
direkturcrypto 526076fe6e feat: full project overhaul — market maker, sniper, WebSocket watcher, terminal UI
- Rename project to polymarket-terminal
- Add Market Maker bot (src/mm.js) with on-chain CTF split/merge/redeem via Gnosis Safe
- Add Orderbook Sniper bot (src/sniper.js) with multi-asset GTC low-price orders
- Add WebSocket watcher (src/services/wsWatcher.js) for real-time RTDS trade events
- Add terminal dashboard UI (src/ui/dashboard.js) using blessed
- Add CTF contract helpers (src/services/ctf.js) for splitPosition, mergePositions, redeemPositions
- Add mmDetector, mmExecutor, sniperDetector, sniperExecutor services
- Add simStats utility for dry-run P&L tracking
- Translate all Indonesian-language strings to professional English across all files
- Rewrite README.md in English with full setup guide, configuration reference, and architecture overview
- Rewrite AGENT.MD in English as comprehensive AI agent and developer reference
- Update package.json name, description, scripts, and keywords

Co-Authored-By: direkturcrypto <direkturcrypto.x@mail3.me>
2026-02-23 23:03:06 +07:00
direkturcrypto 7c7fad45f3 feat: initial polymarket copy trade tool
- Watcher: polls Data API for trader activity
- Executor: buy/sell with market orders + retry logic
- Position manager: JSON-based state tracking
- Auto-sell: limit orders at profit target
- Redeemer: check & redeem winning positions on-chain
- Config: env-based settings with validation
- DRY_RUN mode for safe testing
2026-02-22 15:38:13 +07:00