Skill: synththoughts/grid-trading (version 1.5.3)

--- name: grid-trading description: "Dynamic grid trading strategy for any token pair on EVM L2 chains via OKX DEX API. Features asymmetric grid steps (buy-dense/sell-wide in bullish, reverse in bearish), multi-timeframe trend analysis, trend-adaptive grid sizing, ATR-based volatility, sell trailing optimization, and HODL Alpha tracking. Covers grid modes (arithmetic/geometric), asymmetric buy/sell grid spacing, position sizing strategies (equal/martingale/anti-martingale/pyramid/trend-adaptive), comprehensive risk controls (stop-loss, take-profit, drawdown protection, circuit breakers), trade execution via OKX DEX aggregator, PnL calculation, and Discord notification. Use when creating, modifying, debugging, or tuning a grid trading bot." license: Apache-2.0 metadata: author: SynthThoughts version: "1.5.0" pattern: "pipeline, tool-wrapper" steps: "5" openclaw: requires: env: - OKX_API_KEY - OKX_SECRET_KEY - OKX_PASSPHRASE - WALLET_ADDR optional_env: - ONCHAINOS_ACCOUNT_ID - DISCORD_CHANNEL_ID - DISCORD_BOT_TOKEN bins: - onchainos - python3 primaryEnv: OKX_API_KEY entrypoint: references/eth_grid.py skills: - okx-dex-swap - okx-dex-market - okx-agentic-wallet - okx-onchain-gateway os: - darwin - linux --- # Dynamic Grid Trading Strategy v1.0 Cron-driven grid bot for EVM L2 chains via `onchainos` CLI. Features asymmetric grid steps — different spacing for buy vs sell sides based on trend direction, trend intelligence with multi-timeframe analysis, sell optimization, and HODL Alpha tracking. Every tick: fetch price → MTF analysis → compute grid level → trend-adaptive decision → execute swap → report to Discord. ## Asymmetric Grid Steps Buy and sell sides use different spacing based on trend direction: | Trend | Buy Side | Sell Side | Effect | |-------|----------|-----------|--------| | Bullish | Tighter (accumulate fast) | Wider (hold longer) | Buy dense, sell sparse → captures uptrend | | Bearish | Wider (wait for dip) | Tighter (exit fast) | Sell dense, buy sparse → reduces downside exposure | | Neutral/Weak | Symmetric | Symmetric | Symmetric (default) | Key config: `ASYM_FACTOR=0.4` (max asymmetry ratio). Asymmetry scales with trend strength and only activates when `strength > 0.3`. New grid dict fields: `buy_step`, `sell_step` (backward-compatible `step` = average). Level prices are now non-uniform: below center spaced by `buy_step`, above center by `sell_step`. ## Architecture ``` Cron (5min) → Python script → onchainos CLI → OKX Web3 API → Chain ↓ ↓ grid_state.json Wallet (TEE signing) ↓ ┌─────────────┐ │ MTF Analysis │ ← price_history (288 bars = 24h) │ K-line ATR │ ← okx-dex-market kline (1H × 24) └──────┬──────┘ ↓ Trend-Adaptive Grid Decision ↓ Discord embed (notification) ``` **OKX Skill Dependencies** (command syntax defined in each skill, do not duplicate here): - `okx-dex-swap` — quote, approve, swap execution - `okx-dex-market` — K-line / OHLC data - `okx-agentic-wallet` — wallet switch, balance, contract-call (TEE signing) - `okx-onchain-gateway` — transaction simulation ## Pipeline: Execution Steps **CRITICAL RULE**: Steps MUST execute in order. Do NOT skip steps or proceed past a gate that has not been satisfied. ### Step 1: Data Acquisition **Actions**: 1. Fetch ETH price via `okx-dex-swap` (swap quote) 2. Fetch on-chain balances via `okx-agentic-wallet` (wallet balance) 3. Update `price_history` (append, cap at 288 = 24h @ 5min) 4. Detect external deposits/withdrawals (unexplained balance changes > $100) **Gate** (ALL must pass): - [ ] Price is non-null and > 0 - [ ] At least one balance (ETH or USDC) > 0 - [ ] Circuit breaker not active (`consecutive_errors < 5`) - [ ] Stop not triggered (`stop_triggered == null`) ### Step 2: Multi-Timeframe Analysis **Actions**: 1. Compute short EMA (25min / 5-bar), medium EMA (1h / 12-bar), long EMA (4h / 48-bar) 2. Detect EMA alignment → trend direction (bullish / bearish / neutral) + strength (0-1) 3. Detect 8h structure: split into 4 segments, check higher-highs/higher-lows → uptrend / downtrend / ranging 4. Compute 1h and 4h momentum 5. Fetch K-line data (1H candles, 24 bars) → compute ATR-based volatility (hourly cache) **Output**: `mtf` dict, `kline_vol` float ```python def analyze_multi_timeframe(history, price) -> dict: # Returns {trend, strength, momentum_1h, momentum_4h, structure, # ema_short, ema_medium, ema_long} # EMA alignment: short > medium > long → bullish # Structure: 8h window split into 4 segments # higher-highs + higher-lows → "uptrend" # lower-highs + lower-lows → "downtrend" # else → "ranging" ``` **Gate**: - [ ] `mtf` dict has `trend` and `strength` fields (graceful fallback to neutral if insufficient history) ### Step 3: Grid Decision **Actions**: 1. Calculate dynamic grid with trend-adaptive volatility multiplier: - Grid center = EMA(20) on **1H kline** (20-hour EMA, fetched via `okx-dex-market` kline). Falls back to 5min tick history if kline unavailable. - Base: `VOLATILITY_MULTIPLIER_BASE=2.0` - In trend (strength > 0.3): blend toward `VOLATILITY_MULTIPLIER_TREND=3.0` - Wider grid in trends → fewer trades → more holding 2. Check recalibration triggers (breakout / vol shift / age) 3. Map price → grid level 4. If level changed: determine direction (BUY if level dropped, SELL if rose) 5. Safety checks: cooldown, trend-adaptive position limits, repeat guard, consecutive limit 6. Sell optimization: if SELL in strong uptrend, delay via `_should_delay_sell()` 7. Calculate trade size with trend-adaptive sizing **Gate**: - [ ] Grid is valid (step > 0, levels > 0) - [ ] If trade needed: amount >= `MIN_TRADE_USD` ($5) - [ ] All safety checks passed (cooldown, position limits, etc.) ### Step 4: Execution **Actions**: 1. Get swap quote + tx data from OKX DEX aggregator 2. Pre-simulate via `okx-onchain-gateway` (diagnostic, non-blocking) 3. For BUY: ensure USDC approval via `okx-dex-swap` 4. Sign + broadcast via `okx-agentic-wallet` contract-call (TEE signing) 5. On failure: 1 auto-retry with 3s delay and fresh quote 6. Record trade in state, update grid level ONLY on success **Gate**: - [ ] Transaction hash received, or failure recorded with `retriable` flag - [ ] Level updated only on success; NOT on failure or skip ### Step 5: Notification & Tracking **Actions**: 1. Calculate PnL (USD + ETH denominated) 2. Calculate HODL Alpha: `current_portfolio - (initial_eth × current_price)` 3. Build structured JSON output for AI agent parsing 4. Send Discord embed (green=SELL, blue=BUY, grey=no-trade, red=stop) 5. Emit `---JSON---` block with enriched fields **Output**: Discord notification + structured JSON ## Tunable Parameters ### Grid Structure | Parameter | Default | Description | |---|---|---| | `GRID_LEVELS` | `6` | Number of grid levels. More = finer, more trades | | `GRID_TYPE` | `"arithmetic"` | `"arithmetic"` (fixed $ step) or `"geometric"` (fixed % step) | | `EMA_PERIOD` | `20` | EMA lookback for grid center (applied to 1H kline = 20h) | | `VOLATILITY_MULTIPLIER_BASE` | `2.0` | Base grid width = multiplier x stddev | | `VOLATILITY_MULTIPLIER_TREND` | `3.0` | Wider grid in trending markets | | `ASYM_FACTOR` | `0.4` | Max buy/sell asymmetry ratio. 0=symmetric, 1=fully asymmetric | | `GRID_RECALIBRATE_HOURS` | `12` | Max hours before forced recalibration | ### Multi-Timeframe | Parameter | Default | Description | |---|---|---| | `MTF_SHORT_PERIOD` | `5` | 5-bar EMA (25min @ 5min tick) | | `MTF_MEDIUM_PERIOD` | `12` | 12-bar EMA (1h @ 5min tick) | | `MTF_LONG_PERIOD` | `48` | 48-bar EMA (4h @ 5min tick) | | `MTF_STRUCTURE_PERIOD` | `96` | 96-bar (8h @ 5min tick) for structure detection | ### Sell Optimization | Parameter | Default | Description | |---|---|---| | `SELL_TRAIL_TICKS` | `2` | Wait 2 ticks (10min) of price stability before selling in uptrend | | `SELL_MOMENTUM_THRESHOLD` | `0.005` | Skip sell if 1h momentum > 0.5% in strong uptrend | ### Grid Modes **Arithmetic (等差网格)**: Each level is a fixed USD distance apart. Good for narrow ranges. ``` levels = [center - N*step, ..., center, ..., center + N*step] ``` **Geometric (等比网格)**: Each level is a fixed percentage apart. Better for wide ranges because step size scales with price. The ratio is derived from the arithmetic step: `ratio = 1 + (step / center)`. ```python # In calc_dynamic_grid(), when GRID_TYPE == "geometric": ratio = 1 + (step / center) level_prices = [center * (ratio ** (i - half)) for i in range(GRID_LEVELS + 1)] ``` Both modes store `level_prices` in the grid dict for unified level lookup via `bisect_right`. Asymmetric spacing uses levels below center spaced by `buy_step`, levels above by `sell_step`. The `_build_level_prices()` helper handles both symmetric and asymmetric construction. **Choosing a mode:** | Market | Recommended | Why | |---|---|---| | Tight range ($1900-$2100) | Arithmetic | Even spacing, predictable profit per grid | | Wide range ($1500-$3000) | Geometric | Steps scale with price, avoids crowding at low end | | High volatility | Geometric | Naturally wider steps at higher prices | | Stablecoin pairs | Arithmetic | Fixed small steps (0.1-0.5%) | ### Adaptive Step Sizing Step scales with real-time volatility, modulated by trend strength. Splits into directional buy/sell steps: ``` vol_mult = VOLATILITY_MULTIPLIER_BASE (2.0) if trend_strength > 0.3: vol_mult = blend(BASE, TREND, strength) (up to 3.0) # Asymmetric steps based on trend direction asym = ASYM_FACTOR × strength (if strength > 0.3, else 0) if bullish: buy_mult = vol_mult × (1 - asym) # tighter buy sell_mult = vol_mult × (1 + asym) # wider sell elif bearish: buy_mult = vol_mult × (1 + asym) # wider buy sell_mult = vol_mult × (1 - asym) # tighter sell buy_step = (buy_mult × ATR) / (GRID_LEVELS / 2) sell_step = (sell_mult × ATR) / (GRID_LEVELS / 2) # Both clamped to [price × STEP_MIN_PCT, price × STEP_MAX_PCT], floor $5 step = (buy_step + sell_step) / 2 # backward-compatible average ``` | Parameter | Default | Description | |---|---|---| | `STEP_MIN_PCT` | `0.010` | Step floor as fraction of price (1.0%) | | `STEP_MAX_PCT` | `0.060` | Step cap as fraction of price (6%) | | `VOL_RECALIBRATE_RATIO` | `0.3` | Recalibrate if kline ATR shifts >30% from grid's stored ATR | **Recalibration triggers (asymmetric):** 1. **Downside breakout**: Price < grid lower - `buy_step` → recalibrate **immediately** (buying dips is grid's edge) 2. **Upside breakout**: Price > grid upper + `sell_step` → require **N consecutive ticks** confirmation before recalibrating (anti-chase) 3. Grid age exceeds `GRID_RECALIBRATE_HOURS` 4. Current kline ATR deviates >30% from grid's stored ATR | Parameter | Default | Description | |---|---|---| | `UPSIDE_CONFIRM_TICKS` | `6` | Ticks (30min @ 5min interval) price must hold above grid before upside recalibration | | `MAX_CENTER_SHIFT_PCT` | `0.03` | Max 3% grid center shift per recalibration (prevents chasing spikes) | **Anti-chase mechanism:** - Upside breakout counter resets if price returns to grid range before confirmation - Even after confirmation, center shifts are capped to `MAX_CENTER_SHIFT_PCT` per recalibration - Multiple recalibrations can gradually track a true trend, but a single spike cannot drag the grid ### Position Sizing Strategies Controls how much to trade at each grid level. | Strategy | Description | |---|---| | `"equal"` | Every grid level trades the same amount | | `"martingale"` | BUY more at lower levels, SELL more at higher levels | | `"anti_martingale"` | Reduces exposure as price moves further from center | | `"pyramid"` | Largest position at grid center, tapering toward edges | | `"trend_adaptive"` | **(default)** In bullish: buy more + sell less. In bearish: sell more + buy less | ```python def _calc_sizing_multiplier(level, grid_levels, direction, mtf=None): base_mult = 1.0 if SIZING_STRATEGY == "trend_adaptive" and mtf: trend = mtf.get("trend", "neutral") strength = mtf.get("strength", 0) if trend == "bullish": if direction == "BUY": base_mult = 1.0 + strength * (MAX - 1.0) # buy aggressively else: base_mult = 1.0 - strength * (1.0 - MIN) # sell conservatively elif trend == "bearish": # opposite ... return clamp(base_mult, SIZING_MULTIPLIER_MIN, SIZING_MULTIPLIER_MAX) ``` ### Trade Execution | Parameter | Default | Description | |---|---|---| | `MAX_TRADE_PCT` | `0.12` | Max 12% of portfolio per trade (before sizing multiplier) | | `MIN_TRADE_USD` | `5.0` | Minimum trade size in USD | | `SLIPPAGE_PCT` | `1` | Slippage tolerance for DEX swap | | `GAS_RESERVE` | `0.003` | Native token reserved for gas | ### Risk Controls #### Basic Controls | Parameter | Default | Description | |---|---|---| | `MIN_TRADE_INTERVAL` | `1800` | 30min cooldown between same-direction trades | | `MAX_SAME_DIR_TRADES` | `3` | Max consecutive same-direction trades | | `MAX_CONSECUTIVE_ERRORS` | `5` | Circuit breaker threshold | | `COOLDOWN_AFTER_ERRORS` | `3600` | Cooldown after circuit breaker trips | | `POSITION_MAX_PCT_DEFAULT` | `70` | Block BUY when ETH > this % | | `POSITION_MIN_PCT_DEFAULT` | `30` | Block SELL when ETH < this % | | `POSITION_MAX_PCT_BULLISH` | `80` | Allow more ETH in bullish trend | | `POSITION_MIN_PCT_BEARISH` | `25` | Allow less ETH in bearish trend | Additional safety guards: - **Rapid drop protection**: skip BUY if price dropped >2% in last 30min (6 ticks) - **Consecutive same-direction reset**: limit resets if grid was recalibrated or >1h since last trade - **Anti-repeat**: skip if same direction + same level boundary as last trade - **Trend-adaptive position limits**: limits shift based on trend direction and strength #### Stop-Loss, Trailing Stop & Take-Profit Three protection mechanisms. When triggered, trading halts and a red Discord alert is sent. Use `resume-trading` command to clear. | Parameter | Default | Description | |---|---|---| | `STOP_LOSS_PCT` | `0.15` | Stop if portfolio drops 15% below cost basis | | `TRAILING_STOP_PCT` | `0.10` | Stop if portfolio drops 10% from peak | ```python def _check_stop_conditions(state, total_usd, price): cost_basis = initial + deposits pnl_pct = (total_usd - cost_basis) / cost_basis peak = max(stats["portfolio_peak_usd"], total_usd) # Check: stop_loss, trailing_stop, take_profit ``` #### Risk Control Flow in tick() ``` 1. If stop_triggered is set → log + Discord red alert + refuse trading + return 2. Check _check_stop_conditions → if triggered, set stop_triggered + alert + return 3. Multi-timeframe analysis → get trend context 4. Normal grid logic (cooldown, trend-adaptive position limits, etc.) 5. If SELL in strong uptrend → _should_delay_sell() check ``` ## Core Algorithm ``` 1. Fetch token price 2. Read on-chain balances (ETH + USDC) 3. Multi-timeframe analysis → trend/strength/momentum/structure 4. Fetch K-line ATR volatility (hourly cache) 5. Check if grid needs recalibration (breakout / vol shift / age) → calc_dynamic_grid() uses trend-adaptive volatility multiplier → asymmetric buy_step/sell_step based on trend direction 6. Map price → grid level 7. If level changed: a. Direction: BUY if level dropped, SELL if rose b. If SELL in strong uptrend → delay check (trailing + momentum protection) c. Safety checks (cooldown, trend-adaptive position limits, repeat guard, consecutive limit) d. Calculate trade size (trend-adaptive sizing) e. Execute swap via DEX aggregator f. Record trade, update level ONLY on success 8. Calculate HODL Alpha 9. Report status (JSON + Discord) ``` ## Grid Calculation ```python def calc_dynamic_grid(price, price_history, mtf=None): center = EMA(1H_kline, EMA_PERIOD) # 20-hour EMA on 1H candles atr = calc_kline_volatility(candles) # Trend-adaptive multiplier vol_mult = VOLATILITY_MULTIPLIER_BASE # 2.0 if mtf and mtf["strength"] > 0.3: vol_mult = blend(BASE=2.0, TREND=3.0, factor=strength) # Asymmetric buy/sell multipliers asym = ASYM_FACTOR * strength if strength > 0.3 else 0 if bullish: buy_mult = vol_mult * (1 - asym) # tighter buy grid sell_mult = vol_mult * (1 + asym) # wider sell grid elif bearish: buy_mult = vol_mult * (1 + asym) # wider buy grid sell_mult = vol_mult * (1 - asym) # tighter sell grid buy_step = clamp((buy_mult * atr) / half, floor, ceil) sell_step = clamp((sell_mult * atr) / half, floor, ceil) # Build asymmetric level_prices via _build_level_prices() # Below center: spaced by buy_step; Above center: spaced by sell_step level_prices = _build_level_prices(center, buy_step, sell_step, half, grid_type) return {center, step, buy_step, sell_step, levels, range, vol_pct, type, level_prices} ``` **Examples** (at price $2000, ATR=$50): | Trend | Strength | buy_step | sell_step | Buy Range | Sell Range | Behavior | |---|---|---|---|---|---|---| | Neutral | 0.1 | $33 | $33 | $1901-$2000 | $2000-$2099 | Symmetric, normal | | Bullish | 0.6 | $33 | $54 | $1901-$2000 | $2000-$2162 | Buy dense + sell wide | | Bearish | 0.6 | $54 | $33 | $1838-$2000 | $2000-$2099 | Buy wide + sell dense | | Strong Bull | 0.9 | $24 | $66 | $1928-$2000 | $2000-$2198 | Max asymmetry | ## Sell Optimization Logic ```python def _should_delay_sell(state, current_level, prev_level, mtf, history): """Returns skip reason or None.""" # 1. Momentum protection: skip sell if 1h momentum > 0.5% in uptrend if momentum_1h > SELL_MOMENTUM_THRESHOLD * 100: if trend == "bullish" and structure == "uptrend": return "trend_hold (momentum +X.X%)" # 2. Trailing sell: wait SELL_TRAIL_TICKS (2) before executing trail = state["sell_trail_counter"] level_key = f"{prev_level}->{current_level}" if trail[level_key] < SELL_TRAIL_TICKS: trail[level_key] += 1 return "sell_trail (N/2)" # 3. Cleared → proceed with sell return None ``` ## Trend-Adaptive Position Limits ```python def _get_position_limits(mtf): """Return (max_pct, min_pct) based on trend.""" if trend == "bullish" and strength > 0.3: max_pct = 70 + (80 - 70) * strength # allow more ETH min_pct = 30 elif trend == "bearish" and strength > 0.3: max_pct = 70 min_pct = 30 - (30 - 25) * strength # allow less ETH else: max_pct, min_pct = 70, 30 ``` ## Trade Size Returns `(amount_in_smallest_unit, failure_info)`. SELL returns wei (x1e18), BUY returns uUSDC (x1e6). ```python def calc_trade_amount(direction, eth_bal, usdc_bal, price, current_level=None, grid_levels=None, mtf=None): available_eth = eth_bal - GAS_RESERVE_ETH total_usd = available_eth * price + usdc_bal max_usd = total_usd * MAX_TRADE_PCT # Apply trend-adaptive sizing multiplier = _calc_sizing_multiplier(level, grid_levels, direction, mtf) max_usd *= multiplier if direction == "SELL": return int(min(max_usd / price, available_eth) * 1e18), None # wei else: return int(min(max_usd, usdc_bal * 0.95) * 1e6), None # uUSDC ``` ## Level Update Rule (Critical) | Outcome | Update level? | Rationale | |---|---|---| | Trade succeeded | Yes | Grid crossing consumed | | Trade failed | No | Retry on next tick | | Trade skipped (cooldown/limit) | No | Don't lose the crossing | | Sell delayed (trailing/momentum) | No | Will retry next tick | ## PnL Tracking (dual-denominated) ``` # USD-denominated total_pnl_usd = current_portfolio_usd - cost_basis hodl_value_usd = initial_eth_amount × current_price grid_alpha_usd = current_portfolio_usd - hodl_value_usd # ETH-denominated current_eth_equivalent = current_portfolio_usd / current_price initial_eth_equivalent = cost_basis / initial_price total_pnl_eth = current_eth_equivalent - initial_eth_equivalent ``` **HODL Alpha** (key metric): `grid_alpha_usd > 0` means grid outperforms pure ETH holding. ## State Schema ```json { "version": 5, "grid": {"center": 2000, "step": 43.5, "buy_step": 33.3, "sell_step": 53.7, "levels": 6, "range": [1900, 2161], "vol_pct": 2.1, "type": "arithmetic", "level_prices": [1900, 1933, 1967, 2000, 2054, 2107, 2161]}, "grid_set_at": "ISO timestamp", "current_level": 3, "price_history": ["...max 288 (24h at 5min)"], "trades": [{"time": "...", "direction": "SELL", "price": 2050, "amount_usd": 25, "est_profit": 1.5, "tx": "0x...", "grid_from": 2, "grid_to": 3}], "stats": { "total_trades": 15, "realized_pnl": 5.2, "grid_profit": 3.8, "initial_portfolio_usd": 1000, "initial_eth_price": 2000.0, "portfolio_peak_usd": 1050.0, "total_deposits_usd": 0.0, "deposit_history": [], "trade_attempts": 10, "trade_successes": 10, "trade_failures": 0, "sell_attempts": 5, "sell_successes": 5, "buy_attempts": 5, "buy_successes": 5, "retry_attempts": 0, "retry_successes": 0, "started_at": "ISO timestamp", "last_check": "ISO timestamp" }, "stop_triggered": null, "last_trade_times": {"BUY": "...", "SELL": "..."}, "last_failed_trade": null, "last_balances": {"eth": 0.134, "usdc": 257.33, "time": "ISO timestamp"}, "last_quiet_report": "ISO timestamp", "upside_breakout_ticks": 0, "approved_routers": ["0x..."], "errors": {"consecutive": 0, "cooldown_until": null}, "mtf_cache": null, "kline_cache": null, "sell_trail_counter": {} } ``` Key fields: - `grid.type` + `grid.level_prices`: geometric/arithmetic grid support (asymmetric spacing) - `grid.buy_step` / `grid.sell_step`: directional step sizes; `grid.step` = average for backward compat - `stats.initial_eth_price`: records ETH price at bot start for HODL Alpha calculation - `stats.portfolio_peak_usd`: highest portfolio value (for trailing stop) - `stop_triggered`: string describing trigger condition, or null - `last_failed_trade`: cached for `retry` command (expires after 10min) - `upside_breakout_ticks`: confirmation counter for upside recalibration - `approved_routers`: USDC approval cache to avoid redundant approvals - `mtf_cache`: cached multi-timeframe analysis result - `kline_cache`: cached K-line data (1h TTL) - `sell_trail_counter`: tracks sell delay tick counts per level transition ## Operational Interface ### Sub-Commands | Command | Purpose | Trigger | |---|---|---| | `tick` | Main loop: price → MTF → grid → trade → report | Cron every 5min | | `status` | Print current grid state, balances, PnL, trend | On demand | | `report` | Generate daily performance report (Chinese) | Cron daily 08:00 CST | | `history` | Show recent trade history | On demand | | `reset` | Reset grid (recalibrate from scratch), keep trade history | Manual | | `retry` | Retry last failed trade with fresh quote (expires after 10min) | AI agent / manual | | `analyze` | Output detailed market + MTF + round-trip analysis JSON | AI agent | | `deposit` | Manually record deposit/withdrawal for PnL tracking | Manual | | `resume-trading` | Clear stop_triggered flag and resume trading | Manual / AI agent | ```python COMMANDS = { "tick": tick, "status": status, "report": report, "history": history_cmd, "reset": reset, "retry": retry, "analyze": analyze, "deposit": deposit, "resume-trading": resume_trading } if __name__ == "__main__": cmd = sys.argv[1] if len(sys.argv) > 1 else "tick" COMMANDS.get(cmd, tick)() ``` ### AI Agent Output Protocol The `tick` command outputs a structured JSON block for AI agent parsing: ``` ---JSON--- { "version": "1.0", "status": "trade_executed" | "no_trade" | "cooldown" | "trade_failed" | ..., "market": { "price": 2090.45, "ema": 2085.3, "volatility_pct": 1.2, "trend": "bullish", "trend_strength": 0.65, "momentum_1h": 0.35, "momentum_4h": 1.2, "structure": "uptrend", "kline_atr_pct": 1.8 }, "portfolio": {"eth": 0.134, "usdc": 257.33, "total_usd": 538.0, "eth_pct": 52.1}, "grid_level": 3, "direction": "SELL", "tx_hash": "0x...", "failure_reason": "...", "retriable": true, "hodl_alpha": 2.15, "success_rate": {"total_attempts": 182, "successes": 182, "rate_pct": 100.0} } ``` The `analyze` command outputs additional fields: - `multi_timeframe`: full MTF data (EMA short/medium/long, momentum, structure) - `round_trips`: trade pair analysis (good / micro / loss classification) ### Discord Notification Two card formats pushed via Discord Bot API: **Trade executed** (colored embed): - Green = SELL, Blue = BUY - Fields: price, level, total value, position, PnL, grid profit, HODL Alpha, BaseScan link **No trade** (grey compact card): - One-line: price . level . position . PnL . trend . trade count - Only sent once per `QUIET_INTERVAL` (default 1 hour) ### Deposit/Withdrawal Detection Automatically detects external balance changes: ``` unexplained_change = (current_balance - last_balance) - sum(recorded_trades_since_last) if abs(unexplained_change) > $100: record as deposit or withdrawal → adjust PnL cost basis ``` ### Logging - File: `grid_bot.log` in script directory - Rotation: simple half-file rotation at 1MB - Format: `[YYYY-MM-DD HH:MM:SS] message` ## Adapting to Different Pairs | Consideration | What to adjust | |---|---| | Token decimals | USDC=6, DAI=18, WBTC=8 — affects amount conversion | | Typical volatility | BTC lower vol → smaller `STEP_MIN/MAX_PCT`; meme coins → larger | | Liquidity depth | Low liquidity → smaller `MAX_TRADE_PCT`, add price impact check | | Gas costs | L1 vs L2: adjust `GAS_RESERVE` and `MIN_TRADE_USD` | | Stablecoin pair | TOKEN/USDC pair: `STEP_MIN_PCT` can be much tighter (0.2%) | | Rate limits | Add 300-500ms delay between consecutive OKX API calls | ## AI Review & Optimization AI agent should periodically review trading performance and suggest/apply optimizations. Run weekly or when cumulative PnL stalls. ### Step 1: Pull & Pair Trades Extract recent trades and pair each BUY with its corresponding SELL to form **round trips**. ```python # Matching logic: SELL from level A→B matches BUY from level B→A buy_stack = [] round_trips = [] for trade in trades: if trade["direction"] == "BUY": buy_stack.append(trade) else: # SELL for j in range(len(buy_stack)-1, -1, -1): if buy_stack[j]["grid_to"] == trade["grid_from"]: matched_buy = buy_stack.pop(j) round_trips.append((matched_buy, trade)) break ``` Output per round trip: - **Spread**: `(sell_price - buy_price) / buy_price x 100%` - **Hold time**: minutes between buy and sell - **Status**: profit (spread > 0.3%), micro-profit (0 < spread < 0.3%), loss (spread < 0) ### Step 2: Flag Anomalies | Flag | Condition | Meaning | |---|---|---| | `LOSS` | spread < 0 | Bought high, sold low | | `MICRO` | 0 < spread < 0.3% | Profit too small to cover DEX costs | | `GOOD` | spread >= 0.3% | Healthy grid profit | Key metrics: win rate, loss impact, micro-trade ratio (if > 30%, step too small). ### Step 3: Root Cause Analysis **LOSS trades:** | Pattern | Root Cause | Fix | |---|---|---| | Buy @high, sell @low after recalibration | Grid chased a spike | Increase `UPSIDE_CONFIRM_TICKS`, reduce `MAX_CENTER_SHIFT_PCT` | | Buy @high in trend, sell @low on reversal | EMA too reactive | Increase `EMA_PERIOD` or `GRID_RECALIBRATE_HOURS` | | Loss during flash crash | Stop-loss too loose | Tighten `STOP_LOSS_PCT` | **MICRO trades:** | Pattern | Root Cause | Fix | |---|---|---| | Many trades with < 0.2% spread | Step too small | Increase `STEP_MIN_PCT` | | Rapid back-and-forth at same levels | Low vol, grid too dense | Increase `MIN_TRADE_INTERVAL` | | Trades cluster in 5-10 min windows | Cooldown too short | Increase `MIN_TRADE_INTERVAL` | ### Step 4: Parameter Tuning ``` STEP_MIN_PCT >= DEX_total_cost x 3 where DEX_total_cost ~ slippage + price_impact ~ 0.1-0.3% on L2 → STEP_MIN_PCT >= 0.009 to 0.012 UPSIDE_CONFIRM_TICKS = typical_spike_duration / tick_interval e.g., spikes last ~20min, tick=5min → confirm_ticks = 4-6 MAX_CENTER_SHIFT_PCT = step_pct x 2-3 ``` ### Step 5: Backtest & Apply Simulate new parameters against historical data, then: backup → patch → recalibrate → monitor 24h → re-run analysis. ### Review Checklist (AI Agent Prompt) ``` 1. Read grid_state.json and grid_bot.log 2. Filter trades to review window (default: last 48h) 3. Pair trades into round trips 4. Compute: win_rate, avg_spread, loss_count, micro_count, total_pnl, hodl_alpha 5. If loss_count > 0: trace each loss to recalibration events 6. If micro_ratio > 30%: recommend STEP_MIN_PCT increase 7. Check MTF data for trend alignment during losses 8. Propose specific parameter changes with backtest evidence 9. On user approval: backup → patch → recalibrate → verify ``` ## Failure & Rollback ``` IF Step N fails: 1. Log failure reason to grid_bot.log 2. Increment errors.consecutive 3. If errors.consecutive >= 5: trigger circuit breaker (1h cooldown) 4. Cache failed trade for retry command (10min expiry) 5. DO NOT update grid level 6. Report failure via Discord + JSON output ``` ## Anti-Patterns | Pattern | Problem | |---|---| | Recalibrate every tick | Grid oscillates, no stable levels | | Update level on failure/skip | Silently loses grid crossings | | No position limits | Trending market → 100% one-sided | | Fixed step in volatile market | Too small → over-trades; too large → never triggers | | `sell - buy` as PnL | Net cash flow ≠ profit | | No cooldown | Rapid swings cause burst of trades eating slippage | | No stop-loss | Single crash wipes out months of grid profits | | Martingale without cap | Exponential position growth → liquidation risk | | Arithmetic grid on wide range | $20 step meaningless at $5000 but huge at $500 | | Symmetric recalibration | Chasing upside spikes = buying high then selling low on reversal | | Step floor too low | Micro-profit trades only feed DEX fees, net negative after costs | | No center shift cap | Single spike can drag grid center 5%+, creating losing positions | | Fixed sizing in trends | Selling same size in uptrend = giving away alpha to the market | | Selling immediately in uptrend | Sell delay exists for a reason — let trends play out | | Symmetric grid in strong trends | Asymmetric grids accumulate faster on the favorable side | | Ignoring `buy_step`/`sell_step` in profit calc | Use actual `level_prices` differences, not average `step` |

path: README.md
content: # Grid Trading

基于 OKX DEX API 的 EVM L2 链上动态网格交易策略，适用于任意交易对。

**核心思路：在波动中自动低买高卖。** 将价格区间划分为多个网格层级，价格下跌触网买入，上涨触网卖出。结合 MTF 多时间框架趋势分析，动态调整网格宽度和仓位。

## 特性

- **非对称网格** — 看涨时买侧密集/卖侧稀疏，看跌时反转
- **波动率自适应** — 基于 ATR 动态调整网格宽度
- **多时间框架分析 (MTF)** — 5min 价格 + 1H/4H EMA + 8H 结构检测
- **趋势跟踪** — 动态仓位管理，顺势加仓逆势减仓
- **追踪止盈** — 强上涨中延迟卖出，让利润奔跑
- **动量过滤器** — 趋势动量强劲时跳过卖出信号
- **HODL Alpha 追踪** — 对比策略收益与简单持有
- **风控体系** — 止损 / 追踪止损 / 闪崩保护 / 熔断机制

## 架构

```
┌─────────────────────────────────────────────────────┐
│               AI Agent (Claude)                      │
│  策略设计 → 回测验证 → 参数调优 → 复盘迭代          │
└──────────────────────┬──────────────────────────────┘
                       │ 生成 / 优化
┌──────────────────────▼──────────────────────────────┐
│               策略脚本 (Python)                      │
│  数据采集 → MTF分析 → 网格决策 → 风控检查 → 交易执行│
└──────────────────────┬──────────────────────────────┘
                       │ 调用
┌──────────────────────▼──────────────────────────────┐
│                 onchainos Skills                      │
│  行情 · K线 · 钱包 · DEX路由 · TEE签名 · 交易广播  │
└──────────────────────┬──────────────────────────────┘
                       │ 上链
┌──────────────────────▼──────────────────────────────┐
│                  EVM L2 链上执行                      │
│           OKX DEX 聚合器 → 最优路由 → 成交           │
└─────────────────────────────────────────────────────┘
```

## 网格算法

```
当前价格 → 1H K线 (24根) → ATR% → 波动率分类 → 网格宽度
                                        ↓
                                  MTF 趋势分析
                                        ↓
                              非对称网格 + 动态仓位
                                        ↓
                                  买/卖触发 → 执行
```

| 市场状态 | 网格行为 | 仓位策略 |
|----------|----------|----------|
| 震荡 | 窄间距，双向密集 | 买卖等量 |
| 看涨 | 买侧密集，卖侧稀疏 | 加大买入，减少卖出 |
| 看跌 | 卖侧密集，买侧稀疏 | 加大卖出，减少买入 |
| 高波动 | 自动加宽间距 | 降低单笔金额 |

详细算法说明见 [references/grid-algorithm.md](references/grid-algorithm.md)。

## 与 V3 LP 调仓的区别

| 维度 | 网格交易 | V3 LP 调仓 |
|------|---------|-----------|
| 收益来源 | 网格价差（低买高卖） | LP 手续费（做市） |
| 持仓形式 | ETH + USDC 代币余额 | LP NFT 头寸 |
| 链上操作 | 单步 swap | 四步：claim → remove → swap → deposit |
| 交易频率 | 每 tick 可能触发 | 仅在触发条件满足时调仓 |
| 主要风险 | 单边行情踏空 | 无常损失 (IL) |
| Gas 敏感度 | 低 | 高（多步操作） |

## 快速开始

详细步骤见 [SETUP.md](SETUP.md)。

```bash
# 1. 安装
openclaw skill install grid-trading

# 2. 配置
cd ~/.openclaw/skills/grid-trading/references
cp ../.env.example .env    # 填入 API keys + 钱包地址
vi config.json             # 调整网格参数

# 3. 测试（只读，安全）
python3 eth_grid.py status

# 4. 注册 cron
zeroclaw cron add '*/5 * * * *' \
  'cd ~/.openclaw/skills/grid-trading/references && set -a && . ../.env && set +a && python3 eth_grid.py tick'
```

## 命令

| 命令 | 用途 | 触发 |
|------|------|------|
| `tick` | 主循环：采集→分析→网格决策→交易 | Cron 每 5 分钟 |
| `status` | 价格、余额、网格层级、趋势、收益 | 手动 |
| `report` | 每日报告 (Discord) | Cron 每天 |
| `history` | 近期交易记录 | 手动 |
| `analyze` | 市场分析 + round-trip 配对 | AI Agent |
| `reset` | 重新校准网格 | 手动 |
| `retry` | 重试上次失败的交易 | 手动 |
| `deposit` | 记录外部存取款 | 手动 |
| `resume-trading` | 清除止损恢复交易 | 手动 |

## 目录结构

```
grid-trading/
├── SKILL.md              # AI Agent 核心知识（流水线、状态机、参数）
├── README.md             # 本文件
├── .env.example          # 环境变量模板
└── references/
    ├── eth_grid.py    # 策略代码（零第三方依赖）
    ├── config.json       # 参考配置（所有可调参数）
    └── grid-algorithm.md # 网格算法详解
```

## 前置条件

- **onchainos** — OKX OnchainOS CLI，内置钱包管理、DEX 交易、TEE 签名等 Skills
- **OKX API Key** — DEX 交易权限
- **Python 3.10+** — 零第三方依赖

## License

Apache-2.0


path: references/config.json
content: {
  "chain_id": "8453",
  "chain_name": "base",
  "token0": {
    "symbol": "ETH",
    "address": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
    "decimals": 18
  },
  "token1": {
    "symbol": "USDC",
    "address": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
    "decimals": 6
  },
  "grid_levels": 6,
  "grid_type": "arithmetic",
  "max_trade_pct": 0.12,
  "min_trade_usd": 5.0,
  "gas_reserve_eth": 0.003,
  "slippage_pct": 1,
  "ema_period": 20,
  "volatility_multiplier_base": 1.5,
  "volatility_multiplier_bull": 3.0,
  "volatility_multiplier_bear": 1.0,
  "asym_factor": 0.4,
  "sizing_strategy": "trend_adaptive",
  "sizing_multiplier_min": 0.5,
  "sizing_multiplier_max": 2.0,
  "stop_loss_pct": 0.15,
  "trailing_stop_pct": 0.10,
  "position_max_pct_default": 70,
  "position_min_pct_default": 30,
  "position_max_pct_bullish": 80,
  "position_min_pct_bearish": 25,
  "step_min_pct": 0.010,
  "step_max_pct": 0.060,
  "vol_recalibrate_ratio": 0.3,
  "max_consecutive_errors": 5,
  "max_same_dir_trades": 3,
  "cooldown_after_errors": 3600,
  "quiet_interval": 3600,
  "min_trade_interval": 1800,
  "grid_recalibrate_hours": 12,
  "upside_confirm_ticks": 6,
  "max_center_shift_pct": 0.03,
  "mtf_short_period": 5,
  "mtf_medium_period": 12,
  "mtf_long_period": 48,
  "mtf_structure_period": 96,
  "sell_trail_ticks": 2,
  "sell_momentum_threshold": 0.005,
  "dip_buy_lookback": 12,
  "dip_buy_min_drawdown": 0.005,
  "dip_buy_cooldown": 1800,
  "dip_buy_tiers": [
    [0.03, 2.0],
    [0.02, 1.5],
    [0.01, 1.0],
    [0.005, 0.5]
  ],
  "dip_buy_momentum_floor": -0.5,
  "dip_buy_reversal_ticks": 2,
  "max_log_bytes": 1000000
}


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)