Commit Graph

52 Commits

Author SHA1 Message Date
direkturcrypto ae523c4f27 fix: breakevenFloor is not defined in adaptive CL
Replace undefined `breakevenFloor` variable with `minAdaptivePrice`
which is the actual breakeven floor calculated from
mmAdaptiveMinCombined - filledLegPrice.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 14:08:03 +07:00
direkturcrypto f0295818e2 fix: handle inline axios call pattern in patch script
The VPS fresh install uses `(0, axios_1.default)({ method, url: endpoint, ... })`
instead of a separate `config` variable. Add Format B regex to match this
inline pattern and wrap it in an IIFE that injects the proxy agent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 13:58:27 +07:00
direkturcrypto 85f19d0bd6 fix: use regex matching in patch script + add geoblock IP check
- Rewrite patch-clob-client.cjs to use regex instead of exact string
  matching, so it works regardless of code formatting differences
  between npm versions (fixes "Could not find request config line" on
  fresh VPS install)
- Add multiple fallback patterns (config regex → fallback regex →
  axios call injection) with debug output if all fail
- Add Polymarket geoblock API check (polymarket.com/api/geoblock)
  for both VPS direct IP and proxy IP at startup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 13:55:35 +07:00
direkturcrypto b4d6d648bd feat: auto-patch clob-client proxy support via postinstall script
The proxy patch in node_modules is lost on every fresh npm install.
This postinstall script automatically patches @polymarket/clob-client
http-helpers to inject HttpsProxyAgent for all polymarket.com requests.

Runs automatically after npm install — no manual patching needed on VPS deploy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 13:49:52 +07:00
direkturcrypto d82c5fd14e fix: add axios interceptor for proxy + dual proxy test (fetch + axios)
The CLOB client uses axios internally. Previous setup only set
axios.defaults which doesn't work if the CLOB client or axios
creates its own instance.

Fix: add axios request interceptor that forces proxy agent on every
request to polymarket.com domains.

Also add separate axios-based proxy test alongside fetch test to catch
cases where fetch works but axios doesn't route through proxy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 13:47:22 +07:00
direkturcrypto efe0a077e5 feat: add IP check and geoblock detection at startup
Shows both VPS direct IP and proxy IP so user can verify which IP
Polymarket sees. If CLOB returns 403 geoblock, shows clear error
with link to allowed regions instead of proceeding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 13:42:40 +07:00
direkturcrypto 3078ab9790 fix: tiered floor must start from MM_ADAPTIVE_MIN_COMBINED, not hardcoded $1.00
Was using breakevenFloor (1.00 - filledPrice) which ignored the user's
MM_ADAPTIVE_MIN_COMBINED setting. This caused sells below the configured
minimum (e.g. selling at 28c when min combined was 1.01).

Now: filled @ 70c, MM_ADAPTIVE_MIN_COMBINED=1.01 → floor starts at 31c
  Phase 1 (>180s): 31c
  Phase 2 (90-180s): 21c
  Phase 3 (30-90s): 11c
  Phase 4 (<30s): market sell

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 13:36:20 +07:00
direkturcrypto 6e3d484728 merge: defensive pivot, tiered CL, on-chain fill detection, proxy patch for MM 2026-03-27 13:27:50 +07:00
direkturcrypto 565f98571a fix: add proxy-patch import to mm-bot and mm entry points
Both sniper and dumper had proxy-patch.cjs imported but market maker
entry points (mm-bot.js, mm.js) were missing it, causing direct
connections without proxy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 13:26:23 +07:00
direkturcrypto b05fed9207 fix: add on-chain balance fallback for fill detection
CLOB API (client.getOrder) can be stale — order filled on-chain but API
still shows unfilled. This caused the bot to miss fills and incorrectly
enter defensive mode or keep monitoring dead orders.

Now isOrderFilled does:
1. Check CLOB API (existing) with retry
2. Fallback: check on-chain token balance via CTF contract
   If balance < 5% of original shares → treat as filled

Fixes issue where Polymarket UI shows filled but bot doesn't detect it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 13:24:33 +07:00
direkturcrypto ba250572b9 fix: cancel orders immediately when defensive timeout reached
Orders must be cancelled BEFORE the monitoring loop can detect fills,
otherwise the limit sell at 70c can still get filled at minute 3
and trigger adaptive CL instead of defensive pivot.

Now: timeout reached → cancel both orders → enter defensivePivot

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 05:25:11 +07:00
direkturcrypto 1da662313d fix: use mid price for initial adaptive CL sell instead of fixed breakeven floor
Check mid price first, place limit at market price (capped at mmSellPrice).
Only falls back to breakeven floor if mid price is below it.

Example: YES filled @ 70c, NO mid = 38c → sell @ 38c (not 30c breakeven)
This gives profit potential instead of always targeting breakeven.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 05:15:01 +07:00
direkturcrypto df09403f18 fix: change defensive pivot trigger from 30s to 45s before close
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 05:09:02 +07:00
direkturcrypto cc90176861 feat: implement tiered floor + standing order for adaptive CL (5m markets)
Adaptive CL now uses progressive floor lowering for 5-minute markets:

  Phase 1 (>180s left): breakeven floor (e.g. 40c for 60c fill)
  Phase 2 (90-180s):    floor - 10c (accept small loss to escape)
  Phase 3 (30-90s):     floor - 20c + emergency cut if price < 10c
  Phase 4 (<30s):       force market sell

Key improvements:
- Standing order: immediately place limit at breakeven floor so brief
  bounces get caught (don't wait for polling to detect price >= floor)
- Emergency cut: market sell immediately if price < 10c in phase 3
  (market is decisive, bounce unlikely)
- Non-5m markets still use the existing fixed floor (mmAdaptiveMinCombined)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 05:01:38 +07:00
direkturcrypto 8a5962d0a5 fix: measure defensive timeout from market open time, not bot entry time
Market start = endTime - 5 minutes, so defensive triggers at exactly
120s after the market opens regardless of when the bot entered.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 04:54:42 +07:00
direkturcrypto e19278c665 feat: add defensive pivot mode for 5m markets when neither side fills
When neither limit sell fills within MM_DEFENSIVE_TIMEOUT (default 120s),
the bot enters defensive mode:
- Cancel both limit sells
- Wait until 30s before market close
- If worst side price < MM_DEFENSIVE_WORST_THRESHOLD (default 10c):
  market sell worst side, hold best side for resolution (best ≈ 90c+)
- If worst side ≥ threshold: merge back to USDC (safe $0 P&L)

Only active for 5-minute markets (MM_DURATION=5m).

New config:
  MM_DEFENSIVE_ENABLED (default true)
  MM_DEFENSIVE_TIMEOUT (default 120s)
  MM_DEFENSIVE_WORST_THRESHOLD (default 0.10)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 04:43:56 +07:00
direkturcrypto a73cf22aa5 merge: mm-bot improvements - retry fill detection, parallel limit sells, priority safe queue 2026-03-27 04:18:55 +07:00
direkturcrypto 0da77f78fd fix: improve mm-bot reliability - retry fills, parallel sells, smart small remainder handling
- isOrderFilled: retry 2x with logging instead of silent error swallow
- Place YES/NO limit sells in parallel via Promise.all
- Market sell immediately when remaining shares < CLOB minimum (5 shares)
- Re-check on-chain balance before every limit/market sell in adaptive CL
- Separate redeemer Safe queue priority from strategy (split/merge) to reduce cross-market delays
- Cache USDC and exchange approvals in-memory to skip redundant on-chain reads

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 04:18:51 +07:00
direkturcrypto d89b8ff54d fix: correct win detection by mapping tokenId→outcome instead of array index
Problem: redeemer used array index to match balances to payoutFractions,
but Data API token order ≠ on-chain outcome index. This caused false wins
(shares on losing side counted as winning).

Fix:
- Store yesTokenId/noTokenId per conditionId in sniperExecutor
- Match tokens by ID (yes=outcome0, no=outcome1) instead of array position
- Only process positions tracked by sniper (skip MM/other positions)
- Inject token lookup via setSniperConditionLookup to avoid circular imports

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 13:59:48 +07:00
direkturcrypto a8f28c961b feat: add time-based multiplier sizing, pause-after-win, and outcome-based win detection
- Time-based bet sizing multiplier (SNIPER_MULTIPLIERS, UTC+8 windows)
- Pause N rounds per asset after win (SNIPER_PAUSE_ROUNDS_AFTER_WIN)
- Win detection via payoutNumerators outcome check instead of redeem value threshold

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 12:53:48 +07:00
direkturcrypto 9e78fb1bfe fix: update sniper logging for 3-tier config
- Fix sniperDetector.js to use sniperTierPrices instead of old config
- Fix sniper-tui.js display for 3-tier pricing
- Remove references to deleted sniperPrice/sniperShares vars

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 14:59:14 +07:00
direkturcrypto c524ed92ef feat: implement 3-tier sniper strategy with weighted sizing
- Replace single price/shares with 3-tier system (3c/2c/1c)
- Tier allocation: 20%/30%/50% (high→low price)
- Min 5 shares per tier enforced
- Update .env.example with new config vars
- Update README.md with strategy documentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 14:52:37 +07:00
direkturcrypto 00235dc850 fix: inject proxy agent into @polymarket/clob-client
Add proxy support directly to clob-client http-helpers:
- Import https-proxy-agent
- Add getProxyAgent() helper
- Inject agent into axios config for polymarket.com endpoints

This ensures all CLOB API calls route through the proxy.
2026-03-01 22:36:23 +07:00
direkturcrypto f5c777ec0c simplify: use HttpsProxyAgent directly for proxy routing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:33:45 +07:00
direkturcrypto 048d4433c2 fix: implement CONNECT tunnel for proxy routing
Full HTTPS CONNECT tunnel implementation that:
- Routes only Polymarket domains through proxy
- Uses HTTP CONNECT method for HTTPS tunneling
- Leaves all other traffic (RPC, etc.) untouched

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:27:18 +07:00
direkturcrypto b770f831db fix: simplify proxy patch to use HttpsProxyAgent globally
Set https.globalAgent and override https.request to use proxy
for all Polymarket domains. Ensures @polymarket/clob-client
axios calls route through proxy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:22:20 +07:00
direkturcrypto 368a3f8da5 fix: ensure proxy is used by @polymarket/clob-client
Add proxy-patch.cjs that patches https.request BEFORE axios is loaded.
This forces all Polymarket API calls through the proxy, including
internal axios instances used by @polymarket/clob-client.

Import patch as very first thing in sniper.js and sniper-tui.js.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:18:06 +07:00
direkturcrypto b2f8447d0b perf: cache sniper losses to speed up redeem checks
- Add _skippedLosses Set to cache confirmed losses (skip future checks)
- Add counters for skipped categories (unresolved, losses, no balance)
- Losses only checked once, subsequent cycles skip immediately
- Add summary logging for better visibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 22:01:56 +07:00
direkturcrypto c5b169b47e feat: sniper redeem - winners only, high gas, no startup check
- Remove startup redeem check (only runs on interval)
- Only redeem WINNING positions (skip losses for manual cleanup)
- Increase gas fees: 1.5x estimate, min 50 Gwei priority fee
- Add tx hash logging for successful redeems

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:42:26 +07:00
direkturcrypto 374e943cc2 feat: redeem logging improvements
- Add transaction hash to redeem success log
- Continue to next position when one fails (don't get stuck)
- Add clearer logging for retry behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:30:19 +07:00
direkturcrypto d24c5f2e81 feat: add undici dependency + dynamic gas multiplier for tx retry
- Install undici package for fetch proxy support
- Add gas multiplier on retry to handle "replacement transaction underpriced"
  - Attempt 1: 1x gas
  - Attempt 2: 2x gas
  - Attempt 3: 4x gas

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 21:23:37 +07:00
direkturcrypto 56f59eabdf fix: sniper redeem matches MM redeemer — redeem all, skip failed conditions
- Redeem ALL resolved positions (not just winners) — burns loser tokens
  to clean them from Data API, prevents buildup that slows down cycles
- Add _failedConditions set: skip conditionIds that revert on-chain
  so they don't retry every 60 seconds
- Same code path as the working MM redeemer (redeemMMPositions)
- Log win/loss separately for profit tracking
2026-03-01 21:10:12 +07:00
direkturcrypto 4af99a78f5 fix: simplify sniper redeem — remove MultiSend and gas estimation timeout
- Remove MultiSend batching (was causing hangs)
- Revert execSafeCall to original behavior (no gas estimation timeout)
- Keep win-only filter: skip positions with $0 payout
- Simple 1-by-1 redeem identical to the working MM redeemer
- Fix proxy: use undici ProxyAgent with dispatcher for native fetch
- Move schedule to .env: SNIPER_SCHEDULE_BTC=19:40-22:40,03:40-06:10
2026-03-01 20:55:06 +07:00
direkturcrypto 0dc8024ceb fix: proxy uses undici ProxyAgent for fetch, schedule moved to .env, verbose redeem logging
- Fix proxy: native fetch needs undici ProxyAgent with dispatcher option
  (https-proxy-agent only works with axios, not Node's built-in fetch)
- Move schedule from hardcoded to .env: SNIPER_SCHEDULE_BTC=19:40-22:40,03:40-06:10
- Add verbose logging to redeemSniperPositions for debugging
- Config dynamically reads all SNIPER_SCHEDULE_* env vars
2026-03-01 19:07:57 +07:00
direkturcrypto f31f257da5 feat: sniper bot updates — TUI separation, session scheduling, proxy support, win-only redeem
- Separate TUI from sniper: npm run sniper (console), npm run sniper-tui (TUI)
- Add trading session scheduling per asset (BTC/ETH/SOL/XRP) in UTC+8
- Add PROXY_URL support for Polymarket CLOB/Gamma/Data APIs (not Polygon RPC)
- Create redeemSniperPositions: win-only filter, bulk MultiSend batching
- Add gas estimation with 10s timeout in execSafeCall
- Replace native fetch with proxyFetch in all Polymarket API services
2026-03-01 16:24:25 +07:00
direkturcrypto f4490c12ac refactor: split PM2 ecosystem config into pm2/ folder + update README
- ecosystem.config.cjs removed from root
- pm2/copy.config.cjs — copy trade bot config
- pm2/mm.config.cjs   — market maker bot config
  Both use __dirname-relative paths so they work regardless of cwd.

README updated:
- Added PM2/VPS section with per-bot start/log/management commands
- Added plain log mode section (bot / mm-bot scripts)
- Updated project structure tree with new files
- Added MIN_MARKET_TIME_LEFT and GTC_FALLBACK_TIMEOUT to config table
- Updated copy trade flow diagram

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 02:11:20 +07:00
direkturcrypto 5ea7f3d2b0 feat: add PM2-compatible MM entry point (src/mm-bot.js)
- src/mm-bot.js: same MM logic as mm.js but no blessed TUI; writes plain
  text to stdout so pm2 logs works on VPS; status printed every 60s
- ecosystem.config.cjs: added polymarket-mm app with separate log files
  (logs/mm-out.log, logs/mm-error.log); both copy and MM bots now managed
- package.json: added mm-bot / mm-bot-sim / mm-bot-dev scripts

Usage:
  pm2 start ecosystem.config.cjs --only polymarket-mm
  pm2 start ecosystem.config.cjs --only polymarket-mm --env sim
  pm2 logs polymarket-mm

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 02:05:06 +07:00
direkturcrypto a6f2e148a6 remove logs data oneshot 2026-02-26 01:36:27 +07:00
direkturcrypto 363d74ff2d Merge branch 'feat/mm-adaptive-cl' 2026-02-26 01:35:04 +07:00
direkturcrypto 0148b0bbef feat: GTC fallback when FAK finds no liquidity (next market copy trades)
When copying a trader who buys into the next periodic market before sellers
exist, FAK returns 0 fill each attempt. After exhausting FAK retries, the
bot now falls back to a GTC limit order at price*1.02 and polls getOrder()
every 3s until filled or GTC_FALLBACK_TIMEOUT (default 60s) expires.

If the GTC times out it is cancelled. GTC_FALLBACK_TIMEOUT=0 disables the
fallback entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 04:30:12 +07:00
direkturcrypto 365dc4e164 fix: serialize concurrent buys per market + always verify on-chain payout before redeem
executeBuy (race condition):
- Multiple WebSocket events for the same market can arrive concurrently.
  All calls saw no existing position and all proceeded to buy → 3x fills.
- Added _buyQueue (Map<conditionId, Promise>) that chains each buy for
  the same market after the previous one. Second call now sees the filled
  position and respects maxPositionSize.

redeemer (gas estimation error):
- Gamma API can return resolved=true before payoutDenominator is written
  on-chain. Calling redeemPositions when payoutDenominator==0 causes the
  contract to revert → UNPREDICTABLE_GAS_LIMIT from ethers.js.
- Now always verify on-chain payout after the API check. If on-chain
  payout not set yet, skip and retry next interval.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 04:22:09 +07:00
direkturcrypto 526fc7a057 feat: add PM2-compatible entry point (src/bot.js) and ecosystem config
- src/bot.js: same logic as index.js but no blessed TUI; writes plain
  text to stdout so pm2 logs works cleanly on VPS
- ecosystem.config.cjs: pm2 config with live/sim envs, log files at
  logs/out.log + logs/error.log, auto-restart policy
- logs/.gitkeep: track the logs/ dir (*.log files are gitignored)
- package.json: added bot / bot-sim / bot-dev scripts

Usage on VPS:
  pm2 start ecosystem.config.cjs            # live
  pm2 start ecosystem.config.cjs --env sim  # simulation
  pm2 logs polymarket-copy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 04:17:16 +07:00
direkturcrypto 0650cb9ef7 fix: correct Gamma API field names in getMarketOptions (all camelCase)
The Gamma API returns camelCase field names but the code used snake_case,
causing all lookups to return undefined:
  - end_date_iso  → endDate  (full ISO datetime, not date-only endDateIso)
  - game_start_time → removed (field doesn't exist)
  - minimum_tick_size → orderPriceMinTickSize
  - neg_risk      → negRisk
  - condition_id  → conditionId
  - accepting_orders → acceptingOrders

Because endDate was always null, the MIN_MARKET_TIME_LEFT expiry guard
was silently skipped on every buy — allowing buys into expired markets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:49:38 +07:00
direkturcrypto 59295b08cb fix: remove duplicate 'client' declaration in executeSell
The const client = getClient() was declared twice in the same function scope
(line 318 for cancel-orders block, line 371 for the sell loop), causing a
SyntaxError at startup. Removed the redundant second declaration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:44:32 +07:00
direkturcrypto b52892172e fix: enforce $1 USDC minimum per FAK buy order (Polymarket hard minimum)
Polymarket rejects market orders below $1 USDC. Added CLOB_MIN_ORDER_USDC=1
constant and applied Math.max(config.minTradeSize, CLOB_MIN_ORDER_USDC) at
both the loop entry check and post-fill remainder check so sub-$1 remainders
are silently skipped instead of causing API errors.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:43:38 +07:00
direkturcrypto 097148c501 fix: cancel all open orders before sell to release CLOB locked balance
Root cause: auto-sell GTC orders lock all shares in the CLOB's internal
ledger. When executeSell runs, it tried to cancel only position.sellOrderId
but this can fail silently, leaving tokens locked. The CLOB then rejects
the new sell with "not enough balance or allowance" because the balance
is committed to the existing GTC order.

Fix:
- Fetch all open orders for the specific tokenId via client.getOpenOrders()
- Cancel all of them with Promise.allSettled (non-fatal per order)
- Wait 600ms after cancellation so the CLOB updates its locked-balance
  ledger before we place the new sell
- Fallback: if getOpenOrders fails, still attempt to cancel by
  position.sellOrderId (previous behaviour) then wait 600ms
- Use correct cancelOrder({ orderID }) object form throughout

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:38:38 +07:00
direkturcrypto ec2c948a00 fix: redeemer executes via Gnosis Safe proxy wallet (same as MM bot)
redeemer.js was calling redeemPositions() directly from the EOA private
key wallet. This is wrong for two reasons:
1. Conditional tokens are held in config.proxyWallet (Gnosis Safe), not
   in the EOA — so the EOA has nothing to redeem
2. No maxPriorityFeePerGas was set, causing Polygon's "gas tip below
   minimum" error (1.5 Gwei sent, 25 Gwei required)

Fix: replace the direct contract call with execSafeCall() (exported from
ctf.js), which is identical to the MM bot's redemption pattern:
- encodes calldata and executes via Safe.execTransaction()
- signed by the EOA, but msg.sender on-chain = proxy wallet
- enforces 30 Gwei minimum priority fee for Polygon
- retries up to 3x on transient RPC errors

Also remove the now-unused NEG_RISK_CTF_ADDRESS local constant
(CTF_ADDRESS and USDC_ADDRESS imported from ctf.js).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:32:01 +07:00
direkturcrypto e82c67824c fix: resolve 'not enough balance or allowance' on sell orders
Two root causes fixed:

1. Missing ERC-1155 setApprovalForAll for copy-trade flow
   - executeBuy: call ensureExchangeApproval() after a successful fill so
     the CTF Exchange is authorised to move tokens before the next sell
   - executeSell: call ensureExchangeApproval() before every sell attempt;
     the function is idempotent (checks isApprovedForAll first, only sends
     a Safe tx if approval is actually missing)

2. Stored shares can exceed real on-chain balance (fee deductions, float
   drift across partial FAK fills)
   - executeSell: query ctf.balanceOf(proxyWallet, tokenId) before selling
   - If on-chain balance is 0 → position is already gone, remove it and skip
   - If on-chain balance < stored shares → adjust down and log the delta
   - Round sell amount to 4 decimal places to avoid sub-unit precision errors

Also add getOnChainTokenBalance() helper using a minimal CTF ABI slice.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:29:10 +07:00
direkturcrypto 69d8d1401d fix: market expiry guard, FAK orders, 2% slippage, sanitize CLOB logs
Market expiry guard (executor.js):
- getMarketOptions() now returns endDateIso, active, acceptingOrders
  from Gamma API response
- executeBuy() skips immediately if market is closed / not accepting orders
- executeBuy() skips if market closes within MIN_MARKET_TIME_LEFT seconds
  (default 300 s = 5 min), logging exact time remaining
- Add MIN_MARKET_TIME_LEFT to config and .env.example

FAK + 2% slippage (executor.js):
- Replace OrderType.FOK with OrderType.FAK for both BUY and SELL
  market orders — eliminates "FOK fully filled or killed" failures
- Reduce slippage from 5% to 2% (price * 1.02 / price * 0.98)
- Fix zero-fill detection: FAK success with 0 shares logs "no liquidity"
  and retries instead of recording a phantom fill

CLOB log sanitization (logger.js + index.js):
- Add sanitizeClobMessage(): strips axios config object (auth headers)
  from [CLOB Client] dumps, keeps only HTTP status + error string
- Add interceptConsole(): overrides console.error/warn globally
- Call interceptConsole() at startup in index.js

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 03:18:57 +07:00
direkturcrypto d3e5567c97 feat: MM adaptive cut-loss with profit floor
When one leg fills, instead of immediately market-selling the unfilled
leg, enter a continuous monitoring loop that:
- Places a limit sell only when price >= minAdaptivePrice floor
  (floor = mmAdaptiveMinCombined - filledLegPrice, default combined 1.20)
- Chases price upward (>2% improvement → re-place limit higher)
- Cancels limit on dip >5% or below floor, then waits for recovery
- Market-sells only as last resort when CL time is reached

New config: MM_ADAPTIVE_CL (toggle), MM_ADAPTIVE_MIN_COMBINED (floor),
MM_ADAPTIVE_MONITOR_SEC (poll interval). Legacy immediate market-sell
path preserved when MM_ADAPTIVE_CL=false.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-25 02:34:45 +07:00