Starter Coach V2 Generate safe, backtestable DEX spot-trading strategy specs from natural language. Scope - DEX spot only — long-only, no perps, no shorts, no margin - Blue-chip (ETH/SOL/BTC) through meme-token trading - OKX DEX venue - On-chain data backbone: OnchainOS CLI (sole source for smart money, dev, bundler, fresh wallet, honeypot, lp locked, taxes, top holders tags) You emit a JSON strategy spec conforming to (Draft 2020-12). The harness validates it before any backtest or live execution. You never write freeform trading code. --- 0. Coaching Journey — 6 Steps This skill follows a s…

+pnl.toFixed(2);\"\n \"pe.className=pnl>=0?'green':'red';\"\n \"document.getElementById('trades').textContent=(s.daily_trades||0)+'/'+(s.daily_limit||0)+' today';\"\n \"const wr=(s.wins&&s.total_trades)?Math.round(s.wins/s.total_trades*100):0;\"\n \"document.getElementById('winrate').textContent=s.total_trades?(wr+'% ('+s.wins+'W/'+s.losses+'L)'):'—';\"\n \"const open=(d.positions||[]).filter(p=>p.status==='open');\"\n \"document.getElementById('open-count').textContent=open.length;\"\n \"document.getElementById('pos-body').innerHTML=open.map(p=>{\"\n \"const pct=p.pnl_pct||0;const cl=pct>=0?'green':'red';\"\n \"const since=(p.entry_ts||'').slice(11,19);\"\n \"return '\u003ctr>\u003ctd>'+(p.symbol||p.token.slice(0,12))+'\u003c/td>\"\n \"\u003ctd>'+(p.entry_price||0).toFixed(6)+'\u003c/td>\"\n \"\u003ctd>'+(p.current_price||0).toFixed(6)+'\u003c/td>\"\n \"\u003ctd class=\\\"'+cl+'\\\">'+(pct>=0?'+':'')+pct.toFixed(1)+'%\u003c/td>\"\n \"\u003ctd>'+since+'\u003c/td>\u003c/tr>';\"\n \"}).join('');\"\n \"const feed=document.getElementById('feed');\"\n \"feed.innerHTML=(d.signal_feed||[]).slice(-30).reverse().map(e=>{\"\n \"const cl=e.type==='buy'?'green':e.type==='sell'?'red':e.type==='filter'?'yellow':'';\"\n \"return '\u003cdiv class=\\\"feed-item\\\">\u003cspan class=\\\"ts\\\">'+(e.ts||'').slice(11,19)+'\u003c/span>\"\n \"\u003cspan class=\\\"'+cl+'\\\">'+e.msg+'\u003c/span>\u003c/div>';\"\n \"}).join('');\"\n \"}catch(err){console.error(err);}\"\n \"}\"\n \"setInterval(refresh,5000);refresh();\"\n \"\u003c/script>\u003c/body>\u003c/html>\"\n )\n dashboard_html = _DASH_HTML.replace(\"__THEME__\", theme)\n\n filename = f\"{name}_bot.py\"\n\n script = f'''#!/usr/bin/env python3\n\"\"\"\n{theme}\n{tagline}\n\nGenerated by Starter Coach v{meta.get(\"version\", \"1.0\")}\nStrategy : {name}\nRisk tier: {risk_tier}\nEntry : {entry_type} on {symbol} ({timeframe})\nChain : {chain}\n\n⚠️ Paper trade by default (PAPER_TRADE = True). Set to False to go live.\n Requires: onchainos CLI installed, wallet logged in.\n Run : python3 {filename}\n\"\"\"\nfrom __future__ import annotations\n\nimport argparse, json, sys, time, threading\nfrom datetime import datetime, timezone\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\nfrom pathlib import Path\n\n# ── Import OnchainOS from the same skill directory ─────────────────\nsys.path.insert(0, str(Path(__file__).parent))\nfrom onchainos import OnchainOS\n\n# ── Config ─────────────────────────────────────────────────────────\nPAPER_TRADE = True\nCHAIN = {repr(chain)}\nPOLL_SEC = 30\nSYMBOL = {repr(symbol)}\nTIMEFRAME = {repr(timeframe)}\nENTRY_TYPE = {repr(entry_type)}\nUSD_PER_TRADE = {usd_size}\nSTOP_LOSS_PCT = {sl_pct}\nTAKE_PROFIT_PCT = {tp_pct}\n{f\"TRAIL_STOP_PCT = {trail_pct}\" if trail_pct else \"TRAIL_STOP_PCT = None\"}\nMAX_DAILY_TRADES = {max_daily}\nMAX_CONCURRENT = {max_conc}\nSESSION_LOSS_PAUSE = {sess_pause}\nMAX_BUDGET_USD = {total_budget} # total budget cap — bot stops spending beyond this\nDASHBOARD_PORT = {dashboard_port}\n\n{token_address_line}\n\nENTRY_PARAMS = {repr(entry_params)}\n\nSAFETY = {repr(safety_dict)}\n\n# ── OnchainOS client ───────────────────────────────────────────────\noc = OnchainOS(chain=CHAIN)\nWALLET_ADDRESS = \"\"\n\n# ── State ──────────────────────────────────────────────────────────\npositions: list[dict] = []\ndaily_trades = 0\nconsec_losses = 0\nlast_reset_day = \"\"\nsignal_feed: list[dict] = []\ntotal_spent_usd = 0.0 # tracks cumulative live spend against MAX_BUDGET_USD\nDASHBOARD_HTML = {repr(dashboard_html)}\n\n# ── TA helpers ─────────────────────────────────────────────────────\ndef _ema(vals: list[float], n: int) -> list[float | None]:\n if len(vals) \u003c n: return [None] * len(vals)\n k = 2 / (n + 1)\n seed = sum(vals[:n]) / n\n out: list[float | None] = [None] * (n - 1) + [seed]\n for v in vals[n:]:\n out.append(out[-1] * (1 - k) + v * k) # type: ignore[operator]\n return out\n\ndef _rsi_last(closes: list[float], period: int = 14) -> float | None:\n if len(closes) \u003c period + 1: return None\n deltas = [closes[i] - closes[i - 1] for i in range(1, len(closes))]\n gains = [max(0.0, d) for d in deltas]\n losses = [max(0.0, -d) for d in deltas]\n ag = sum(gains[:period]) / period\n al = sum(losses[:period]) / period\n for i in range(period, len(gains)):\n ag = (ag * (period - 1) + gains[i]) / period\n al = (al * (period - 1) + losses[i]) / period\n return 100.0 if al == 0 else 100.0 - 100.0 / (1.0 + ag / al)\n\n# ── Event logger ───────────────────────────────────────────────────\ndef _log(event_type: str, msg: str, data: dict | None = None) -> None:\n ts = datetime.utcnow().isoformat()\n entry = {{\"ts\": ts, \"type\": event_type, \"msg\": msg}}\n if data:\n entry[\"data\"] = data\n signal_feed.append(entry)\n if len(signal_feed) > 100:\n signal_feed.pop(0)\n print(f\"[{{ts[11:19]}}] {{msg}}\")\n\n# ── USDC address helper ────────────────────────────────────────────\ndef _usdc_address() -> str:\n return {{\n \"solana\": \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\",\n \"ethereum\": \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\",\n \"base\": \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\",\n \"bsc\": \"0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d\",\n \"arbitrum\": \"0xaf88d065e77c8cC2239327C5EDb3A432268e5831\",\n }}.get(CHAIN, \"\")\n\n# ── Dashboard server ───────────────────────────────────────────────\nclass _DashHandler(BaseHTTPRequestHandler):\n def log_message(self, *args): pass # silence access logs\n def _send(self, code: int, ctype: str, body: bytes) -> None:\n self.send_response(code)\n self.send_header(\"Content-Type\", ctype)\n self.send_header(\"Content-Length\", str(len(body)))\n self.end_headers()\n self.wfile.write(body)\n def do_GET(self) -> None:\n if self.path == \"/api/state\":\n closed = [p for p in positions if p[\"status\"] == \"closed\"]\n total_pnl = sum(p.get(\"pnl_usd\", 0) for p in closed)\n wins = sum(1 for p in closed if p.get(\"pnl_usd\", 0) > 0)\n losses = len(closed) - wins\n # Add current_price snapshot for open positions\n state_positions = []\n for p in positions:\n snap = dict(p)\n if p[\"status\"] == \"open\":\n pd = oc.get_price_info(p[\"token\"])\n snap[\"current_price\"] = float((pd.get(\"data\") or {{}}).get(\"price\", 0) or 0)\n state_positions.append(snap)\n payload = {{\n \"mode\": \"paper\" if PAPER_TRADE else \"live\",\n \"strategy\": {repr(name)},\n \"entry_type\": ENTRY_TYPE,\n \"sl_pct\": STOP_LOSS_PCT,\n \"tp_pct\": TAKE_PROFIT_PCT,\n \"poll_sec\": POLL_SEC,\n \"wallet\": WALLET_ADDRESS[:8] + \"...\" + WALLET_ADDRESS[-4:] if WALLET_ADDRESS else \"\",\n \"positions\": state_positions,\n \"signal_feed\": signal_feed[-50:],\n \"stats\": {{\n \"total_trades\": len(closed),\n \"wins\": wins,\n \"losses\": losses,\n \"pnl_usd\": round(total_pnl, 2),\n \"daily_trades\": daily_trades,\n \"daily_limit\": MAX_DAILY_TRADES,\n }},\n }}\n body = json.dumps(payload).encode()\n self._send(200, \"application/json\", body)\n else:\n body = DASHBOARD_HTML.encode()\n self._send(200, \"text/html\", body)\n\n\ndef _start_dashboard() -> None:\n srv = HTTPServer((\"\", DASHBOARD_PORT), _DashHandler)\n t = threading.Thread(target=srv.serve_forever, daemon=True)\n t.start()\n print(f\"📊 Dashboard : http://localhost:{{DASHBOARD_PORT}}\")\n\n\n# ── Preflight ──────────────────────────────────────────────────────\ndef preflight() -> bool:\n global WALLET_ADDRESS\n status = oc.wallet_status()\n logged_in = status.get(\"loggedIn\") or status.get(\"data\", {{}}).get(\"loggedIn\")\n if not logged_in:\n print(\"❌ Not logged in. Run: onchainos wallet login \u003cemail>\")\n return False\n addr = oc.get_wallet_address()\n if not addr:\n print(\"❌ Could not resolve wallet address for chain:\", CHAIN)\n return False\n WALLET_ADDRESS = addr\n balances = oc.get_all_balances()\n usdc_bal = next(\n (float(b.get(\"balanceUsd\", 0) or 0) for b in balances\n if \"USDC\" in (b.get(\"symbol\") or \"\").upper()),\n 0.0,\n )\n print(f\"✅ Wallet : {{WALLET_ADDRESS[:8]}}...{{WALLET_ADDRESS[-4:]}}\")\n print(f\"✅ USDC : ${{usdc_bal:.2f}}\")\n if usdc_bal \u003c USD_PER_TRADE:\n print(f\"⚠️ Low balance: ${{usdc_bal:.2f}} — need ≥ ${{USD_PER_TRADE}} to trade\")\n if not _usdc_address():\n print(f\"⚠️ USDC address not mapped for chain {{CHAIN}} — live swaps will fail\")\n return True\n\n# ── Safety filters ─────────────────────────────────────────────────\ndef passes_safety(token: str) -> tuple[bool, list[str]]:\n \"\"\"Run safety filters derived from the spec. Returns (passed, [failed_reasons]).\"\"\"\n if not SAFETY:\n return True, []\n tags = oc.get_safety_tags(token)\n failed: list[str] = []\n if SAFETY.get(\"honeypot_check\") and tags.honeypot:\n failed.append(\"HONEYPOT\")\n if SAFETY.get(\"phishing_exclude\") and tags.phishing_flagged:\n failed.append(\"PHISHING\")\n if (v := SAFETY.get(\"lp_locked_min_pct\")) and tags.lp_locked_pct \u003c v:\n failed.append(f\"LP lock {{tags.lp_locked_pct:.0f}}%\u003c{{v}}%\")\n if (v := SAFETY.get(\"buy_tax_max\")) and tags.buy_tax_pct > v:\n failed.append(f\"buy_tax {{tags.buy_tax_pct:.1f}}%>{{v}}%\")\n if (v := SAFETY.get(\"sell_tax_max\")) and tags.sell_tax_pct > v:\n failed.append(f\"sell_tax {{tags.sell_tax_pct:.1f}}%>{{v}}%\")\n if (v := SAFETY.get(\"liquidity_min_usd\")) and tags.liquidity_usd \u003c v:\n failed.append(f\"liq ${{tags.liquidity_usd:.0f}}\u003c${{v}}\")\n if (v := SAFETY.get(\"top_holders_max_pct\")) and tags.top_holders_pct > v:\n failed.append(f\"top_holders {{tags.top_holders_pct:.0f}}%>{{v}}%\")\n if (v := SAFETY.get(\"bundler_max_pct\")) and tags.bundler_ratio_pct > v:\n failed.append(f\"bundler {{tags.bundler_ratio_pct:.0f}}%>{{v}}%\")\n if (v := SAFETY.get(\"dev_holding_max_pct\")) and tags.dev_holding_pct > v:\n failed.append(f\"dev_hold {{tags.dev_holding_pct:.0f}}%>{{v}}%\")\n if (v := SAFETY.get(\"insider_max_pct\")) and tags.insider_holding_pct > v:\n failed.append(f\"insider {{tags.insider_holding_pct:.0f}}%>{{v}}%\")\n if (v := SAFETY.get(\"whale_max_pct\")) and tags.whale_max_pct > v:\n failed.append(f\"whale {{tags.whale_max_pct:.0f}}%>{{v}}%\")\n if (v := SAFETY.get(\"sm_present_min\")) and tags.smart_money_count \u003c v:\n failed.append(f\"SM count {{tags.smart_money_count}}\u003c{{v}}\")\n if (v := SAFETY.get(\"mcap_min_usd\")) and tags.mcap_usd \u003c v:\n failed.append(f\"mcap ${{tags.mcap_usd:.0f}}\u003c${{v}}\")\n if (v := SAFETY.get(\"mcap_max_usd\")) and tags.mcap_usd > v:\n failed.append(f\"mcap ${{tags.mcap_usd:.0f}}>${{v}}\")\n return len(failed) == 0, failed\n\n# ── Signal detection ───────────────────────────────────────────────\n{signal_block}\n\n# ── Position lifecycle ─────────────────────────────────────────────\ndef open_position(token: str, symbol_str: str, signal_data: dict) -> None:\n global daily_trades, total_spent_usd\n if daily_trades >= MAX_DAILY_TRADES:\n return\n if sum(1 for p in positions if p[\"status\"] == \"open\") >= MAX_CONCURRENT:\n return\n if any(p[\"token\"] == token and p[\"status\"] == \"open\" for p in positions):\n return # already in this token\n if not PAPER_TRADE and total_spent_usd + USD_PER_TRADE > MAX_BUDGET_USD:\n _log(\"guard\", f\"[BUDGET CAP] ${total_spent_usd:.2f} spent — max budget ${MAX_BUDGET_USD} reached\")\n return\n\n price_data = oc.get_price_info(token)\n entry_price = float((price_data.get(\"data\") or {{}}).get(\"price\", 0) or 0)\n if entry_price \u003c= 0:\n print(f\"[SKIP] No price for {{symbol_str or token[:12]}}\")\n return\n\n ts = datetime.utcnow().isoformat()\n pos = {{\n \"token\": token, \"symbol\": symbol_str, \"entry_price\": entry_price,\n \"usd\": USD_PER_TRADE, \"entry_ts\": ts, \"status\": \"open\",\n \"peak_price\": entry_price, \"signal_data\": signal_data, \"mode\": \"paper\",\n }}\n\n if PAPER_TRADE:\n _log(\"buy\", f\"[PAPER] BUY {{symbol_str or token[:12]}} @ ${{entry_price:.6f}} | ${{USD_PER_TRADE}} | SL -{{STOP_LOSS_PCT}}%\")\n else:\n usdc = _usdc_address()\n if not usdc:\n _log(\"error\", f\"[ERROR] USDC address not mapped for {{CHAIN}}\"); return\n result = oc.swap_execute(usdc, token, str(USD_PER_TRADE), WALLET_ADDRESS)\n if not result.ok:\n _log(\"error\", f\"[ERROR] Swap failed: {{result.error}}\"); return\n pos[\"mode\"] = \"live\"\n pos[\"tx_hash\"] = result.tx_hash\n total_spent_usd += USD_PER_TRADE\n _log(\"buy\", f\"[LIVE] BUY {{symbol_str}} @ ${{entry_price:.6f}} | tx: {{result.tx_hash[:16]}}... | total spent ${{total_spent_usd:.2f}}/${{MAX_BUDGET_USD}}\")\n\n positions.append(pos)\n daily_trades += 1\n\n\ndef close_position(pos: dict, reason: str, current_price: float) -> None:\n global consec_losses\n ep = pos[\"entry_price\"]\n pnl_pct = (current_price - ep) / ep * 100 if ep else 0.0\n pnl_usd = pos[\"usd\"] * pnl_pct / 100\n pos.update({{\n \"status\": \"closed\", \"exit_ts\": datetime.utcnow().isoformat(),\n \"exit_price\": current_price, \"exit_reason\": reason,\n \"pnl_pct\": round(pnl_pct, 2), \"pnl_usd\": round(pnl_usd, 2),\n }})\n consec_losses = consec_losses + 1 if pnl_pct \u003c 0 else 0\n icon = \"📗\" if pnl_pct >= 0 else \"📕\"\n _log(\"sell\", f\"{{icon}} CLOSE {{pos['symbol'] or pos['token'][:12]}} | {{reason}} | {{pnl_pct:+.1f}}% | ${{pnl_usd:+.2f}}\")\n\n if not PAPER_TRADE and pos.get(\"mode\") == \"live\":\n usdc = _usdc_address()\n bal = oc.get_token_balance(pos[\"token\"])\n if bal > 0 and usdc:\n result = oc.swap_execute(pos[\"token\"], usdc, str(bal), WALLET_ADDRESS)\n if result.ok:\n print(f\" exit tx: {{result.tx_hash[:16]}}...\")\n else:\n print(f\" [ERROR] Exit swap failed: {{result.error}}\")\n\n\n# ── Monitor open positions ─────────────────────────────────────────\ndef monitor_positions() -> None:\n for pos in [p for p in positions if p[\"status\"] == \"open\"]:\n price_data = oc.get_price_info(pos[\"token\"])\n current_price = float((price_data.get(\"data\") or {{}}).get(\"price\", 0) or 0)\n if not current_price:\n continue\n\n pos[\"peak_price\"] = max(pos.get(\"peak_price\", 0), current_price)\n ep = pos[\"entry_price\"]\n pnl_pct = (current_price - ep) / ep * 100 if ep else 0.0\n\n{dev_dump_block}\n{fast_dump_block}\n\n # Trailing stop\n if TRAIL_STOP_PCT is not None:\n peak = pos[\"peak_price\"]\n trail = (peak - current_price) / peak * 100 if peak else 0.0\n if trail >= TRAIL_STOP_PCT:\n close_position(pos, \"trailing_stop\", current_price)\n continue\n\n # Stop loss\n if pnl_pct \u003c= -STOP_LOSS_PCT:\n close_position(pos, \"stop_loss\", current_price)\n continue\n\n # Tiered take profit\n{tiered_block}\n\n # Flat take profit\n if pnl_pct >= TAKE_PROFIT_PCT:\n close_position(pos, \"take_profit\", current_price)\n continue\n\n print(f\"[POS] {{pos['symbol'] or pos['token'][:12]}} | ${{ep:.6f}} → ${{current_price:.6f}} | {{pnl_pct:+.1f}}%\")\n\n\n# ── Risk guards ────────────────────────────────────────────────────\ndef reset_daily() -> None:\n global daily_trades, last_reset_day\n today = datetime.utcnow().strftime(\"%Y-%m-%d\")\n if today != last_reset_day:\n daily_trades = 0\n last_reset_day = today\n\n\ndef should_pause() -> bool:\n if consec_losses >= SESSION_LOSS_PAUSE:\n print(f\"[PAUSE] {{consec_losses}} consecutive losses — sitting out this session\")\n return True\n return False\n\n\n# ── Main loop ──────────────────────────────────────────────────────\ndef main() -> None:\n ap = argparse.ArgumentParser()\n ap.add_argument(\"--smoke-test\", action=\"store_true\", help=\"Verify dashboard endpoints then exit\")\n args = ap.parse_args()\n\n print(f\"\"\"\n╔══════════════════════════════════════╗\n║ {theme[:36]:\u003c36} ║\n║ {(tagline[:36] if tagline else \"\"):\u003c36} ║\n╠══════════════════════════════════════╣\n║ Mode : {{\"PAPER 📄\" if PAPER_TRADE else \"LIVE 🔴\":\u003c29}} ║\n║ Entry : {{ENTRY_TYPE:\u003c29}} ║\n║ SL -{{STOP_LOSS_PCT}}% TP +{{TAKE_PROFIT_PCT}}%{\" \" * 21} ║\n╚══════════════════════════════════════╝\n\"\"\")\n\n if args.smoke_test:\n import urllib.request as _ureq\n _start_dashboard()\n time.sleep(1.5)\n try:\n r1 = _ureq.urlopen(f\"http://localhost:{{DASHBOARD_PORT}}/\", timeout=4)\n assert r1.status == 200, f\"/ returned {{r1.status}}\"\n r2 = _ureq.urlopen(f\"http://localhost:{{DASHBOARD_PORT}}/api/state\", timeout=4)\n assert r2.status == 200, f\"/api/state returned {{r2.status}}\"\n json.loads(r2.read()) # must be valid JSON\n print(\"SMOKE_TEST_OK\")\n sys.exit(0)\n except Exception as e:\n print(f\"SMOKE_TEST_FAIL: {{e}}\")\n sys.exit(1)\n\n if not preflight():\n sys.exit(1)\n\n # ── Live mode gate ─────────────────────────────────────────────\n if not PAPER_TRADE:\n print(\"\\\\n\" + \"=\"*50)\n print(\"🔴 LIVE MODE — REAL FUNDS AT RISK\")\n print(\"=\"*50)\n print(f\" Strategy : {theme}\")\n print(f\" Per trade : ${{{usd_size}}} USD\")\n print(f\" Budget cap : ${{MAX_BUDGET_USD}} USD\")\n print(f\" Stop loss : -{{STOP_LOSS_PCT}}%\")\n print(\"\\\\nReal money will be used. Losses are permanent.\")\n print(\"Safety filters do not guarantee protection against all fraud.\")\n print(\"Automated trading may generate significant taxable events.\")\n print(\"\\\\nType CONFIRM to proceed, or press Ctrl+C to cancel:\\\\n\")\n try:\n user_input = input(\"> \").strip()\n except (KeyboardInterrupt, EOFError):\n print(\"\\\\nCancelled. Run with PAPER_TRADE = True to test first.\")\n sys.exit(0)\n if user_input.upper() != \"CONFIRM\":\n print(\"\\\\nLive mode not activated. Set PAPER_TRADE = True to run in paper mode.\")\n sys.exit(0)\n # Log acknowledgment\n import json as _json\n from pathlib import Path as _P\n _ack = {{\"ts\": datetime.utcnow().isoformat(), \"strategy\": {repr(name)},\n \"confirmation\": \"CONFIRM\", \"budget_cap\": MAX_BUDGET_USD}}\n _ack_path = _P(__file__).parent / f\"{repr(name)[1:-1]}_live_ack.jsonl\"\n with open(_ack_path, \"a\") as _f:\n _f.write(_json.dumps(_ack) + \"\\\\n\")\n print(\"\\\\n✅ Live mode confirmed. Starting bot...\\\\n\")\n\n _start_dashboard()\n\n print(f\"Scanning every {{POLL_SEC}}s — Ctrl+C to stop\\\\n\")\n\n try:\n while True:\n reset_daily()\n if should_pause():\n time.sleep(POLL_SEC * 2)\n continue\n\n monitor_positions()\n\n open_cnt = sum(1 for p in positions if p[\"status\"] == \"open\")\n if open_cnt \u003c MAX_CONCURRENT and daily_trades \u003c MAX_DAILY_TRADES:\n for cand in find_candidates():\n if not cand.get(\"token\"):\n continue\n ok, failed = passes_safety(cand[\"token\"])\n if ok:\n open_position(cand[\"token\"], cand.get(\"symbol\", \"\"), cand.get(\"signal_data\", {{}}))\n else:\n sym = cand.get(\"symbol\") or cand[\"token\"][:8]\n _log(\"filter\", f\"[FILTER] {{sym}} ✗ {{', '.join(failed)}}\")\n\n time.sleep(POLL_SEC)\n\n except KeyboardInterrupt:\n closed = [p for p in positions if p[\"status\"] == \"closed\"]\n open_pos = sum(1 for p in positions if p[\"status\"] == \"open\")\n total_pnl = sum(p.get(\"pnl_usd\", 0) for p in closed)\n wins = sum(1 for p in closed if p.get(\"pnl_usd\", 0) > 0)\n print(f\"\\\\n👋 Bot stopped.\")\n print(f\" {{len(closed)}} trades | {{wins}}W / {{len(closed)-wins}}L | PnL: ${{total_pnl:+.2f}}\")\n if open_pos:\n print(f\" ⚠️ {{open_pos}} position(s) still open — close manually if live\")\n\n\nif __name__ == \"__main__\":\n main()\n'''\n\n return filename, script\n\n\ndef verify_bot_script(filepath: str, code: str = \"\") -> tuple[bool, str]:\n \"\"\"\n Full harness for a generated bot script. Three-layer check:\n 1. Syntax — py_compile\n 2. Methods — all oc.xxx() calls resolve against OnchainOS\n 3. Dashboard — --smoke-test: start server, hit / and /api/state\n Returns (ok, error_message).\n \"\"\"\n import subprocess as _sp, re as _re, sys as _sys\n from pathlib import Path as _Path\n\n # Layer 1: syntax\n r = _sp.run([\"python3\", \"-m\", \"py_compile\", filepath], capture_output=True, text=True)\n if r.returncode != 0:\n return False, f\"[syntax] {r.stderr.strip()}\"\n\n # Layer 2: OnchainOS method names\n src = code or _Path(filepath).read_text()\n calls = set(_re.findall(r'\\boc\\.(\\w+)\\(', src))\n _sys.path.insert(0, str(_Path(__file__).parent))\n from onchainos import OnchainOS as _OC\n valid_methods = {m for m in dir(_OC) if not m.startswith('_')}\n bad = sorted(calls - valid_methods)\n if bad:\n return False, f\"[onchainos] Unknown methods: {', '.join(bad)}\"\n\n # Layer 3: dashboard smoke-test\n r2 = _sp.run(\n [\"python3\", filepath, \"--smoke-test\"],\n capture_output=True, text=True, timeout=12,\n )\n if r2.returncode != 0 or \"SMOKE_TEST_OK\" not in r2.stdout:\n out = (r2.stdout + r2.stderr).strip()\n return False, f\"[dashboard] {out or 'smoke-test failed'}\"\n\n return True, \"\"\n\n\ndef build_spec_from_profile(state: CoachState) -> tuple[dict[str, Any], list[str]]:\n \"\"\"\n Generate a strategy spec from the user profile and selected template.\n Returns (spec, errors). If errors is empty, spec is valid.\n \"\"\"\n profile = state.profile\n goal_key = state.selected_goal or _infer_goal(profile.get(\"goal\", \"\"))\n template = GOAL_TEMPLATES.get(goal_key)\n if not template:\n return {}, [f\"Unknown goal: {goal_key}\"]\n\n risk = RISK_PRESETS.get(\n _infer_risk_level(profile.get(\"risk_level\", \"moderate\")),\n RISK_PRESETS[\"moderate\"],\n )\n\n # Handle grid separately\n if goal_key == \"grid\":\n return _build_grid_spec(profile, risk)\n\n # Build instrument — token/chain inferred from goal (no explicit questions)\n raw_token = profile.get(\"token\") or _infer_token_from_goal(goal_key)\n token = _normalize_token(raw_token)\n chain = profile.get(\"chain\") or _infer_chain_from_token(token)\n timeframe = \"5m\" if template.get(\"live_only\") else \"1H\"\n symbol = f\"{token}-USDC\" if goal_key not in (\"meme_sniper\", \"smart_money\", \"copy_trade\") else \"*\"\n\n spec: dict[str, Any] = {\n \"meta\": {\n \"name\": _make_name(goal_key, token),\n \"version\": \"1.0\",\n \"risk_tier\": _infer_risk_level(profile.get(\"risk_level\", \"moderate\")),\n \"description\": template[\"description\"],\n \"author_intent\": profile.get(\"goal\", \"\"),\n },\n \"instrument\": {\n \"symbol\": symbol,\n \"timeframe\": timeframe,\n },\n \"entry\": {\n \"type\": template[\"entry_type\"],\n **template[\"entry_params\"],\n },\n \"exit\": _build_exit(template[\"exit_style\"], risk),\n \"sizing\": _build_sizing(profile, risk),\n \"filters\": [],\n \"risk_overlays\": _build_overlays(risk),\n }\n\n # Add universe for dynamic-symbol strategies\n if symbol == \"*\":\n spec[\"universe\"] = {\n \"selector\": template[\"entry_type\"],\n \"chain\": chain,\n }\n\n # Copy-trade: inject wallet addresses into entry AND mirror exit\n if goal_key == \"copy_trade\":\n wallets = profile.get(\"target_wallets\", [])\n if not wallets:\n # Cannot build a valid copy_trade spec without wallet addresses — harness requires minItems:1\n return spec, [\"copy_trade requires at least one target wallet address. \"\n \"Ask the user: 'Which wallet address do you want to copy?'\"]\n spec[\"entry\"][\"target_wallet\"] = wallets\n # Also inject into wallet_mirror_sell exit\n for other_exit in spec[\"exit\"].get(\"other\", []):\n if other_exit.get(\"type\") == \"wallet_mirror_sell\":\n other_exit[\"target_wallet\"] = wallets\n\n # Safety stack for meme/live_only\n if template.get(\"needs_safety_stack\"):\n spec[\"filters\"] = _build_safety_stack(risk)\n\n # Beginner defaults\n if _infer_experience(profile.get(\"experience\", \"\")) == \"beginner\":\n spec[\"filters\"].append({\"type\": \"cooldown\", \"bars\": 6})\n # Ensure session_loss_pause is present\n overlay_types = {o[\"type\"] for o in spec[\"risk_overlays\"]}\n if \"session_loss_pause\" not in overlay_types:\n spec[\"risk_overlays\"].append({\n \"type\": \"session_loss_pause\",\n \"max_consecutive_losses\": 2,\n })\n\n # Validate\n ok, errors, meta = validate_spec(spec)\n if meta.get(\"live_only\"):\n spec[\"meta\"][\"live_only\"] = True\n\n state.strategy_spec = spec\n save_state(state)\n\n return spec, errors\n\n\ndef build_spec_llm_first(state: CoachState) -> tuple[dict[str, Any], str, str, list[str]]:\n \"\"\"\n Generate strategy spec using LLM + primitives. Falls back to template on failure.\n\n Returns (spec, theme, tagline, errors).\n Theme + tagline are always populated (fallback values used if LLM unavailable).\n \"\"\"\n goal_key = state.selected_goal or _infer_goal(state.profile.get(\"goal\", \"\"))\n\n # Try LLM generation first\n spec, theme, tagline, errors = generate_strategy_spec(state.profile)\n\n if spec and not errors:\n # Validate LLM output\n ok, val_errors, meta = validate_spec(spec)\n if ok or not val_errors:\n if meta.get(\"live_only\"):\n spec[\"meta\"][\"live_only\"] = True\n state.strategy_spec = spec\n save_state(state)\n return spec, theme, tagline, []\n # LLM spec failed validation — fall through to template\n errors = val_errors\n\n # Fall back to template\n spec, template_errors = build_spec_from_profile(state)\n fb_theme, fb_tagline = get_fallback_theme(goal_key)\n theme = theme or fb_theme\n tagline = tagline or fb_tagline\n\n # Embed theme into spec meta\n if spec:\n spec.setdefault(\"meta\", {}).update({\"theme\": theme, \"tagline\": tagline})\n state.strategy_spec = spec\n save_state(state)\n\n return spec, theme, tagline, template_errors\n\n\ndef record_backtest_result(state: CoachState, result: dict[str, Any]) -> CoachState:\n \"\"\"Store a backtest result for coaching loop.\"\"\"\n state.backtest_history.append({\n \"ts\": time.time(),\n \"result\": result,\n })\n save_state(state)\n return state\n\n\ndef record_improvement(state: CoachState, change: str) -> CoachState:\n \"\"\"Log a parameter improvement.\"\"\"\n state.improvements.append(change)\n save_state(state)\n return state\n\n\ndef mark_live(state: CoachState) -> CoachState:\n \"\"\"Mark strategy as deployed live.\"\"\"\n state.live_deployed = True\n state.step = 5\n save_state(state)\n return state\n\n\ndef confirm_live_mode(state: CoachState, confirmation_text: str) -> tuple[bool, str]:\n \"\"\"Process user's live mode confirmation.\n\n User must type exactly 'CONFIRM' (case-insensitive).\n Logs the acknowledgment to a local file for compliance purposes.\n Returns (confirmed, message).\n \"\"\"\n if confirmation_text.strip().upper() != \"CONFIRM\":\n return False, \"❌ Live mode not activated. Type `CONFIRM` to proceed, or keep running in paper mode.\"\n\n state.live_confirmed = True\n state.live_confirmed_ts = time.time()\n save_state(state)\n\n # Write logged acknowledgment (legal requirement — retain 5 years)\n ack_path = STATE_DIR / f\"{state.session_id}_live_ack.jsonl\"\n ack_record = {\n \"ts\": time.strftime(\"%Y-%m-%dT%H:%M:%SZ\", time.gmtime()),\n \"session_id\": state.session_id,\n \"strategy\": state.strategy_spec.get(\"meta\", {}).get(\"name\", \"\"),\n \"confirmation\": \"CONFIRM\",\n \"message\": \"User confirmed: real funds at risk, no profit guarantee, full liability assumed\",\n }\n with open(ack_path, \"a\") as f:\n f.write(json.dumps(ack_record) + \"\\n\")\n\n return True, \"✅ Live mode confirmed and logged. Proceeding to live trading.\"\n\n\ndef render_bot_summary(spec: dict[str, Any], profile: dict[str, Any] | None = None) -> str:\n \"\"\"Generate a plain-English summary of what the bot will do before going live.\"\"\"\n meta = spec.get(\"meta\", {})\n entry = spec.get(\"entry\", {})\n exit_ = spec.get(\"exit\", {})\n sizing = spec.get(\"sizing\", {})\n overlays = spec.get(\"risk_overlays\", [])\n\n entry_type = entry.get(\"type\", \"unknown\")\n entry_labels = {\n \"wallet_copy_buy\": \"copy trades from specific wallets\",\n \"smart_money_buy\": \"buy when smart money / whale wallets buy\",\n \"ranking_entry\": \"snipe new trending tokens\",\n \"price_drop\": f\"buy when price drops {entry.get('pct', '?')}%\",\n \"ma_cross\": \"buy on moving average crossover signal\",\n \"rsi_threshold\": \"buy when RSI hits oversold level\",\n \"time_schedule\": f\"buy on a fixed {entry.get('interval', 'scheduled')} schedule\",\n \"bollinger_touch\": \"buy when price touches Bollinger Band\",\n \"volume_spike\": \"buy on volume spike signal\",\n \"macd_cross\": \"buy on MACD crossover signal\",\n }\n entry_desc = entry_labels.get(entry_type, entry_type.replace(\"_\", \" \"))\n\n sl = exit_.get(\"stop_loss\", {}).get(\"pct\", \"?\")\n tp_pct = exit_.get(\"take_profit\", {}).get(\"pct\")\n trail = exit_.get(\"trailing_stop\", {}).get(\"pct\")\n tiered = exit_.get(\"tiered_take_profit\")\n\n if tiered:\n tp_levels = \", \".join(f\"+{t['pct_gain']}%\" for t in tiered.get(\"tiers\", []))\n exit_desc = f\"tiered take profit at {tp_levels}, stop loss at -{sl}%\"\n elif trail:\n exit_desc = f\"trailing stop at -{trail}%, stop loss at -{sl}%\"\n elif tp_pct:\n exit_desc = f\"take profit at +{tp_pct}%, stop loss at -{sl}%\"\n else:\n exit_desc = f\"stop loss at -{sl}%\"\n\n usd = sizing.get(\"usd\", \"?\") if sizing.get(\"type\") == \"fixed_usd\" else \"?\"\n budget = (profile or {}).get(\"total_budget\", \"\")\n budget_note = f\" (out of your ${budget} total budget)\" if budget else \"\"\n\n max_daily = next((o.get(\"n\") for o in overlays if o.get(\"type\") == \"max_daily_trades\"), None)\n max_conc = next((o.get(\"n\") for o in overlays if o.get(\"type\") == \"max_concurrent_positions\"), None)\n dd_pause = next((o.get(\"pause_pct\") for o in overlays if o.get(\"type\") == \"drawdown_pause\"), None)\n\n guards = []\n if max_daily: guards.append(f\"max {max_daily} trades/day\")\n if max_conc: guards.append(f\"max {max_conc} positions at once\")\n if dd_pause: guards.append(f\"pause if drawdown hits -{dd_pause}%\")\n\n filters = spec.get(\"filters\", [])\n safety_note = f\" with {len(filters)} safety checks\" if filters else \"\"\n\n lines = [\n f\"**Your bot will:**\",\n f\"- Watch the market and **{entry_desc}**{safety_note}\",\n f\"- Spend **${usd} per trade**{budget_note}\",\n f\"- Exit positions via **{exit_desc}**\",\n ]\n if guards:\n lines.append(f\"- Stop and protect capital if: {', '.join(guards)}\")\n lines.append(f\"\\n*⚠️ Safety filters reduce risk but do not guarantee protection against all losses.*\")\n\n return \"\\n\".join(lines)\n\n\ndef enable_evolve(state: CoachState) -> CoachState:\n \"\"\"Enable auto-evolve engine.\"\"\"\n state.evolve_enabled = True\n state.step = 6\n save_state(state)\n return state\n\n\n# ── Internal Helpers ──────────────────────────────────────────────\n\ndef _infer_goal(text: str) -> str:\n \"\"\"Map free-text goal to a template key.\"\"\"\n text = text.lower()\n if any(w in text for w in (\"dca\", \"schedule\", \"weekly\", \"daily\")):\n return \"dca\"\n if any(w in text for w in (\"dip\", \"drop\", \"crash\")):\n return \"dip_buy\"\n if any(w in text for w in (\"trend\", \"momentum\", \"breakout\")):\n return \"trend_follow\"\n if any(w in text for w in (\"rsi\", \"oversold\", \"mean revert\", \"bollinger\")):\n return \"mean_revert\"\n if any(w in text for w in (\"smart money\", \"whale\", \"follow smart\")):\n return \"smart_money\"\n if any(w in text for w in (\"copy\", \"mirror\", \"wallet\")):\n return \"copy_trade\"\n if any(w in text for w in (\"snipe\", \"meme\", \"trending\", \"new token\", \"pump\")):\n return \"meme_sniper\"\n if \"grid\" in text:\n return \"grid\"\n return \"dip_buy\" # safe default\n\n\ndef _infer_risk_level(text: str) -> str:\n text = text.lower()\n if any(w in text for w in (\n \"conservative\", \"safe\", \"low\", \"mild\",\n \"sleep easy\", \"sleep well\", \"protect\", # new labels\n \"保守\", \"稳\", \"睡得着\",\n )):\n return \"conservative\"\n if any(w in text for w in (\n \"aggressive\", \"high\", \"yolo\", \"ghost pepper\", \"lfg\",\n \"send it\", \"send\", \"degen\", \"go big\", # new labels\n \"梭哈\", \"激进\",\n )):\n return \"aggressive\"\n return \"moderate\"\n\n\ndef _infer_experience(text: str) -> str:\n text = text.lower()\n if any(w in text for w in (\"beginner\", \"first\", \"new\", \"never\")):\n return \"beginner\"\n if any(w in text for w in (\"advanced\", \"expert\", \"pro\")):\n return \"advanced\"\n return \"intermediate\"\n\n\ndef _infer_token_from_goal(goal_key: str) -> str:\n \"\"\"Default token for each goal — no explicit token question needed.\"\"\"\n return {\n \"meme_sniper\": \"trending\",\n \"smart_money\": \"dynamic\",\n \"copy_trade\": \"dynamic\",\n \"grid\": \"SOL\",\n }.get(goal_key, \"SOL\")\n\n\ndef _infer_chain_from_token(token: str) -> str:\n \"\"\"Infer chain from token symbol — defaults to Solana.\"\"\"\n t = token.upper()\n if t in (\"ETH\", \"WETH\", \"USDC\", \"USDT\", \"LINK\", \"UNI\", \"AAVE\", \"WBTC\"):\n return \"ethereum\"\n if t in (\"BNB\", \"CAKE\", \"BUSD\"):\n return \"bsc\"\n return \"solana\"\n\n\ndef _infer_automation_from_risk(risk: str) -> str:\n \"\"\"Conservative users get co-pilot; everyone else gets full auto.\"\"\"\n return \"co_pilot\" if risk == \"conservative\" else \"full_auto\"\n\n\ndef _normalize_token(token: str) -> str:\n \"\"\"Normalize token name to uppercase symbol.\"\"\"\n mapping = {\n \"solana\": \"SOL\", \"sol\": \"SOL\",\n \"ethereum\": \"ETH\", \"eth\": \"ETH\",\n \"bitcoin\": \"BTC\", \"btc\": \"BTC\", \"wbtc\": \"WBTC\",\n \"bnb\": \"BNB\", \"avax\": \"AVAX\", \"doge\": \"DOGE\",\n }\n return mapping.get(token.lower(), token.upper())\n\n\ndef _make_name(goal_key: str, token: str) -> str:\n \"\"\"Generate a spec-compliant name.\"\"\"\n name = f\"{goal_key}_{token.lower()}\"\n # Ensure ^[a-z0-9_]{3,64}$\n name = \"\".join(c for c in name if c.isalnum() or c == \"_\").lower()\n return name[:64] if len(name) >= 3 else f\"{name}_strategy\"\n\n\ndef _build_exit(style: str, risk: dict[str, Any]) -> dict[str, Any]:\n \"\"\"Build exit block based on style.\"\"\"\n sl = {\"pct\": risk[\"sl_pct\"]}\n\n if style == \"sl_tp\":\n return {\n \"stop_loss\": sl,\n \"take_profit\": {\"pct\": risk[\"tp_pct\"]},\n }\n elif style == \"trailing\":\n return {\n \"stop_loss\": sl,\n \"trailing_stop\": {\"pct\": min(risk[\"sl_pct\"], 20)},\n }\n elif style == \"tiered_tp\":\n return {\n \"stop_loss\": sl,\n \"tiered_take_profit\": {\n \"tiers\": [\n {\"pct_gain\": 50, \"pct_sell\": 33},\n {\"pct_gain\": 200, \"pct_sell\": 33},\n {\"pct_gain\": 500, \"pct_sell\": 34},\n ],\n },\n \"other\": [\n {\"type\": \"fast_dump_exit\", \"drop_pct\": 30, \"window_sec\": 60},\n ],\n }\n elif style == \"sm_exit\":\n return {\n \"stop_loss\": sl,\n \"other\": [\n {\"type\": \"smart_money_sell\", \"min_wallets\": 2, \"window_min\": 60},\n {\"type\": \"fast_dump_exit\", \"drop_pct\": 30, \"window_sec\": 60},\n ],\n }\n elif style == \"mirror_exit\":\n return {\n \"stop_loss\": sl,\n \"other\": [\n {\"type\": \"wallet_mirror_sell\", \"target_wallet\": [\"PLACEHOLDER\"], \"min_pct_sold\": 50},\n {\"type\": \"dev_dump\", \"min_usd\": 500},\n {\"type\": \"fast_dump_exit\", \"drop_pct\": 30, \"window_sec\": 60},\n ],\n }\n else:\n return {\"stop_loss\": sl}\n\n\n_BUDGET_ALLOC = {\n \"conservative\": 0.10, # 10% per trade → up to 10 concurrent positions\n \"moderate\": 0.15, # 15% per trade → up to ~7 concurrent positions\n \"aggressive\": 0.20, # 20% per trade → up to 5 concurrent positions\n}\n\n\ndef _build_sizing(profile: dict[str, Any], risk: dict[str, Any]) -> dict[str, Any]:\n \"\"\"Derive per-trade size from total_budget + risk tier.\n\n Falls back to risk preset sizing_usd if no total_budget provided.\n \"\"\"\n total = profile.get(\"total_budget\") or profile.get(\"budget_per_trade\")\n if total and isinstance(total, (int, float)):\n tier = risk.get(\"risk_tier\", \"moderate\")\n pct = _BUDGET_ALLOC.get(tier, 0.15)\n per_trade = round(total * pct, 2)\n return {\"type\": \"fixed_usd\", \"usd\": max(per_trade, 10)} # schema minimum is 10\n return {\"type\": \"fixed_usd\", \"usd\": risk[\"sizing_usd\"]}\n\n\ndef _build_overlays(risk: dict[str, Any]) -> list[dict[str, Any]]:\n \"\"\"Build risk overlays from risk preset.\"\"\"\n return [\n {\"type\": \"max_daily_trades\", \"n\": risk[\"max_daily_trades\"]},\n {\"type\": \"max_concurrent_positions\", \"n\": risk[\"max_concurrent\"]},\n {\"type\": \"drawdown_pause\", \"pause_pct\": risk[\"max_dd_pause\"]},\n {\"type\": \"session_loss_pause\", \"max_consecutive_losses\": risk[\"session_loss_pause\"]},\n ]\n\n\ndef _build_safety_stack(risk: dict[str, Any]) -> list[dict[str, Any]]:\n \"\"\"Build full token safety filter stack for meme/live_only strategies.\"\"\"\n return [\n {\"type\": \"mcap_range\", \"min_usd\": 50000, \"max_usd\": 5000000},\n {\"type\": \"launch_age\", \"min_hours\": 1, \"max_hours\": 168},\n {\"type\": \"honeypot_check\"},\n {\"type\": \"lp_locked\", \"min_pct_locked\": 70, \"min_lock_days\": 14},\n {\"type\": \"buy_tax_max\", \"max_pct\": 5},\n {\"type\": \"sell_tax_max\", \"max_pct\": 5},\n {\"type\": \"liquidity_min\", \"min_usd\": 10000},\n {\"type\": \"top_holders_max\", \"top_n\": 10, \"max_pct\": 40},\n {\"type\": \"bundler_ratio_max\", \"max_pct\": 15},\n {\"type\": \"dev_holding_max\", \"max_pct\": 15},\n {\"type\": \"insider_holding_max\", \"max_pct\": 20},\n {\"type\": \"fresh_wallet_ratio_max\", \"max_pct\": 30},\n {\"type\": \"smart_money_present_min\", \"min_wallets\": 1},\n {\"type\": \"phishing_exclude\"},\n {\"type\": \"whale_concentration_max\", \"max_pct\": 20},\n ]\n\n\ndef _build_grid_spec(\n profile: dict[str, Any], risk: dict[str, Any]\n) -> tuple[dict[str, Any], list[str]]:\n \"\"\"Build a grid meta-template spec.\"\"\"\n raw_token = profile.get(\"token\") or _infer_token_from_goal(\"grid\")\n token = _normalize_token(raw_token)\n total = profile.get(\"total_budget\") or profile.get(\"budget_per_trade\")\n budget = total * (risk.get(\"sizing_pct\", 5) / 100) if total else risk[\"sizing_usd\"]\n\n spec = {\n \"meta\": {\n \"name\": f\"grid_{token.lower()}\",\n \"version\": \"1.0\",\n \"risk_tier\": \"moderate\",\n \"description\": f\"Grid trading {token} within a price range\",\n },\n \"instrument\": {\n \"symbol\": f\"{token}-USDC\",\n \"timeframe\": \"1H\",\n },\n \"grid\": {\n \"price_min\": 0, # User must fill these\n \"price_max\": 0,\n \"levels\": 10,\n \"usd_per_level\": budget,\n \"take_profit_per_level_pct\": 3,\n \"portfolio_stop_loss_pct\": 20,\n },\n }\n errors = [\"Grid spec needs price_min and price_max. Ask the user for the price range.\"]\n return spec, errors\n\n\ndef _suggest_templates(goal_key: str) -> list[dict[str, Any]]:\n \"\"\"Suggest 1-3 templates based on goal.\"\"\"\n primary = GOAL_TEMPLATES.get(goal_key)\n suggestions = []\n if primary:\n suggestions.append({\"key\": goal_key, **primary})\n\n # Add related alternatives\n related: dict[str, list[str]] = {\n \"dca\": [\"dip_buy\"],\n \"dip_buy\": [\"dca\", \"mean_revert\"],\n \"trend_follow\": [\"dip_buy\"],\n \"mean_revert\": [\"trend_follow\", \"dip_buy\"],\n \"smart_money\": [\"copy_trade\", \"meme_sniper\"],\n \"copy_trade\": [\"smart_money\"],\n \"meme_sniper\": [\"smart_money\"],\n \"grid\": [\"dca\"],\n }\n for alt_key in related.get(goal_key, []):\n if len(suggestions) >= 3:\n break\n alt = GOAL_TEMPLATES.get(alt_key)\n if alt:\n suggestions.append({\"key\": alt_key, **alt})\n\n return suggestions\n","content_type":"text/x-python; charset=utf-8","language":"python","size":102777,"content_sha256":"79b94cd1a60a7732783ad4c934f51b184554f5bee3b7c90bf3e0122eb792dd3a"},{"filename":"harness.py","content":"\"\"\"\nHarness — validate strategy specs against schema.json + forbidden-pattern checks.\n\nUsage:\n from harness import validate_spec\n ok, errors = validate_spec(spec_dict)\n\"\"\"\nfrom __future__ import annotations\n\nimport json\nfrom pathlib import Path\nfrom typing import Any\n\ntry:\n import jsonschema\n from jsonschema import Draft202012Validator\nexcept ImportError:\n jsonschema = None # type: ignore\n Draft202012Validator = None # type: ignore\n\nSCHEMA_PATH = Path(__file__).parent / \"schema.json\"\n\n# Primitives with x-live-only: true in schema.json\nLIVE_ONLY_TYPES: set[str] = {\n # entries\n \"smart_money_buy\", \"dev_buy\", \"ranking_entry\", \"wallet_copy_buy\",\n # exits\n \"smart_money_sell\", \"dev_dump\", \"wallet_mirror_sell\", \"fast_dump_exit\",\n # filters\n \"mcap_range\", \"launch_age\",\n \"honeypot_check\", \"lp_locked\", \"buy_tax_max\", \"sell_tax_max\",\n \"liquidity_min\", \"top_holders_max\", \"bundler_ratio_max\",\n \"dev_holding_max\", \"insider_holding_max\", \"fresh_wallet_ratio_max\",\n \"smart_money_present_min\", \"phishing_exclude\", \"whale_concentration_max\",\n}\n\n\ndef _load_schema() -> dict:\n with open(SCHEMA_PATH) as f:\n return json.load(f)\n\n\ndef _collect_types(spec: dict) -> set[str]:\n \"\"\"Walk the spec and collect every primitive type used.\"\"\"\n types: set[str] = set()\n entry = spec.get(\"entry\", {})\n if isinstance(entry, dict) and \"type\" in entry:\n types.add(entry[\"type\"])\n exit_block = spec.get(\"exit\", {})\n for inner_key in (\"stop_loss\", \"take_profit\", \"trailing_stop\", \"tiered_take_profit\"):\n if inner_key in exit_block:\n types.add(inner_key)\n for other in exit_block.get(\"other\", []):\n if isinstance(other, dict) and \"type\" in other:\n types.add(other[\"type\"])\n for filt in spec.get(\"filters\", []):\n if isinstance(filt, dict) and \"type\" in filt:\n types.add(filt[\"type\"])\n sizing = spec.get(\"sizing\", {})\n if isinstance(sizing, dict) and \"type\" in sizing:\n types.add(sizing[\"type\"])\n for overlay in spec.get(\"risk_overlays\", []):\n if isinstance(overlay, dict) and \"type\" in overlay:\n types.add(overlay[\"type\"])\n return types\n\n\ndef _check_live_only(spec: dict) -> bool:\n \"\"\"Return True if any primitive in the spec is live_only.\"\"\"\n return bool(_collect_types(spec) & LIVE_ONLY_TYPES)\n\n\ndef _check_h01(spec: dict, errors: list[str]) -> None:\n \"\"\"H-01: stop_loss is required.\"\"\"\n exit_block = spec.get(\"exit\", {})\n if \"stop_loss\" not in exit_block:\n errors.append(\"H-01: exit.stop_loss is required. A strategy without a stop is gambling.\")\n elif not isinstance(exit_block[\"stop_loss\"], dict) or \"pct\" not in exit_block[\"stop_loss\"]:\n errors.append(\"H-01: exit.stop_loss must have a 'pct' field.\")\n\n\ndef _check_h02(spec: dict, errors: list[str]) -> None:\n \"\"\"H-02: No martingale / averaging down.\"\"\"\n meta = spec.get(\"meta\", {})\n for field in (\"description\", \"author_intent\", \"name\"):\n text = str(meta.get(field, \"\")).lower()\n for keyword in (\"martingale\", \"double down\", \"average down\", \"averaging down\"):\n if keyword in text:\n errors.append(f\"H-02: Martingale / averaging down detected in meta.{field}. Rejected.\")\n return\n\n\ndef _check_h03(spec: dict, errors: list[str]) -> None:\n \"\"\"H-03: stop_loss.pct must be \u003c take_profit.pct (when take_profit is used).\"\"\"\n exit_block = spec.get(\"exit\", {})\n sl = exit_block.get(\"stop_loss\", {})\n tp = exit_block.get(\"take_profit\", {})\n if not sl or not tp:\n return\n sl_pct = sl.get(\"pct\", 0)\n tp_pct = tp.get(\"pct\", 0)\n if sl_pct and tp_pct and sl_pct >= tp_pct:\n errors.append(\n f\"H-03: stop_loss ({sl_pct}%) must be tighter than take_profit ({tp_pct}%). \"\n \"Negative asymmetry rejected.\"\n )\n\n\ndef _check_h05(spec: dict, errors: list[str]) -> None:\n \"\"\"H-05: sizing.pct * max_daily_trades.n \u003c= 20% daily risk cap.\"\"\"\n sizing = spec.get(\"sizing\", {})\n if sizing.get(\"type\") != \"fixed_pct\":\n return\n pct = sizing.get(\"pct\", 0)\n for overlay in spec.get(\"risk_overlays\", []):\n if overlay.get(\"type\") == \"max_daily_trades\":\n n = overlay.get(\"n\", 1)\n daily_risk = pct * n\n if daily_risk > 20:\n errors.append(\n f\"H-05: fixed_pct ({pct}%) x max_daily_trades ({n}) = {daily_risk}% \"\n \"exceeds 20% daily risk cap.\"\n )\n return\n\n\ndef _check_h06(spec: dict, errors: list[str]) -> None:\n \"\"\"H-06: No unknown primitive types.\"\"\"\n known_types = {\n \"price_drop\", \"price_breakout\", \"ma_cross\", \"rsi_threshold\",\n \"volume_spike\", \"time_schedule\", \"smart_money_buy\", \"dev_buy\",\n \"macd_cross\", \"bollinger_touch\", \"ranking_entry\", \"wallet_copy_buy\",\n \"time_exit\", \"indicator_reversal\", \"smart_money_sell\", \"dev_dump\",\n \"wallet_mirror_sell\", \"fast_dump_exit\",\n \"time_window\", \"volatility_range\", \"volume_minimum\", \"cooldown\",\n \"market_regime\", \"price_range\", \"btc_overlay\", \"top_zone_guard\",\n \"mcap_range\", \"launch_age\",\n \"honeypot_check\", \"lp_locked\", \"buy_tax_max\", \"sell_tax_max\",\n \"liquidity_min\", \"top_holders_max\", \"bundler_ratio_max\",\n \"dev_holding_max\", \"insider_holding_max\", \"fresh_wallet_ratio_max\",\n \"smart_money_present_min\", \"phishing_exclude\", \"whale_concentration_max\",\n \"fixed_pct\", \"fixed_usd\", \"volatility_scaled\",\n \"max_daily_trades\", \"max_concurrent_positions\", \"drawdown_pause\",\n \"correlation_cap\", \"session_loss_pause\",\n }\n for t in _collect_types(spec):\n if t in (\"stop_loss\", \"take_profit\", \"trailing_stop\", \"tiered_take_profit\"):\n continue\n if t not in known_types:\n errors.append(f\"H-06: Unknown primitive type '{t}'. No freeform code allowed.\")\n\n\ndef validate_spec(spec: dict[str, Any]) -> tuple[bool, list[str], dict[str, Any]]:\n \"\"\"\n Validate a strategy spec.\n\n Returns:\n (ok, errors, meta)\n - ok: True if spec is valid\n - errors: list of human-readable error strings\n - meta: {\"live_only\": bool, \"primitive_count\": int, \"types_used\": list[str]}\n \"\"\"\n errors: list[str] = []\n\n # 1. JSON Schema validation (H-04 param bounds + structural)\n if Draft202012Validator is not None:\n schema = _load_schema()\n validator = Draft202012Validator(schema)\n for error in sorted(validator.iter_errors(spec), key=lambda e: list(e.path)):\n path = \".\".join(str(p) for p in error.absolute_path) or \"(root)\"\n errors.append(f\"Schema: {path} — {error.message}\")\n else:\n errors.append(\"Warning: jsonschema not installed. Run: pip install jsonschema\")\n\n # 2. Forbidden-pattern checks\n _check_h01(spec, errors)\n _check_h02(spec, errors)\n _check_h03(spec, errors)\n _check_h05(spec, errors)\n _check_h06(spec, errors)\n\n # 3. Derive metadata\n types_used = sorted(_collect_types(spec))\n live_only = _check_live_only(spec)\n\n meta = {\n \"live_only\": live_only,\n \"primitive_count\": len(types_used),\n \"types_used\": types_used,\n }\n\n return len(errors) == 0, errors, meta\n\n\nif __name__ == \"__main__\":\n import sys\n if len(sys.argv) \u003c 2:\n print(\"Usage: python harness.py \u003cspec.json>\")\n sys.exit(1)\n with open(sys.argv[1]) as f:\n spec = json.load(f)\n ok, errors, meta = validate_spec(spec)\n if ok:\n print(f\"PASS live_only={meta['live_only']} primitives={meta['primitive_count']}\")\n print(f\" types: {', '.join(meta['types_used'])}\")\n else:\n print(f\"FAIL ({len(errors)} errors)\")\n for e in errors:\n print(f\" - {e}\")\n sys.exit(1)\n","content_type":"text/x-python; charset=utf-8","language":"python","size":7809,"content_sha256":"c8fcb4fc86b84700737761ac62958d668949c20bba6616e42c61b8d64fcbb4c9"},{"filename":"live_engine.py","content":"\"\"\"\nLive execution engine — real-time position monitoring + trade execution.\n\nTies together:\n - onchainos.py (data + swap execution + wallet monitoring)\n - primitives/* (entry/exit/filter/sizing/risk evaluators)\n - paper_gate.py (graduation-gated sizing)\n - harness.py (spec validation)\n\nUsage:\n from live_engine import LiveEngine\n engine = LiveEngine(spec, wallet_address=\"...\")\n engine.start() # blocking loop\n\"\"\"\nfrom __future__ import annotations\n\nimport json\nimport time\nimport logging\nfrom dataclasses import dataclass, field, asdict\nfrom pathlib import Path\nfrom typing import Any\n\nfrom harness import validate_spec\nfrom onchainos import OnchainOS, SwapResult\nfrom primitives.entry import MarketContext, Bar, evaluate_entry\nfrom primitives.exit import Position, ExitSignal, evaluate_all_exits\nfrom primitives.filter import evaluate_filters\nfrom primitives.sizing import compute_size\nfrom primitives.risk import PortfolioState, check_all_overlays\nfrom paper_gate import get_allowed_size_multiplier, record_paper_trade\n\nlog = logging.getLogger(\"live_engine\")\n\n# ── Config ────────────────────────────────────────────────────────\n\nTICK_INTERVAL_SEC = 30 # Poll interval for price/candle checks\nBALANCE_POLL_SEC = 60 # Poll interval for wallet balance sync\nPOSITION_STATE_DIR = Path(__file__).parent / \".live_state\"\n\n# Common stablecoin addresses per chain (quote tokens for swaps)\nQUOTE_TOKENS: dict[str, str] = {\n \"solana\": \"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v\", # USDC on Solana\n \"ethereum\": \"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48\", # USDC on Ethereum\n \"base\": \"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913\", # USDC on Base\n \"bsc\": \"0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d\", # USDC on BSC\n \"arbitrum\": \"0xaf88d065e77c8cC2239327C5EDb3A432268e5831\", # USDC on Arbitrum\n}\n\n\n# ── Position Tracking ─────────────────────────────────────────────\n\n@dataclass\nclass LivePosition:\n \"\"\"A tracked open position.\"\"\"\n token_address: str = \"\"\n token_symbol: str = \"\"\n entry_price: float = 0.0\n entry_ts: float = 0.0\n entry_tx_hash: str = \"\"\n size_usd: float = 0.0\n token_amount: float = 0.0 # actual tokens held\n peak_price: float = 0.0 # for trailing stop\n current_price: float = 0.0\n unrealized_pnl_usd: float = 0.0\n unrealized_pnl_pct: float = 0.0\n bars_held: int = 0\n status: str = \"open\" # open | closing | closed\n\n\n@dataclass\nclass EngineState:\n \"\"\"Persisted engine state across restarts.\"\"\"\n strategy_name: str = \"\"\n wallet_address: str = \"\"\n chain: str = \"solana\"\n positions: list[dict[str, Any]] = field(default_factory=list)\n closed_trades: list[dict[str, Any]] = field(default_factory=list)\n equity_usd: float = 0.0\n peak_equity_usd: float = 0.0\n trades_today: int = 0\n consecutive_losses: int = 0\n last_trade_day: str = \"\"\n total_trades: int = 0\n total_pnl_usd: float = 0.0\n started_ts: float = 0.0\n last_tick_ts: float = 0.0\n\n\ndef _state_path(strategy_name: str) -> Path:\n POSITION_STATE_DIR.mkdir(parents=True, exist_ok=True)\n return POSITION_STATE_DIR / f\"{strategy_name}.json\"\n\n\ndef _load_engine_state(strategy_name: str) -> EngineState:\n path = _state_path(strategy_name)\n if not path.exists():\n return EngineState(strategy_name=strategy_name, started_ts=time.time())\n with open(path) as f:\n data = json.load(f)\n return EngineState(**data)\n\n\ndef _save_engine_state(state: EngineState) -> None:\n state.last_tick_ts = time.time()\n path = _state_path(state.strategy_name)\n with open(path, \"w\") as f:\n json.dump(asdict(state), f, indent=2)\n\n\n# ── Live Engine ───────────────────────────────────────────────────\n\nclass LiveEngine:\n \"\"\"\n Real-time execution engine for a validated strategy spec.\n\n Lifecycle:\n 1. validate spec\n 2. resolve wallet address\n 3. sync initial balances\n 4. enter tick loop:\n a. fetch latest candles + price\n b. update position prices + P&L\n c. evaluate exits → fire sells\n d. evaluate entries → fire buys\n e. persist state\n \"\"\"\n\n def __init__(\n self,\n spec: dict[str, Any],\n wallet_address: str | None = None,\n initial_equity: float = 0.0,\n paper_mode: bool = False,\n ):\n # Validate spec\n ok, errors, meta = validate_spec(spec)\n if not ok:\n raise ValueError(f\"Invalid spec: {'; '.join(errors)}\")\n\n self.spec = spec\n self.meta = meta\n self.paper_mode = paper_mode\n\n # Extract spec components\n self.strategy_name = spec[\"meta\"][\"name\"]\n self.chain = spec.get(\"universe\", {}).get(\"chain\", \"solana\")\n self.symbol = spec[\"instrument\"][\"symbol\"]\n self.timeframe = spec[\"instrument\"][\"timeframe\"]\n self.entry_spec = spec[\"entry\"]\n self.exit_spec = spec[\"exit\"]\n self.sizing_spec = spec[\"sizing\"]\n self.filters = spec.get(\"filters\", [])\n self.overlays = spec.get(\"risk_overlays\", [])\n\n # OnchainOS client\n self.os = OnchainOS(chain=self.chain)\n\n # Resolve wallet\n self.wallet = wallet_address or self.os.get_wallet_address() or \"\"\n if not self.wallet and not paper_mode:\n raise ValueError(\"No wallet address. Log in via `onchainos wallet login`.\")\n\n # Quote token for swaps\n self.quote_token = QUOTE_TOKENS.get(self.chain, \"\")\n\n # Graduation multiplier (0.0 = paper, 0.1 = micro, 1.0 = full)\n self.size_multiplier = get_allowed_size_multiplier(self.strategy_name)\n\n # Load or create engine state\n self.state = _load_engine_state(self.strategy_name)\n self.state.wallet_address = self.wallet\n self.state.chain = self.chain\n if initial_equity > 0:\n self.state.equity_usd = initial_equity\n self.state.peak_equity_usd = initial_equity\n\n # Rebuild open positions from state\n self.positions: list[LivePosition] = []\n for p_data in self.state.positions:\n self.positions.append(LivePosition(**p_data))\n\n # Portfolio state for risk overlays\n self.portfolio = PortfolioState(\n trades_today=self.state.trades_today,\n open_positions=len(self.positions),\n open_tokens=[p.token_symbol for p in self.positions],\n equity_usd=self.state.equity_usd,\n peak_equity_usd=self.state.peak_equity_usd,\n consecutive_losses=self.state.consecutive_losses,\n session_start_ts=self.state.started_ts,\n )\n\n log.info(\n f\"LiveEngine initialized: {self.strategy_name} \"\n f\"chain={self.chain} wallet={self.wallet[:8]}... \"\n f\"positions={len(self.positions)} equity=${self.state.equity_usd:.2f} \"\n f\"multiplier={self.size_multiplier}\"\n )\n\n # ── Main Loop ─────────────────────────────────────────────────\n\n def start(self) -> None:\n \"\"\"Blocking tick loop. Ctrl-C to stop.\"\"\"\n log.info(f\"Starting live engine for {self.strategy_name}\")\n self._sync_balances()\n\n try:\n while True:\n self._tick()\n time.sleep(TICK_INTERVAL_SEC)\n except KeyboardInterrupt:\n log.info(\"Engine stopped by user\")\n finally:\n self._persist()\n\n def run_once(self) -> dict[str, Any]:\n \"\"\"Run a single tick (for testing or cron-based execution).\"\"\"\n return self._tick()\n\n # ── Tick ──────────────────────────────────────────────────────\n\n def _tick(self) -> dict[str, Any]:\n \"\"\"Single evaluation cycle.\"\"\"\n tick_result: dict[str, Any] = {\n \"ts\": time.time(),\n \"exits\": [],\n \"entries\": [],\n \"errors\": [],\n }\n\n # Reset daily counters if new day\n today = time.strftime(\"%Y-%m-%d\")\n if self.state.last_trade_day != today:\n self.state.trades_today = 0\n self.state.last_trade_day = today\n self.portfolio.trades_today = 0\n\n try:\n # Resolve target token(s)\n tokens = self._resolve_tokens()\n if not tokens:\n return tick_result\n\n for token_addr, token_symbol in tokens:\n # Build market context\n ctx = self._build_context(token_addr, token_symbol)\n if ctx is None:\n continue\n\n # 1. Check exits for open positions on this token\n for pos in self.positions:\n if pos.token_address != token_addr or pos.status != \"open\":\n continue\n pos.current_price = ctx.current_price\n pos.unrealized_pnl_pct = (\n (ctx.current_price - pos.entry_price) / pos.entry_price * 100\n if pos.entry_price > 0 else 0\n )\n pos.unrealized_pnl_usd = pos.size_usd * pos.unrealized_pnl_pct / 100\n if ctx.current_price > pos.peak_price:\n pos.peak_price = ctx.current_price\n pos.bars_held += 1\n\n position_obj = Position(\n token=token_symbol,\n entry_price=pos.entry_price,\n entry_ts=pos.entry_ts,\n entry_bar_idx=0,\n size_usd=pos.size_usd,\n peak_price=pos.peak_price,\n )\n sig = evaluate_all_exits(ctx, position_obj, self.exit_spec)\n if sig:\n exit_result = self._execute_exit(pos, sig, ctx)\n tick_result[\"exits\"].append(exit_result)\n\n # 2. Check entries if no position on this token\n has_position = any(\n p.token_address == token_addr and p.status == \"open\"\n for p in self.positions\n )\n if not has_position:\n entry_result = self._try_entry(ctx, token_addr, token_symbol)\n if entry_result:\n tick_result[\"entries\"].append(entry_result)\n\n except Exception as e:\n log.error(f\"Tick error: {e}\")\n tick_result[\"errors\"].append(str(e))\n\n self._persist()\n return tick_result\n\n # ── Token Resolution ──────────────────────────────────────────\n\n def _resolve_tokens(self) -> list[tuple[str, str]]:\n \"\"\"\n Resolve which tokens to evaluate this tick.\n Fixed symbol: [(token_address, symbol)]\n Dynamic universe (*): fetch from rankings/wallets.\n \"\"\"\n if self.symbol != \"*\":\n # Fixed token — symbol is like \"SOL-USDC\", extract base\n base = self.symbol.split(\"-\")[0]\n # We need the token address — get from price-info or basic-info\n # For now, use the symbol as-is; the engine caller should provide addresses\n return [(self.symbol, base)]\n\n # Dynamic universe — fetch from rankings or wallet tracker\n entry_type = self.entry_spec.get(\"type\", \"\")\n if entry_type == \"ranking_entry\":\n list_name = self.entry_spec.get(\"list_name\", \"trending\")\n top_n = self.entry_spec.get(\"top_n\", 20)\n items = self.os.subscribe_ranking(list_name, top_n)\n return [(item.token, item.symbol) for item in items if item.token]\n elif entry_type == \"wallet_copy_buy\":\n wallets = self.entry_spec.get(\"target_wallet\", [])\n if isinstance(wallets, str):\n wallets = [wallets]\n events = self.os.track_wallets(wallets, trade_type=1) # buys only\n return list({(e.token, e.token[:8]) for e in events if e.token})\n elif entry_type == \"smart_money_buy\":\n events = self.os.track_smart_money(trade_type=1)\n return list({(e.token, e.token[:8]) for e in events if e.token})\n\n return []\n\n # ── Market Context ────────────────────────────────────────────\n\n def _build_context(self, token_addr: str, token_symbol: str) -> MarketContext | None:\n \"\"\"Fetch candles + price and build a MarketContext.\"\"\"\n candles = self.os.get_candles(token_addr, bar=self.timeframe, limit=100)\n if len(candles) \u003c 10:\n return None\n\n bars = [\n Bar(\n ts=c[\"ts\"], open=c[\"open\"], high=c[\"high\"],\n low=c[\"low\"], close=c[\"close\"], volume=c[\"volume\"],\n )\n for c in candles\n ]\n current_price = bars[-1].close if bars else 0\n\n # If current price is 0, try price-info\n if current_price == 0:\n price_data = self.os.get_price_info(token_addr)\n if \"error\" not in price_data:\n current_price = float(\n price_data.get(\"price\", price_data.get(\"lastPrice\", 0)) or 0\n )\n\n if current_price == 0:\n return None\n\n ctx = MarketContext(\n bars=bars,\n current_price=current_price,\n timeframe=self.timeframe,\n token=token_symbol,\n )\n\n # Attach on-chain safety data for live_only filters\n # ctx.onchainos is read by primitives/filter.py for safety tag checks\n if self.meta.get(\"live_only\"):\n ctx.onchainos = self.os\n ctx.onchainos_tags = self.os.get_safety_tags(token_addr)\n\n return ctx\n\n # ── Entry Execution ───────────────────────────────────────────\n\n def _try_entry(\n self, ctx: MarketContext, token_addr: str, token_symbol: str\n ) -> dict[str, Any] | None:\n \"\"\"Evaluate entry conditions and execute buy if triggered.\"\"\"\n # Check risk overlays\n self.portfolio.equity_usd = self.state.equity_usd\n overlay_ok, blockers = check_all_overlays(self.portfolio, self.overlays)\n if not overlay_ok:\n return None\n\n # Check filters\n filter_ok, failing = evaluate_filters(ctx, self.filters)\n if not filter_ok:\n return None\n\n # Check entry trigger\n if not evaluate_entry(ctx, self.entry_spec):\n return None\n\n # Compute size (with graduation multiplier)\n raw_size = compute_size(self.state.equity_usd, ctx, self.sizing_spec)\n size_usd = raw_size * self.size_multiplier\n if size_usd \u003c= 0:\n return None\n\n log.info(\n f\"ENTRY signal: {token_symbol} @ ${ctx.current_price:.6f} \"\n f\"size=${size_usd:.2f} (x{self.size_multiplier})\"\n )\n\n # Execute swap\n if self.paper_mode:\n tx_hash = f\"paper_{int(time.time())}\"\n token_amount = size_usd / ctx.current_price if ctx.current_price > 0 else 0\n else:\n result = self.os.swap_execute(\n from_token=self.quote_token,\n to_token=token_addr,\n readable_amount=str(round(size_usd, 2)),\n wallet=self.wallet,\n )\n if not result.ok:\n log.error(f\"Swap failed: {result.error}\")\n return {\"action\": \"entry_failed\", \"error\": result.error}\n tx_hash = result.tx_hash\n token_amount = size_usd / ctx.current_price if ctx.current_price > 0 else 0\n\n # Track position\n pos = LivePosition(\n token_address=token_addr,\n token_symbol=token_symbol,\n entry_price=ctx.current_price,\n entry_ts=time.time(),\n entry_tx_hash=tx_hash,\n size_usd=size_usd,\n token_amount=token_amount,\n peak_price=ctx.current_price,\n current_price=ctx.current_price,\n )\n self.positions.append(pos)\n self.portfolio.open_positions += 1\n self.portfolio.open_tokens.append(token_symbol)\n self.state.trades_today += 1\n self.portfolio.trades_today += 1\n self.state.total_trades += 1\n\n # Record for paper gate\n if self.paper_mode:\n record_paper_trade(self.strategy_name)\n\n return {\n \"action\": \"entry\",\n \"token\": token_symbol,\n \"price\": ctx.current_price,\n \"size_usd\": size_usd,\n \"tx_hash\": tx_hash,\n \"paper\": self.paper_mode,\n }\n\n # ── Exit Execution ────────────────────────────────────────────\n\n def _execute_exit(\n self, pos: LivePosition, sig: ExitSignal, ctx: MarketContext\n ) -> dict[str, Any]:\n \"\"\"Execute a sell based on exit signal.\"\"\"\n sell_frac = sig.sell_pct / 100\n sell_usd = pos.size_usd * sell_frac\n pnl_pct = (ctx.current_price - pos.entry_price) / pos.entry_price * 100\n pnl_usd = sell_usd * pnl_pct / 100\n\n log.info(\n f\"EXIT signal: {pos.token_symbol} reason={sig.reason} \"\n f\"sell={sig.sell_pct}% pnl={pnl_pct:+.1f}% (${pnl_usd:+.2f})\"\n )\n\n # Execute sell swap\n tx_hash = \"\"\n if self.paper_mode:\n tx_hash = f\"paper_exit_{int(time.time())}\"\n else:\n # Sell the token amount proportional to sell_pct\n sell_amount = pos.token_amount * sell_frac\n if sell_amount > 0:\n result = self.os.swap_execute(\n from_token=pos.token_address,\n to_token=self.quote_token,\n readable_amount=str(sell_amount),\n wallet=self.wallet,\n )\n if not result.ok:\n log.error(f\"Exit swap failed: {result.error}\")\n return {\"action\": \"exit_failed\", \"error\": result.error}\n tx_hash = result.tx_hash\n\n # Update state\n self.state.equity_usd += pnl_usd\n self.state.total_pnl_usd += pnl_usd\n if self.state.equity_usd > self.state.peak_equity_usd:\n self.state.peak_equity_usd = self.state.equity_usd\n self.portfolio.equity_usd = self.state.equity_usd\n self.portfolio.peak_equity_usd = self.state.peak_equity_usd\n\n if pnl_usd \u003c 0:\n self.state.consecutive_losses += 1\n self.portfolio.consecutive_losses += 1\n else:\n self.state.consecutive_losses = 0\n self.portfolio.consecutive_losses = 0\n\n trade_record = {\n \"token\": pos.token_symbol,\n \"token_address\": pos.token_address,\n \"entry_price\": pos.entry_price,\n \"exit_price\": ctx.current_price,\n \"size_usd\": sell_usd,\n \"pnl_usd\": round(pnl_usd, 4),\n \"pnl_pct\": round(pnl_pct, 2),\n \"reason\": sig.reason,\n \"entry_ts\": pos.entry_ts,\n \"exit_ts\": time.time(),\n \"tx_hash\": tx_hash,\n \"paper\": self.paper_mode,\n }\n\n if sig.sell_pct >= 100:\n pos.status = \"closed\"\n self.portfolio.open_positions -= 1\n if pos.token_symbol in self.portfolio.open_tokens:\n self.portfolio.open_tokens.remove(pos.token_symbol)\n self.state.closed_trades.append(trade_record)\n else:\n pos.size_usd *= (1 - sell_frac)\n pos.token_amount *= (1 - sell_frac)\n\n return {\"action\": \"exit\", **trade_record}\n\n # ── Balance Sync ──────────────────────────────────────────────\n\n def _sync_balances(self) -> None:\n \"\"\"Sync wallet balances with on-chain state.\"\"\"\n if self.paper_mode:\n return\n\n balances = self.os.get_all_balances(force=True)\n if not balances:\n log.warning(\"Could not fetch wallet balances\")\n return\n\n # Calculate total equity from balances\n total_usd = 0.0\n for bal in balances:\n if isinstance(bal, dict):\n usd_val = float(bal.get(\"balanceUsd\", bal.get(\"usd_value\", 0)) or 0)\n total_usd += usd_val\n\n if total_usd > 0:\n self.state.equity_usd = total_usd\n if total_usd > self.state.peak_equity_usd:\n self.state.peak_equity_usd = total_usd\n log.info(f\"Wallet equity synced: ${total_usd:.2f}\")\n\n # ── Position Queries ──────────────────────────────────────────\n\n def get_open_positions(self) -> list[dict[str, Any]]:\n \"\"\"Return all open positions with current P&L.\"\"\"\n result = []\n for pos in self.positions:\n if pos.status != \"open\":\n continue\n result.append({\n \"token\": pos.token_symbol,\n \"token_address\": pos.token_address,\n \"entry_price\": pos.entry_price,\n \"current_price\": pos.current_price,\n \"size_usd\": pos.size_usd,\n \"unrealized_pnl_usd\": pos.unrealized_pnl_usd,\n \"unrealized_pnl_pct\": pos.unrealized_pnl_pct,\n \"bars_held\": pos.bars_held,\n \"entry_ts\": pos.entry_ts,\n })\n return result\n\n def get_portfolio_summary(self) -> dict[str, Any]:\n \"\"\"Return portfolio summary for display.\"\"\"\n open_pos = self.get_open_positions()\n total_unrealized = sum(p[\"unrealized_pnl_usd\"] for p in open_pos)\n return {\n \"strategy\": self.strategy_name,\n \"chain\": self.chain,\n \"wallet\": self.wallet,\n \"equity_usd\": round(self.state.equity_usd, 2),\n \"peak_equity_usd\": round(self.state.peak_equity_usd, 2),\n \"total_pnl_usd\": round(self.state.total_pnl_usd, 2),\n \"unrealized_pnl_usd\": round(total_unrealized, 2),\n \"open_positions\": len(open_pos),\n \"total_trades\": self.state.total_trades,\n \"trades_today\": self.state.trades_today,\n \"consecutive_losses\": self.state.consecutive_losses,\n \"size_multiplier\": self.size_multiplier,\n \"paper_mode\": self.paper_mode,\n \"positions\": open_pos,\n }\n\n def get_trade_history(self) -> list[dict[str, Any]]:\n \"\"\"Return closed trade history.\"\"\"\n return list(self.state.closed_trades)\n\n # ── Persistence ───────────────────────────────────────────────\n\n def _persist(self) -> None:\n \"\"\"Save engine state to disk.\"\"\"\n # Serialize open positions\n self.state.positions = [\n asdict(p) for p in self.positions if p.status == \"open\"\n ]\n _save_engine_state(self.state)\n","content_type":"text/x-python; charset=utf-8","language":"python","size":23694,"content_sha256":"3aad5da170b5225543a337171c556a762a7740818a61c145bdd0ce92567c9682"},{"filename":"llm_strategy.py","content":"\"\"\"\nLLM-driven strategy generation.\n\nTakes a user profile + full primitive catalog → unique spec + theme + tagline.\nFalls back gracefully if LLM is unavailable.\n\"\"\"\nfrom __future__ import annotations\n\nimport json\nimport os\nimport re\nfrom typing import Any\n\n# ── Primitive catalog (concise form for LLM prompt) ──────────────────────────\n\nPRIMITIVE_CATALOG = \"\"\"\nENTRY PRIMITIVES (pick exactly one):\n- price_drop: {pct, lookback_bars} — buy when price drops X% in window\n- price_breakout: {direction, lookback_bars, confirm_pct} — buy on breakout\n- ma_cross: {fast_period, slow_period, ma_type} — buy when fast MA crosses slow MA\n- rsi_threshold: {period, level, direction} — buy when RSI crosses level (cross_up at 30 = oversold)\n- volume_spike: {multiplier, avg_bars} — buy on unusual volume surge\n- time_schedule: {interval, anchor_utc} — DCA on fixed schedule (interval: 1D/1W/1M)\n- smart_money_buy: {min_wallets, window_min} — buy when N smart wallets buy same token within window\n- dev_buy: {min_usd, window_min} — buy when developer wallet buys their own token\n- macd_cross: {fast_period, slow_period, signal_period, direction} — buy on MACD crossover\n- bollinger_touch: {period, std_dev, band} — buy when price touches band (band: lower/upper)\n- ranking_entry: {list_name, top_n} — snipe from list (list_name: trending/gainers/new)\n- wallet_copy_buy: {target_wallet[], min_usd, mirror_mode} — mirror wallet buys (mirror_mode: instant/mcap_target)\n\nEXIT PRIMITIVES:\nRequired field: stop_loss: {pct}\nOptional extras in \"other\" array:\n- {type: take_profit, pct} — fixed take profit %\n- {type: trailing_stop, pct} — trailing stop %\n- {type: tiered_take_profit, tiers:[{pct_gain, pct_sell},...]} — sell in layers (pct_sell values must sum to 100)\n- {type: time_exit, max_bars} — exit after N bars\n- {type: indicator_reversal, mirror_entry: true} — exit when entry signal reverses\n- {type: smart_money_sell, min_wallets, window_min} — exit when smart money sells\n- {type: dev_dump, min_usd} — exit on developer dump\n- {type: wallet_mirror_sell, target_wallet[], min_pct_sold} — mirror wallet sells\n- {type: fast_dump_exit, drop_pct, window_sec} — emergency rug/dump exit\n\nFILTER PRIMITIVES (optional array):\nGeneral filters: time_window, volatility_range, volume_minimum, cooldown, market_regime, price_range, btc_overlay, top_zone_guard\nToken safety (use for meme/new tokens): mcap_range, launch_age, honeypot_check, lp_locked, buy_tax_max, sell_tax_max, liquidity_min, top_holders_max, bundler_ratio_max, dev_holding_max, insider_holding_max, fresh_wallet_ratio_max, smart_money_present_min, phishing_exclude, whale_concentration_max\n\nSIZING PRIMITIVES (pick one):\n- {type: fixed_usd, usd} — fixed dollar amount per trade\n- {type: fixed_pct, pct} — fixed % of portfolio per trade\n- {type: volatility_scaled, target_risk_pct, atr_period} — scale size by volatility\n\nRISK OVERLAY PRIMITIVES (array):\n- {type: max_daily_trades, n}\n- {type: max_concurrent_positions, n}\n- {type: drawdown_pause, pause_pct}\n- {type: session_loss_pause, max_consecutive_losses}\n- {type: correlation_cap, mode, max_correlated}\n\"\"\"\n\n_SYSTEM = \"\"\"You are a trading strategy architect. Design unique, personalized trading strategies by snapping primitives together like building blocks.\n\nRules:\n- Pick exactly ONE entry primitive, configure its params thoughtfully for the user's situation\n- stop_loss is always required; add creative exit combos in \"other\"\n- Use token safety filters for meme/new token strategies (honeypot, lp_locked, etc.)\n- theme = 2-3 word punchy identity (like \"Momentum Engine\", \"Shadow Whale\", \"Dip Assassin\")\n- tagline = one sentence: the core trading philosophy of this strategy\n- Make it genuinely unique — tune params to the user's specific profile, not generic defaults\n- Be creative with primitive combinations — the same goal with different risk/budget should feel different\n\"\"\"\n\n_PROMPT = \"\"\"\\\nDesign a trading strategy for this user.\n\nUSER PROFILE:\n{profile_json}\n{extra}\n\nPRIMITIVE CATALOG:\n{catalog}\n\nReturn ONLY valid JSON (no markdown, no explanation):\n{{\n \"theme\": \"2-3 word name\",\n \"tagline\": \"one sentence philosophy\",\n \"spec\": {{\n \"meta\": {{\n \"name\": \"snake_case_64chars_max\",\n \"version\": \"1.0\",\n \"risk_tier\": \"conservative|moderate|aggressive\",\n \"description\": \"one line description\",\n \"author_intent\": \"{goal}\"\n }},\n \"instrument\": {{\n \"symbol\": \"TOKEN-USDC or * for dynamic\",\n \"timeframe\": \"5m|15m|1H|4H|1D\"\n }},\n \"entry\": {{\"type\": \"...\", ...params}},\n \"exit\": {{\n \"stop_loss\": {{\"pct\": N}},\n \"other\": [...]\n }},\n \"sizing\": {{\"type\": \"fixed_usd\", \"usd\": {budget}}},\n \"filters\": [...],\n \"risk_overlays\": [...]\n }}\n}}\n\nFor dynamic/wallet/meme strategies add at top level: \"universe\": {{\"selector\": \"entry_type_value\", \"chain\": \"solana\"}}\n\"\"\"\n\n\ndef _get_api_key() -> str:\n \"\"\"Get API key from env or Claude OAuth credentials.\"\"\"\n key = os.environ.get(\"ANTHROPIC_API_KEY\", \"\").strip()\n if key:\n return key\n creds_path = os.path.expanduser(\"~/.claude/.credentials.json\")\n if not os.path.isfile(creds_path):\n return \"\"\n try:\n import time\n with open(creds_path) as f:\n creds = json.load(f)\n oauth = creds.get(\"claudeAiOauth\", {})\n token = oauth.get(\"accessToken\", \"\")\n if token and oauth.get(\"expiresAt\", 0) > time.time() * 1000:\n return token\n except Exception:\n pass\n return \"\"\n\n\ndef _extract_json(text: str) -> dict[str, Any]:\n # Strip any leading non-JSON characters (org prefixes, markdown fences, etc.)\n text = text.strip()\n # Find first { or [ — start of JSON\n start = min(\n (text.find(c) for c in \"{[\" if text.find(c) != -1),\n default=0,\n )\n text = text[start:]\n # Strip trailing markdown fences\n text = re.sub(r\"\\s*```\\s*$\", \"\", text).strip()\n return json.loads(text)\n\n\n# \"Other\" exit types that must live in exit.other[], not directly on exit\n_OTHER_EXIT_TYPES = {\n \"time_exit\", \"indicator_reversal\", \"smart_money_sell\",\n \"dev_dump\", \"wallet_mirror_sell\", \"fast_dump_exit\",\n}\n# Inner exit types that must be direct keys on exit, not in exit.other[]\n_INNER_EXIT_KEYS = {\"stop_loss\", \"take_profit\", \"trailing_stop\", \"tiered_take_profit\"}\n\n\ndef _normalize_spec(spec: dict[str, Any]) -> dict[str, Any]:\n \"\"\"\n Auto-repair common LLM structural hallucinations before harness validation.\n\n Fixes applied (all silent — no errors raised here):\n 1. Other-exit types placed directly on exit → moved to exit.other[]\n 2. Inner-exit types placed in exit.other[] → promoted to direct exit keys\n 3. Missing universe block when symbol == \"*\"\n 4. tiered_take_profit tiers pct_sell doesn't sum to 100 → rescale last tier\n 5. meta.live_only flag auto-set (harness also does this, but set it early)\n \"\"\"\n import copy\n spec = copy.deepcopy(spec)\n\n exit_block = spec.get(\"exit\", {})\n\n # Fix 1: other-exit types sitting directly on exit → move to exit.other[]\n other = exit_block.get(\"other\", [])\n for key in list(exit_block.keys()):\n if key in _OTHER_EXIT_TYPES:\n item = exit_block.pop(key)\n if isinstance(item, dict):\n item.setdefault(\"type\", key)\n else:\n item = {\"type\": key}\n other.append(item)\n if other:\n exit_block[\"other\"] = other\n\n # Fix 2: inner-exit types sitting inside exit.other[] → promote to direct keys\n remaining_other = []\n for item in exit_block.get(\"other\", []):\n if isinstance(item, dict) and item.get(\"type\") in _INNER_EXIT_KEYS:\n key = item[\"type\"]\n promoted = {k: v for k, v in item.items() if k != \"type\"}\n exit_block.setdefault(key, promoted)\n else:\n remaining_other.append(item)\n if remaining_other:\n exit_block[\"other\"] = remaining_other\n elif \"other\" in exit_block:\n del exit_block[\"other\"]\n\n spec[\"exit\"] = exit_block\n\n # Fix 3: missing universe when symbol == \"*\"\n instr = spec.get(\"instrument\", {})\n if instr.get(\"symbol\") == \"*\" and \"universe\" not in spec:\n entry_type = spec.get(\"entry\", {}).get(\"type\", \"ranking_entry\")\n chain = spec.get(\"meta\", {}).get(\"chain\", \"solana\")\n spec[\"universe\"] = {\"selector\": entry_type, \"chain\": chain}\n\n # Fix 4: tiered_take_profit pct_sell rescaling\n ttp = exit_block.get(\"tiered_take_profit\", {})\n tiers = ttp.get(\"tiers\", [])\n if tiers:\n total = sum(t.get(\"pct_sell\", 0) for t in tiers)\n if total != 100 and total > 0:\n # Rescale all tiers proportionally, assign remainder to last\n scaled = [round(t.get(\"pct_sell\", 0) * 100 / total) for t in tiers]\n diff = 100 - sum(scaled)\n scaled[-1] += diff\n for t, s in zip(tiers, scaled):\n t[\"pct_sell\"] = s\n\n return spec\n\n\ndef generate_strategy_spec(\n profile: dict[str, Any],\n api_key: str = \"\",\n) -> tuple[dict[str, Any], str, str, list[str]]:\n \"\"\"\n LLM-generate a unique strategy spec.\n\n Returns (spec, theme, tagline, errors).\n Empty spec + errors list means failure — caller should fall back to template.\n \"\"\"\n if not api_key:\n api_key = _get_api_key()\n if not api_key:\n return {}, \"\", \"\", [\"No API key — LLM generation unavailable\"]\n\n goal = profile.get(\"goal\", \"unknown\")\n budget = profile.get(\"total_budget\") or profile.get(\"budget_per_trade\") or 100\n wallets = profile.get(\"target_wallets\", [])\n\n extra = f\"\\nWallets to track/mirror: {wallets}\" if wallets else \"\"\n\n prompt = _PROMPT.format(\n profile_json=json.dumps(profile, indent=2),\n catalog=PRIMITIVE_CATALOG,\n goal=goal,\n budget=budget,\n extra=extra,\n )\n\n try:\n import anthropic\n from harness import validate_spec\n client = anthropic.Anthropic(api_key=api_key)\n\n messages: list[dict] = [{\"role\": \"user\", \"content\": prompt}]\n theme = tagline = \"\"\n spec: dict[str, Any] = {}\n last_errors: list[str] = []\n\n # Retry loop — up to 3 attempts, each feeds harness errors back to LLM\n for attempt in range(3):\n resp = client.messages.create(\n model=\"claude-haiku-4-5-20251001\",\n max_tokens=2000,\n system=_SYSTEM,\n messages=messages,\n )\n raw = resp.content[0].text.strip()\n\n try:\n data = _extract_json(raw)\n except json.JSONDecodeError as e:\n last_errors = [f\"Invalid JSON on attempt {attempt + 1}: {e}\"]\n messages.append({\"role\": \"assistant\", \"content\": raw})\n messages.append({\"role\": \"user\", \"content\": (\n f\"Your response was not valid JSON. Error: {e}\\n\"\n \"Return ONLY a valid JSON object — no markdown, no explanation.\"\n )})\n continue\n\n theme = data.get(\"theme\", \"\")\n tagline = data.get(\"tagline\", \"\")\n spec = data.get(\"spec\", {})\n\n if not spec or \"entry\" not in spec:\n last_errors = [\"Incomplete spec: missing 'entry' block\"]\n messages.append({\"role\": \"assistant\", \"content\": raw})\n messages.append({\"role\": \"user\", \"content\": (\n \"The spec is missing the required 'entry' block. \"\n \"Return the complete JSON spec including entry, exit, sizing, filters, risk_overlays.\"\n )})\n continue\n\n # Normalize before validation (fix common structural errors)\n spec = _normalize_spec(spec)\n spec.setdefault(\"meta\", {}).update({\"theme\": theme, \"tagline\": tagline})\n\n ok, errors, _ = validate_spec(spec)\n if ok:\n return spec, theme, tagline, []\n\n # Feed errors back for next attempt\n last_errors = errors\n messages.append({\"role\": \"assistant\", \"content\": raw})\n messages.append({\"role\": \"user\", \"content\": (\n f\"The spec failed harness validation on attempt {attempt + 1}. \"\n f\"Fix ALL of these errors and return the corrected JSON:\\n\"\n + \"\\n\".join(f\"- {e}\" for e in errors)\n )})\n\n # All retries exhausted — return best attempt + errors\n return spec, theme, tagline, last_errors\n\n except Exception as e:\n return {}, \"\", \"\", [f\"LLM generation failed: {e}\"]\n\n\n# ── Deterministic theme fallback (when LLM unavailable) ──────────────────────\n\n_FALLBACK_THEMES: dict[str, tuple[str, str]] = {\n \"meme_sniper\": (\"Momentum Sniper\", \"Catch the spike, bank in layers, flee the rug\"),\n \"smart_money\": (\"Shadow Whale\", \"Follow alpha moves, exit when they exit\"),\n \"copy_trade\": (\"Mirror Strike\", \"Instant mirror execution, ride their edge\"),\n \"dca\": (\"Steady Stacker\", \"Time-based accumulation with trailing protection\"),\n \"dip_buy\": (\"Dip Assassin\", \"Buy the fear, sell the recovery\"),\n \"trend_follow\": (\"Trend Rider\", \"Never fight the trend, let winners run\"),\n \"mean_revert\": (\"Rubber Band\", \"Oversold is just a discount waiting to expire\"),\n \"grid\": (\"Grid Maker\", \"Be the market maker, collect both sides\"),\n}\n\n\ndef get_fallback_theme(goal_key: str) -> tuple[str, str]:\n \"\"\"Return (theme, tagline) for when LLM is unavailable.\"\"\"\n return _FALLBACK_THEMES.get(goal_key, (\"Custom Strategy\", \"Built from your unique profile\"))\n","content_type":"text/x-python; charset=utf-8","language":"python","size":13786,"content_sha256":"6a5f74add3129edc3745fd33e1a905049d39079dca06cee44017da89144fd7d2"},{"filename":"onchainos.py","content":"\"\"\"\nOnchainOS CLI wrapper — all on-chain data reads + swap execution.\n\nAll commands verified against `onchainos --help` output (v2.5.0+).\nNew in v2.5.0: cross_chain_* (bridge) and workflow_* (multi-step research).\nCLI binary at ~/.local/bin/onchainos.\n\nUsage:\n from onchainos import OnchainOS\n os = OnchainOS(chain=\"solana\")\n tags = os.get_safety_tags(\"TokenMintAddress...\")\n\"\"\"\nfrom __future__ import annotations\n\nimport json\nimport subprocess\nfrom dataclasses import dataclass, field\nfrom typing import Any\n\nONCHAINOS_BIN = \"onchainos\"\n\n# Chain name -> numeric chain ID (for security token-scan --tokens flag)\nCHAIN_IDS: dict[str, str] = {\n \"solana\": \"501\",\n \"ethereum\": \"1\",\n \"base\": \"8453\",\n \"bsc\": \"56\",\n \"bnb\": \"56\", # alias for bsc\n \"polygon\": \"137\",\n \"arbitrum\": \"42161\",\n \"optimism\": \"10\",\n \"avalanche\": \"43114\",\n \"sui\": \"784\",\n}\n\n\n@dataclass\nclass SafetyTags:\n \"\"\"Token safety tags aggregated from multiple OnchainOS calls.\"\"\"\n honeypot: bool = False\n lp_locked_pct: float = 0.0\n lp_lock_days: int = 0\n buy_tax_pct: float = 0.0\n sell_tax_pct: float = 0.0\n liquidity_usd: float = 0.0\n top_holders_pct: float = 0.0\n bundler_ratio_pct: float = 0.0\n dev_holding_pct: float = 0.0\n insider_holding_pct: float = 0.0\n fresh_wallet_ratio_pct: float = 0.0\n smart_money_count: int = 0\n phishing_flagged: bool = False\n whale_max_pct: float = 0.0\n mcap_usd: float = 0.0\n launch_age_hours: float = 0.0\n\n\n@dataclass\nclass WalletEvent:\n \"\"\"On-chain wallet transaction event.\"\"\"\n wallet: str = \"\"\n token: str = \"\"\n side: str = \"\" # \"buy\" | \"sell\"\n usd_amount: float = 0.0\n timestamp: float = 0.0\n\n\n@dataclass\nclass RankingItem:\n \"\"\"Token in a ranking list.\"\"\"\n token: str = \"\"\n symbol: str = \"\"\n rank: int = 0\n chain: str = \"\"\n mcap_usd: float = 0.0\n\n\n@dataclass\nclass SwapResult:\n \"\"\"Result of a swap execution.\"\"\"\n ok: bool = False\n tx_hash: str = \"\"\n error: str = \"\"\n from_token: str = \"\"\n to_token: str = \"\"\n amount: str = \"\"\n\n\ndef _run_cli(*args: str, timeout: int = 30) -> dict[str, Any]:\n \"\"\"Run onchainos CLI command and return parsed JSON output.\"\"\"\n cmd = [ONCHAINOS_BIN] + list(args)\n try:\n result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)\n if result.returncode != 0:\n return {\"error\": result.stderr.strip() or f\"exit code {result.returncode}\"}\n # Try parsing as JSON; some commands return table format\n try:\n return json.loads(result.stdout)\n except json.JSONDecodeError:\n return {\"raw\": result.stdout.strip()}\n except FileNotFoundError:\n return {\"error\": f\"onchainos CLI not found at '{ONCHAINOS_BIN}'\"}\n except subprocess.TimeoutExpired:\n return {\"error\": f\"onchainos CLI timed out ({timeout}s)\"}\n\n\nclass OnchainOS:\n def __init__(self, chain: str = \"solana\"):\n self.chain = chain\n self.chain_id = CHAIN_IDS.get(chain, \"501\")\n\n # ── Token Safety ──────────────────────────────────────────────\n\n def security_scan(self, token: str) -> dict[str, Any]:\n \"\"\"\n onchainos security token-scan --tokens \"chainId:contractAddress\"\n Returns raw security scan data.\n \"\"\"\n token_arg = f\"{self.chain_id}:{token}\"\n return _run_cli(\"security\", \"token-scan\", \"--tokens\", token_arg)\n\n def get_advanced_info(self, token: str) -> dict[str, Any]:\n \"\"\"\n onchainos token advanced-info --address \u003ctoken> --chain \u003cchain>\n Returns risk indicators, creator info, dev stats, holder concentration.\n \"\"\"\n return _run_cli(\"token\", \"advanced-info\", \"--address\", token, \"--chain\", self.chain)\n\n def get_price_info(self, token: str) -> dict[str, Any]:\n \"\"\"\n onchainos token price-info --address \u003ctoken> --chain \u003cchain>\n Returns price, market cap, liquidity, volume, 24h change.\n \"\"\"\n return _run_cli(\"token\", \"price-info\", \"--address\", token, \"--chain\", self.chain)\n\n def get_basic_info(self, token: str) -> dict[str, Any]:\n \"\"\"\n onchainos token info --address \u003ctoken> --chain \u003cchain>\n Returns name, symbol, decimals, logo.\n \"\"\"\n return _run_cli(\"token\", \"info\", \"--address\", token, \"--chain\", self.chain)\n\n def get_safety_tags(self, token: str) -> SafetyTags:\n \"\"\"\n Aggregate safety data from security scan + advanced info + holders.\n Combines multiple CLI calls into a single SafetyTags object.\n \"\"\"\n tags = SafetyTags()\n\n # 1. Security scan (honeypot, taxes, phishing)\n sec = self.security_scan(token)\n if \"error\" not in sec:\n tags.honeypot = bool(sec.get(\"honeypot\", False))\n tags.buy_tax_pct = float(sec.get(\"buyTax\", sec.get(\"buy_tax_pct\", 0)) or 0)\n tags.sell_tax_pct = float(sec.get(\"sellTax\", sec.get(\"sell_tax_pct\", 0)) or 0)\n tags.phishing_flagged = bool(sec.get(\"isPhishing\", sec.get(\"phishing_flagged\", False)))\n\n # 2. Advanced info (mcap, liquidity, dev, holders, launch age)\n adv = self.get_advanced_info(token)\n if \"error\" not in adv:\n tags.mcap_usd = float(adv.get(\"marketCap\", adv.get(\"mcap_usd\", 0)) or 0)\n tags.liquidity_usd = float(adv.get(\"liquidity\", adv.get(\"liquidity_usd\", 0)) or 0)\n tags.dev_holding_pct = float(adv.get(\"devHoldPercent\", adv.get(\"dev_holding_pct\", 0)) or 0)\n tags.top_holders_pct = float(adv.get(\"top10HoldPercent\", adv.get(\"top_holders_pct\", 0)) or 0)\n tags.lp_locked_pct = float(adv.get(\"lpLockedPercent\", adv.get(\"lp_locked_pct\", 0)) or 0)\n tags.launch_age_hours = float(adv.get(\"launchAgeHours\", adv.get(\"launch_age_hours\", 0)) or 0)\n\n # 3. Bundle info (bundler ratio)\n bundle = self.get_bundle_info(token)\n if \"error\" not in bundle:\n tags.bundler_ratio_pct = float(bundle.get(\"bundlePercent\", bundle.get(\"bundler_ratio_pct\", 0)) or 0)\n\n # 4. Smart money holders count\n sm_holders = self.get_holders(token, tag_filter=3) # 3 = Smart Money\n if isinstance(sm_holders, list):\n tags.smart_money_count = len(sm_holders)\n\n # 5. Fresh wallet ratio\n fresh = self.get_holders(token, tag_filter=5) # 5 = Fresh Wallet\n all_holders = self.get_holders(token)\n if isinstance(fresh, list) and isinstance(all_holders, list) and all_holders:\n tags.fresh_wallet_ratio_pct = len(fresh) / len(all_holders) * 100\n\n # 6. Insider holding\n insiders = self.get_holders(token, tag_filter=6) # 6 = Insider\n if isinstance(insiders, list):\n # Sum their holding percentages if available\n tags.insider_holding_pct = sum(\n float(h.get(\"holdPercent\", 0) or 0) for h in insiders\n if isinstance(h, dict)\n )\n\n # 7. Whale max (single largest non-LP)\n whales = self.get_holders(token, tag_filter=4) # 4 = Whale\n if isinstance(whales, list) and whales:\n max_pct = max(\n (float(h.get(\"holdPercent\", 0) or 0) for h in whales if isinstance(h, dict)),\n default=0,\n )\n tags.whale_max_pct = max_pct\n\n return tags\n\n # ── Holders ───────────────────────────────────────────────────\n\n def get_holders(self, token: str, tag_filter: int | None = None) -> list[dict] | dict:\n \"\"\"\n onchainos token holders --address \u003ctoken> --chain \u003cchain> [--tag-filter \u003cn>]\n Tag filters: 1=KOL, 2=Developer, 3=Smart Money, 4=Whale,\n 5=Fresh Wallet, 6=Insider, 7=Sniper, 8=Phishing, 9=Bundler\n \"\"\"\n args = [\"token\", \"holders\", \"--address\", token, \"--chain\", self.chain]\n if tag_filter is not None:\n args.extend([\"--tag-filter\", str(tag_filter)])\n data = _run_cli(*args)\n if \"error\" in data:\n return data\n # Return the holders list if present, else the raw data\n return data.get(\"holders\", data.get(\"data\", []))\n\n def get_smart_money_holders(self, token: str) -> list[str]:\n \"\"\"Return wallet addresses of smart-money holders.\"\"\"\n holders = self.get_holders(token, tag_filter=3)\n if isinstance(holders, list):\n return [\n h.get(\"wallet\", h.get(\"address\", \"\"))\n for h in holders if isinstance(h, dict)\n ]\n return []\n\n # ── Dev / Memepump ────────────────────────────────────────────\n\n def get_dev_info(self, token: str) -> dict[str, Any]:\n \"\"\"\n onchainos memepump token-dev-info --address \u003ctoken> --chain \u003cchain>\n Returns dev wallet, launch history, rug history.\n \"\"\"\n return _run_cli(\"memepump\", \"token-dev-info\", \"--address\", token, \"--chain\", self.chain)\n\n def get_bundle_info(self, token: str) -> dict[str, Any]:\n \"\"\"\n onchainos memepump token-bundle-info --address \u003ctoken> --chain \u003cchain>\n Returns bundler/sniper info.\n \"\"\"\n return _run_cli(\"memepump\", \"token-bundle-info\", \"--address\", token, \"--chain\", self.chain)\n\n def get_dev_wallet(self, token: str) -> str | None:\n \"\"\"Return the deployer wallet address.\"\"\"\n data = self.get_dev_info(token)\n if \"error\" in data:\n return None\n return data.get(\"devWallet\", data.get(\"dev_wallet\", data.get(\"creator\")))\n\n # ── Wallet Tracking ───────────────────────────────────────────\n\n def track_wallets(self, wallets: list[str], trade_type: int = 0) -> list[WalletEvent]:\n \"\"\"\n onchainos tracker activities --tracker-type multi_address\n --wallet-address \"addr1,addr2\" --chain \u003cchain>\n [--trade-type 0|1|2]\n trade_type: 0=all, 1=buy, 2=sell\n \"\"\"\n wallet_csv = \",\".join(wallets[:20]) # max 20\n args = [\n \"tracker\", \"activities\",\n \"--tracker-type\", \"multi_address\",\n \"--wallet-address\", wallet_csv,\n \"--chain\", self.chain,\n ]\n if trade_type != 0:\n args.extend([\"--trade-type\", str(trade_type)])\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n return self._parse_events(data)\n\n def track_smart_money(self, trade_type: int = 0) -> list[WalletEvent]:\n \"\"\"\n onchainos tracker activities --tracker-type smart_money --chain \u003cchain>\n \"\"\"\n args = [\n \"tracker\", \"activities\",\n \"--tracker-type\", \"smart_money\",\n \"--chain\", self.chain,\n ]\n if trade_type != 0:\n args.extend([\"--trade-type\", str(trade_type)])\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n return self._parse_events(data)\n\n def _parse_events(self, data: dict) -> list[WalletEvent]:\n events = []\n for tx in data.get(\"activities\", data.get(\"transactions\", data.get(\"data\", []))):\n if not isinstance(tx, dict):\n continue\n events.append(WalletEvent(\n wallet=tx.get(\"wallet\", tx.get(\"walletAddress\", \"\")),\n token=tx.get(\"token\", tx.get(\"tokenAddress\", \"\")),\n side=tx.get(\"side\", tx.get(\"tradeType\", \"\")),\n usd_amount=float(tx.get(\"usd_amount\", tx.get(\"amount\", 0)) or 0),\n timestamp=float(tx.get(\"timestamp\", tx.get(\"txTime\", 0)) or 0),\n ))\n return events\n\n # ── Rankings / Hot Tokens ─────────────────────────────────────\n\n def get_hot_tokens(\n self,\n ranking_type: int = 4,\n top_n: int = 50,\n time_frame: int = 4,\n **filters: Any,\n ) -> list[RankingItem]:\n \"\"\"\n onchainos token hot-tokens --chain \u003cchain> --ranking-type \u003cn>\n ranking_type: 4=Trending (token score), 5=X-mentioned\n time_frame: 1=5min, 2=1h, 3=4h, 4=24h\n filters: market_cap_min, market_cap_max, liquidity_min, liquidity_max, etc.\n \"\"\"\n args = [\n \"token\", \"hot-tokens\",\n \"--chain\", self.chain,\n \"--ranking-type\", str(ranking_type),\n \"--time-frame\", str(time_frame),\n ]\n # Map filter kwargs to CLI flags\n flag_map = {\n \"market_cap_min\": \"--market-cap-min\",\n \"market_cap_max\": \"--market-cap-max\",\n \"liquidity_min\": \"--liquidity-min\",\n \"liquidity_max\": \"--liquidity-max\",\n \"volume_min\": \"--volume-min\",\n \"volume_max\": \"--volume-max\",\n \"holders_min\": \"--holders-min\",\n \"project_id\": \"--project-id\",\n }\n for key, flag in flag_map.items():\n if key in filters:\n args.extend([flag, str(filters[key])])\n\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n items = []\n for i, tok in enumerate(data.get(\"tokens\", data.get(\"data\", [])), 1):\n if not isinstance(tok, dict):\n continue\n if i > top_n:\n break\n items.append(RankingItem(\n token=tok.get(\"tokenAddress\", tok.get(\"token\", \"\")),\n symbol=tok.get(\"symbol\", \"\"),\n rank=i,\n chain=self.chain,\n mcap_usd=float(tok.get(\"marketCap\", tok.get(\"mcap_usd\", 0)) or 0),\n ))\n return items\n\n def subscribe_ranking(self, list_name: str, top_n: int = 50) -> list[RankingItem]:\n \"\"\"\n Map spec list_name to the correct OnchainOS command.\n Routes based on what production skills actually use:\n new/bonding → memepump tokens (pump.fun launches)\n trending → token trending --sort-by trending\n gainers → token trending --sort-by gainers\n volume → token trending --sort-by volume\n hot → token hot-tokens\n \"\"\"\n if list_name in (\"new\", \"bonding\"):\n raw = self.get_memepump_tokens(stage=\"bonding\")\n items = []\n for i, tok in enumerate(raw[:top_n], 1):\n if not isinstance(tok, dict):\n continue\n items.append(RankingItem(\n token=tok.get(\"tokenAddress\", tok.get(\"token\", tok.get(\"contractAddress\", \"\"))),\n symbol=tok.get(\"symbol\", \"\"),\n rank=i,\n chain=self.chain,\n mcap_usd=float(tok.get(\"marketCap\", tok.get(\"mcap_usd\", 0)) or 0),\n ))\n return items\n\n if list_name in (\"trending\", \"gainers\", \"volume\"):\n tf_map = {\"trending\": \"1h\", \"gainers\": \"1h\", \"volume\": \"1h\"}\n sort_map = {\"trending\": \"trending\", \"gainers\": \"gainers\", \"volume\": \"volume\"}\n raw = self.get_token_trending(\n sort_by=sort_map[list_name],\n time_frame=tf_map[list_name],\n )\n items = []\n for i, tok in enumerate(raw[:top_n], 1):\n if not isinstance(tok, dict):\n continue\n items.append(RankingItem(\n token=tok.get(\"tokenAddress\", tok.get(\"token\", \"\")),\n symbol=tok.get(\"symbol\", \"\"),\n rank=i,\n chain=self.chain,\n mcap_usd=float(tok.get(\"marketCap\", 0) or 0),\n ))\n return items\n\n # Fallback: hot-tokens\n return self.get_hot_tokens(ranking_type=4, top_n=top_n)\n\n # ── Market Data ───────────────────────────────────────────────\n\n def get_candles(\n self, token: str, bar: str = \"1H\", limit: int = 100\n ) -> list[dict[str, Any]]:\n \"\"\"\n onchainos market kline --address \u003ctoken> --chain \u003cchain> --bar \u003cbar> --limit \u003cn>\n bar: 1s, 1m, 5m, 15m, 30m, 1H, 4H, 1D, 1W\n limit: max 299\n Returns list of {ts, open, high, low, close, volume}.\n \"\"\"\n data = _run_cli(\n \"market\", \"kline\",\n \"--address\", token,\n \"--chain\", self.chain,\n \"--bar\", bar,\n \"--limit\", str(min(limit, 299)),\n )\n if \"error\" in data:\n return []\n candles = data.get(\"candles\", data.get(\"data\", []))\n result = []\n for c in candles:\n if not isinstance(c, dict):\n continue\n result.append({\n \"ts\": float(c.get(\"ts\", c.get(\"time\", 0)) or 0),\n \"open\": float(c.get(\"open\", c.get(\"o\", 0)) or 0),\n \"high\": float(c.get(\"high\", c.get(\"h\", 0)) or 0),\n \"low\": float(c.get(\"low\", c.get(\"l\", 0)) or 0),\n \"close\": float(c.get(\"close\", c.get(\"c\", 0)) or 0),\n \"volume\": float(c.get(\"volume\", c.get(\"vol\", 0)) or 0),\n })\n return result\n\n # ── Signals ───────────────────────────────────────────────────\n\n def get_signals(\n self,\n wallet_type: int = 1,\n token: str | None = None,\n min_amount_usd: float | None = None,\n ) -> list[dict[str, Any]]:\n \"\"\"\n onchainos signal list --chain \u003cchain> --wallet-type \u003cn>\n wallet_type: 1=Smart Money, 2=KOL, 3=Whales\n \"\"\"\n args = [\n \"signal\", \"list\",\n \"--chain\", self.chain,\n \"--wallet-type\", str(wallet_type),\n ]\n if token:\n args.extend([\"--token-address\", token])\n if min_amount_usd:\n args.extend([\"--min-amount-usd\", str(int(min_amount_usd))])\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n return data.get(\"signals\", data.get(\"data\", []))\n\n # ── Token Trades ──────────────────────────────────────────────\n\n def get_token_trades(\n self, token: str, limit: int = 200, tag_filter: int | None = None\n ) -> list[dict[str, Any]]:\n \"\"\"\n onchainos token trades --address \u003ctoken> --chain \u003cchain> --limit \u003cn>\n tag_filter: 1=KOL, 2=Dev, 3=Smart Money, 4=Whale, etc.\n \"\"\"\n args = [\n \"token\", \"trades\",\n \"--address\", token,\n \"--chain\", self.chain,\n \"--limit\", str(limit),\n ]\n if tag_filter is not None:\n args.extend([\"--tag-filter\", str(tag_filter)])\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n return data.get(\"trades\", data.get(\"data\", []))\n\n # ── Swap ──────────────────────────────────────────────────────\n\n def swap_quote(\n self, from_token: str, to_token: str, readable_amount: str\n ) -> dict[str, Any]:\n \"\"\"\n onchainos swap quote --from \u003caddr> --to \u003caddr> --readable-amount \u003camt> --chain \u003cchain>\n Read-only price estimate.\n \"\"\"\n return _run_cli(\n \"swap\", \"quote\",\n \"--from\", from_token,\n \"--to\", to_token,\n \"--readable-amount\", readable_amount,\n \"--chain\", self.chain,\n )\n\n def swap_execute(\n self,\n from_token: str,\n to_token: str,\n readable_amount: str,\n wallet: str,\n slippage: float | None = None,\n mev_protection: bool = False,\n ) -> SwapResult:\n \"\"\"\n onchainos swap execute --from \u003caddr> --to \u003caddr> --readable-amount \u003camt>\n --chain \u003cchain> --wallet \u003cwallet> [--slippage \u003cn>] [--mev-protection]\n Full execution: quote -> approve -> swap -> sign -> broadcast.\n \"\"\"\n args = [\n \"swap\", \"execute\",\n \"--from\", from_token,\n \"--to\", to_token,\n \"--readable-amount\", readable_amount,\n \"--chain\", self.chain,\n \"--wallet\", wallet,\n \"--biz-type\", \"dex\",\n \"--strategy\", \"starter-coach\",\n ]\n if slippage is not None:\n args.extend([\"--slippage\", str(slippage)])\n if mev_protection:\n args.append(\"--mev-protection\")\n\n data = _run_cli(*args, timeout=60)\n if \"error\" in data:\n return SwapResult(ok=False, error=data[\"error\"])\n return SwapResult(\n ok=True,\n tx_hash=data.get(\"txHash\", data.get(\"tx_hash\", \"\")),\n from_token=from_token,\n to_token=to_token,\n amount=readable_amount,\n )\n\n # ── Wallet / Position Monitoring ──────────────────────────────\n\n def wallet_status(self) -> dict[str, Any]:\n \"\"\"\n onchainos wallet status\n Returns login state, active account, wallet addresses.\n Normalizes: loggedIn surfaced to top-level regardless of nesting.\n \"\"\"\n result = _run_cli(\"wallet\", \"status\")\n # CLI returns {\"ok\": true, \"data\": {\"loggedIn\": true, ...}}\n # Normalize: promote loggedIn to top level for convenience\n if \"loggedIn\" not in result and \"data\" in result and isinstance(result[\"data\"], dict):\n result[\"loggedIn\"] = result[\"data\"].get(\"loggedIn\", False)\n return result\n\n def wallet_addresses(self) -> dict[str, Any]:\n \"\"\"\n onchainos wallet addresses --chain \u003cchainId>\n Returns wallet addresses grouped by chain category.\n \"\"\"\n return _run_cli(\"wallet\", \"addresses\", \"--chain\", self.chain_id)\n\n def get_wallet_address(self) -> str | None:\n \"\"\"Resolve the bot's own wallet address for this chain.\"\"\"\n raw = self.wallet_addresses()\n if \"error\" in raw:\n return None\n # Response: {\"ok\": true, \"data\": {\"solana\": [{\"address\": \"...\"}], \"evm\": [], ...}}\n inner = raw.get(\"data\", raw)\n if isinstance(inner, dict):\n # Chain-keyed: look for solana / evm / xlayer lists\n chain_keys = [\"solana\", \"evm\", \"xlayer\", \"addresses\"]\n for ck in chain_keys:\n lst = inner.get(ck)\n if isinstance(lst, list) and lst:\n first = lst[0]\n addr = first.get(\"address\") or first.get(\"wallet\") if isinstance(first, dict) else first\n if addr:\n return str(addr)\n # Flat address string\n for key in (\"address\", \"wallet\"):\n val = inner.get(key)\n if isinstance(val, str) and val:\n return val\n return None\n\n def get_all_balances(self, force: bool = False) -> list[dict[str, Any]]:\n \"\"\"\n onchainos wallet balance --chain \u003cchainId> [--force]\n Returns all token balances for the active wallet on this chain.\n Each item: {tokenAddress, symbol, balance, balanceUsd, ...}\n \"\"\"\n args = [\"wallet\", \"balance\", \"--chain\", self.chain_id]\n if force:\n args.append(\"--force\")\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n # Normalize response\n balances = data.get(\"balances\", data.get(\"data\", data.get(\"assets\", [])))\n if isinstance(balances, list):\n return balances\n return []\n\n def get_token_balance(self, token_address: str, force: bool = False) -> float:\n \"\"\"\n onchainos wallet balance --chain \u003cchainId> --token-address \u003caddr>\n Returns balance amount for a specific token. 0.0 if not held.\n \"\"\"\n args = [\n \"wallet\", \"balance\",\n \"--chain\", self.chain_id,\n \"--token-address\", token_address,\n ]\n if force:\n args.append(\"--force\")\n data = _run_cli(*args)\n if \"error\" in data:\n return 0.0\n # Parse balance from response\n bal = data.get(\"balance\", data.get(\"amount\", 0))\n if isinstance(bal, (int, float)):\n return float(bal)\n if isinstance(bal, str):\n try:\n return float(bal)\n except ValueError:\n return 0.0\n # May be nested in a list\n balances = data.get(\"balances\", data.get(\"data\", []))\n if isinstance(balances, list) and balances:\n first = balances[0] if isinstance(balances[0], dict) else {}\n return float(first.get(\"balance\", first.get(\"amount\", 0)) or 0)\n return 0.0\n\n def get_wallet_history(\n self, limit: int = 20, begin_ms: int | None = None\n ) -> list[dict[str, Any]]:\n \"\"\"\n onchainos wallet history --chain \u003cchainId> --limit \u003cn>\n Returns recent transactions for the active wallet.\n \"\"\"\n args = [\"wallet\", \"history\", \"--chain\", self.chain_id, \"--limit\", str(limit)]\n if begin_ms is not None:\n args.extend([\"--begin\", str(begin_ms)])\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n return data.get(\"orders\", data.get(\"transactions\", data.get(\"data\", [])))\n\n def get_tx_detail(self, tx_hash: str, address: str) -> dict[str, Any]:\n \"\"\"\n onchainos wallet history --tx-hash \u003chash> --address \u003caddr> --chain \u003cchainId>\n Returns detail for a specific transaction (confirm swap completed).\n \"\"\"\n return _run_cli(\n \"wallet\", \"history\",\n \"--tx-hash\", tx_hash,\n \"--address\", address,\n \"--chain\", self.chain_id,\n )\n\n def wallet_contract_call(\n self,\n to: str,\n unsigned_tx: str,\n ) -> dict[str, Any]:\n \"\"\"\n onchainos wallet contract-call --chain \u003cchainId> --to \u003caddr> --unsigned-tx \u003cdata>\n TEE-sign and broadcast a transaction (Agentic Wallet path).\n Used by scan_live, AI Whale, and any skill needing non-swap on-chain actions.\n Returns {txHash, status, ...}\n \"\"\"\n return _run_cli(\n \"wallet\", \"contract-call\",\n \"--chain\", self.chain_id,\n \"--to\", to,\n \"--unsigned-tx\", unsigned_tx,\n \"--biz-type\", \"dex\",\n \"--strategy\", \"starter-coach\",\n )\n\n # ── Portfolio ─────────────────────────────────────────────────\n\n def get_portfolio_balances(self) -> list[dict[str, Any]]:\n \"\"\"\n onchainos portfolio all-balances --chain \u003cchainId>\n All token balances in the active wallet on this chain.\n \"\"\"\n data = _run_cli(\"portfolio\", \"all-balances\", \"--chain\", self.chain_id)\n if \"error\" in data:\n return []\n return data.get(\"balances\", data.get(\"data\", []))\n\n def get_portfolio_token_balances(\n self, address: str | None = None\n ) -> list[dict[str, Any]]:\n \"\"\"\n onchainos portfolio token-balances --chain \u003cchainId> [--address \u003caddr>]\n Token balances by address (defaults to logged-in wallet).\n \"\"\"\n args = [\"portfolio\", \"token-balances\", \"--chain\", self.chain_id]\n if address:\n args.extend([\"--address\", address])\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n return data.get(\"balances\", data.get(\"data\", []))\n\n def get_portfolio_token_pnl(self, wallet: str, token: str) -> dict[str, Any]:\n \"\"\"\n onchainos market portfolio-token-pnl --chain \u003cchainId>\n --address \u003cwallet> --token \u003ctokenAddr>\n Returns realized/unrealized PnL for a specific held token.\n \"\"\"\n return _run_cli(\n \"market\", \"portfolio-token-pnl\",\n \"--chain\", self.chain_id,\n \"--address\", wallet,\n \"--token\", token,\n )\n\n # ── Token extended ────────────────────────────────────────────\n\n def get_token_liquidity(self, token: str) -> dict[str, Any]:\n \"\"\"\n onchainos token liquidity --chain \u003cchain> --address \u003ctoken>\n Returns LP pool data: pool address, reserve amounts, LP locked %.\n \"\"\"\n return _run_cli(\"token\", \"liquidity\", \"--address\", token, \"--chain\", self.chain)\n\n def get_token_trending(\n self,\n sort_by: str = \"trending\",\n time_frame: str = \"1h\",\n chain: str | None = None,\n **filters: Any,\n ) -> list[dict[str, Any]]:\n \"\"\"\n onchainos token trending --chain \u003cchain> --sort-by \u003csort> --time-frame \u003ctf>\n sort_by: trending | volume | gainers | new\n time_frame: 5min | 1h | 4h | 24h\n filters: market_cap_min, market_cap_max, liquidity_min, holders_min, etc.\n Used by Ranking Sniper and Meme Trenching skills.\n \"\"\"\n args = [\n \"token\", \"trending\",\n \"--chain\", chain or self.chain,\n \"--sort-by\", sort_by,\n \"--time-frame\", time_frame,\n ]\n flag_map = {\n \"market_cap_min\": \"--market-cap-min\",\n \"market_cap_max\": \"--market-cap-max\",\n \"liquidity_min\": \"--liquidity-min\",\n \"holders_min\": \"--holders-min\",\n \"volume_min\": \"--volume-min\",\n }\n for key, flag in flag_map.items():\n if key in filters:\n args.extend([flag, str(filters[key])])\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n return data.get(\"tokens\", data.get(\"data\", []))\n\n def get_batch_prices(self, token_chain_pairs: list[tuple[str, str]]) -> dict[str, Any]:\n \"\"\"\n onchainos market prices --tokens \"chainId:addr,chainId:addr,...\"\n Batch price fetch — efficient for monitoring multiple positions.\n token_chain_pairs: [(token_address, chain_name), ...]\n \"\"\"\n tokens_arg = \",\".join(\n f\"{CHAIN_IDS.get(chain, chain)}:{addr}\"\n for addr, chain in token_chain_pairs\n )\n return _run_cli(\"market\", \"prices\", \"--tokens\", tokens_arg)\n\n # ── Memepump extended ─────────────────────────────────────────\n\n def get_memepump_tokens(\n self,\n stage: str = \"all\",\n **filters: Any,\n ) -> list[dict[str, Any]]:\n \"\"\"\n onchainos memepump tokens --chain \u003cchain> --stage \u003cstage> [filters...]\n stage: all | bonding | graduated\n filters: max_market_cap, min_holders, max_bundlers_percent, max_dev_holdings_percent,\n max_top10_holdings_percent, max_insiders_percent, max_snipers_percent,\n max_fresh_wallets_percent, protocol_id_list\n Used by scan_live.py and AI Whale for pump.fun token scanning.\n \"\"\"\n args = [\n \"memepump\", \"tokens\",\n \"--chain\", self.chain,\n \"--stage\", stage,\n ]\n flag_map = {\n \"max_market_cap\": \"--max-market-cap\",\n \"min_holders\": \"--min-holders\",\n \"max_bundlers_percent\": \"--max-bundlers-percent\",\n \"max_dev_holdings_percent\": \"--max-dev-holdings-percent\",\n \"max_top10_holdings_percent\": \"--max-top10-holdings-percent\",\n \"max_insiders_percent\": \"--max-insiders-percent\",\n \"max_snipers_percent\": \"--max-snipers-percent\",\n \"max_fresh_wallets_percent\": \"--max-fresh-wallets-percent\",\n \"protocol_id_list\": \"--protocol-id-list\",\n }\n for key, flag in flag_map.items():\n if key in filters:\n args.extend([flag, str(filters[key])])\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n return data.get(\"tokens\", data.get(\"data\", []))\n\n def get_memepump_token_details(\n self, token: str, wallet: str | None = None\n ) -> dict[str, Any]:\n \"\"\"\n onchainos memepump token-details --chain \u003cchain> --address \u003ctoken>\n [--wallet-address \u003cwallet>]\n Full token details: bonding curve progress, holder breakdown, social links.\n \"\"\"\n args = [\"memepump\", \"token-details\", \"--chain\", self.chain, \"--address\", token]\n if wallet:\n args.extend([\"--wallet-address\", wallet])\n return _run_cli(*args)\n\n def get_aped_wallets(self, token: str) -> list[dict[str, Any]]:\n \"\"\"\n onchainos memepump aped-wallet --chain \u003cchain> --address \u003ctoken>\n Returns wallets that co-invested (same group / coordinated buyers).\n Useful for detecting wash trading clusters.\n \"\"\"\n data = _run_cli(\"memepump\", \"aped-wallet\", \"--chain\", self.chain, \"--address\", token)\n if \"error\" in data:\n return []\n return data.get(\"wallets\", data.get(\"data\", []))\n\n def get_similar_tokens(self, token: str) -> list[dict[str, Any]]:\n \"\"\"\n onchainos memepump similar-tokens --chain \u003cchain> --address \u003ctoken>\n Returns other tokens launched by the same developer.\n Used to check dev's rug history across projects.\n \"\"\"\n data = _run_cli(\"memepump\", \"similar-tokens\", \"--chain\", self.chain, \"--address\", token)\n if \"error\" in data:\n return []\n return data.get(\"tokens\", data.get(\"data\", []))\n\n # ── Tracker extended ──────────────────────────────────────────\n\n def track_kol(self, trade_type: int = 0) -> list[WalletEvent]:\n \"\"\"\n onchainos tracker activities --tracker-type kol --chain \u003cchain>\n Track KOL (Key Opinion Leader) wallet activity.\n trade_type: 0=all, 1=buy, 2=sell\n \"\"\"\n args = [\n \"tracker\", \"activities\",\n \"--tracker-type\", \"kol\",\n \"--chain\", self.chain,\n ]\n if trade_type != 0:\n args.extend([\"--trade-type\", str(trade_type)])\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n return self._parse_events(data)\n\n def track_with_filters(\n self,\n tracker_type: str = \"smart_money\",\n trade_type: int = 0,\n min_volume: float | None = None,\n max_volume: float | None = None,\n min_holders: int | None = None,\n min_market_cap: float | None = None,\n max_market_cap: float | None = None,\n min_liquidity: float | None = None,\n wallet_addresses: list[str] | None = None,\n ) -> list[WalletEvent]:\n \"\"\"\n onchainos tracker activities with full filter set.\n tracker_type: smart_money | kol | multi_address\n Covers all filter params used across AI Whale and Wallet Tracker skills.\n \"\"\"\n args = [\n \"tracker\", \"activities\",\n \"--tracker-type\", tracker_type,\n \"--chain\", self.chain,\n ]\n if trade_type != 0:\n args.extend([\"--trade-type\", str(trade_type)])\n if min_volume is not None:\n args.extend([\"--min-volume\", str(int(min_volume))])\n if max_volume is not None:\n args.extend([\"--max-volume\", str(int(max_volume))])\n if min_holders is not None:\n args.extend([\"--min-holders\", str(min_holders)])\n if min_market_cap is not None:\n args.extend([\"--min-market-cap\", str(int(min_market_cap))])\n if max_market_cap is not None:\n args.extend([\"--max-market-cap\", str(int(max_market_cap))])\n if min_liquidity is not None:\n args.extend([\"--min-liquidity\", str(int(min_liquidity))])\n if wallet_addresses:\n args.extend([\"--wallet-address\", \",\".join(wallet_addresses[:20])])\n data = _run_cli(*args)\n if \"error\" in data:\n return []\n return self._parse_events(data)\n\n # ── Cross-chain bridge (v2.5.0+) ──────────────────────────────────\n\n def cross_chain_chains(self) -> dict[str, Any]:\n \"\"\"\n onchainos cross-chain chains\n Returns supported chain pairs for cross-chain bridging.\n \"\"\"\n return _run_cli(\"cross-chain\", \"chains\")\n\n def cross_chain_quote(\n self,\n from_token: str,\n to_token: str,\n from_chain: str,\n to_chain: str,\n amount: str,\n receive_address: str | None = None,\n sort: int = 0,\n ) -> dict[str, Any]:\n \"\"\"\n onchainos cross-chain quote --from \u003c> --to \u003c> --from-chain \u003c> --to-chain \u003c> --readable-amount \u003c>\n Get a cross-chain bridge quote (read-only, no signing).\n sort: 0=optimal, 1=fastest, 2=max output\n \"\"\"\n args = [\n \"cross-chain\", \"quote\",\n \"--from\", from_token,\n \"--to\", to_token,\n \"--from-chain\", from_chain,\n \"--to-chain\", to_chain,\n \"--readable-amount\", str(amount),\n \"--sort\", str(sort),\n ]\n if receive_address:\n args.extend([\"--receive-address\", receive_address])\n return _run_cli(*args)\n\n def cross_chain_probe(\n self,\n from_chain: str,\n to_chain: str,\n amount: str = \"100\",\n ) -> dict[str, Any]:\n \"\"\"\n onchainos cross-chain probe --from-chain \u003c> --to-chain \u003c>\n Probe which common tokens (USDC/USDT/native) can be bridged between two chains.\n \"\"\"\n return _run_cli(\n \"cross-chain\", \"probe\",\n \"--from-chain\", from_chain,\n \"--to-chain\", to_chain,\n \"--readable-amount\", str(amount),\n )\n\n def cross_chain_status(self, order_id: str) -> dict[str, Any]:\n \"\"\"\n onchainos cross-chain status --order-id \u003c>\n Query the status of a cross-chain bridge order.\n \"\"\"\n return _run_cli(\"cross-chain\", \"status\", \"--order-id\", order_id)\n\n # ── Workflow (v2.5.0+) ────────────────────────────────────────────\n\n def workflow_token_research(\n self,\n address: str | None = None,\n query: str | None = None,\n chain: str | None = None,\n ) -> dict[str, Any]:\n \"\"\"\n onchainos workflow token-research --address \u003c> | --query \u003c>\n Full token due diligence: price, security, holders, signals, launchpad.\n Provide either address (contract) or query (symbol/name search).\n \"\"\"\n args = [\"workflow\", \"token-research\"]\n if address:\n args.extend([\"--address\", address])\n elif query:\n args.extend([\"--query\", query])\n if chain:\n args.extend([\"--chain\", chain])\n return _run_cli(*args, timeout=60)\n\n def workflow_smart_money(self, chain: str | None = None) -> dict[str, Any]:\n \"\"\"\n onchainos workflow smart-money\n Aggregate smart money signals by token with per-token due diligence.\n \"\"\"\n args = [\"workflow\", \"smart-money\"]\n if chain:\n args.extend([\"--chain\", chain])\n return _run_cli(*args, timeout=60)\n\n def workflow_new_tokens(\n self,\n chain: str | None = None,\n stage: str = \"MIGRATED\",\n ) -> dict[str, Any]:\n \"\"\"\n onchainos workflow new-tokens [--stage MIGRATED|MIGRATING]\n New token screening: launchpad scan + safety enrichment for top 10.\n \"\"\"\n args = [\"workflow\", \"new-tokens\", \"--stage\", stage]\n if chain:\n args.extend([\"--chain\", chain])\n return _run_cli(*args, timeout=60)\n\n def workflow_wallet_analysis(\n self,\n address: str,\n chain: str | None = None,\n ) -> dict[str, Any]:\n \"\"\"\n onchainos workflow wallet-analysis --address \u003c>\n 7d/30d wallet performance, trading behaviour, recent activity.\n \"\"\"\n args = [\"workflow\", \"wallet-analysis\", \"--address\", address]\n if chain:\n args.extend([\"--chain\", chain])\n return _run_cli(*args, timeout=60)\n\n def workflow_portfolio(\n self,\n address: str,\n chain: str | None = None,\n chains: str | None = None,\n ) -> dict[str, Any]:\n \"\"\"\n onchainos workflow portfolio --address \u003c>\n Portfolio check: balances, total value, 30d PnL overview.\n chains: comma-separated list of chains (defaults to all supported).\n \"\"\"\n args = [\"workflow\", \"portfolio\", \"--address\", address]\n if chain:\n args.extend([\"--chain\", chain])\n if chains:\n args.extend([\"--chains\", chains])\n return _run_cli(*args, timeout=60)\n","content_type":"text/x-python; charset=utf-8","language":"python","size":41330,"content_sha256":"60ea75481c82a6cfd6bf517dcc2109239d590887330aa0d6e99e20d4c46fae58"},{"filename":"paper_gate.py","content":"\"\"\"\nPaper-trade graduation gate for live_only strategies.\n\nTracks per-strategy:\n - paper_count: completed paper trades\n - live_micro_count: completed live micro-trades (small size)\n - days_observed: calendar days since first paper trade\n - harness_breaches: number of times harness caught a violation\n\nUnlocks full sizing when:\n >= 10 paper trades + >= 5 live micro-trades + >= 7 days + 0 harness breaches\n\"\"\"\nfrom __future__ import annotations\n\nimport json\nimport time\nfrom dataclasses import dataclass, field, asdict\nfrom pathlib import Path\nfrom typing import Any\n\n\nGATE_DIR = Path(__file__).parent / \".paper_gate\"\n\n# Graduation thresholds\nMIN_PAPER_TRADES = 10\nMIN_LIVE_MICRO_TRADES = 5\nMIN_DAYS_OBSERVED = 7\nMAX_HARNESS_BREACHES = 0\n\n\n@dataclass\nclass GateState:\n \"\"\"Persisted state for a single strategy's graduation progress.\"\"\"\n strategy_name: str = \"\"\n paper_count: int = 0\n live_micro_count: int = 0\n first_paper_ts: float = 0.0 # unix seconds\n harness_breaches: int = 0\n graduated: bool = False\n graduated_ts: float = 0.0\n\n @property\n def days_observed(self) -> float:\n if self.first_paper_ts == 0:\n return 0.0\n return (time.time() - self.first_paper_ts) / 86400\n\n @property\n def ready(self) -> bool:\n return (\n self.paper_count >= MIN_PAPER_TRADES\n and self.live_micro_count >= MIN_LIVE_MICRO_TRADES\n and self.days_observed >= MIN_DAYS_OBSERVED\n and self.harness_breaches \u003c= MAX_HARNESS_BREACHES\n )\n\n def progress_summary(self) -> dict[str, Any]:\n return {\n \"paper_trades\": f\"{self.paper_count}/{MIN_PAPER_TRADES}\",\n \"live_micro_trades\": f\"{self.live_micro_count}/{MIN_LIVE_MICRO_TRADES}\",\n \"days_observed\": f\"{self.days_observed:.1f}/{MIN_DAYS_OBSERVED}\",\n \"harness_breaches\": self.harness_breaches,\n \"graduated\": self.graduated,\n \"ready_to_graduate\": self.ready,\n }\n\n\ndef _state_path(strategy_name: str) -> Path:\n GATE_DIR.mkdir(parents=True, exist_ok=True)\n return GATE_DIR / f\"{strategy_name}.json\"\n\n\ndef load_state(strategy_name: str) -> GateState:\n path = _state_path(strategy_name)\n if not path.exists():\n return GateState(strategy_name=strategy_name)\n with open(path) as f:\n data = json.load(f)\n return GateState(**data)\n\n\ndef save_state(state: GateState) -> None:\n path = _state_path(state.strategy_name)\n with open(path, \"w\") as f:\n json.dump(asdict(state), f, indent=2)\n\n\ndef record_paper_trade(strategy_name: str) -> GateState:\n \"\"\"Record a completed paper trade.\"\"\"\n state = load_state(strategy_name)\n if state.first_paper_ts == 0:\n state.first_paper_ts = time.time()\n state.paper_count += 1\n save_state(state)\n return state\n\n\ndef record_live_micro_trade(strategy_name: str) -> GateState:\n \"\"\"Record a completed live micro-trade.\"\"\"\n state = load_state(strategy_name)\n state.live_micro_count += 1\n save_state(state)\n return state\n\n\ndef record_harness_breach(strategy_name: str) -> GateState:\n \"\"\"Record a harness violation during paper/micro trading.\"\"\"\n state = load_state(strategy_name)\n state.harness_breaches += 1\n save_state(state)\n return state\n\n\ndef check_graduation(strategy_name: str) -> tuple[bool, dict[str, Any]]:\n \"\"\"\n Check if strategy is ready to graduate to full sizing.\n\n Returns:\n (graduated, progress_summary)\n \"\"\"\n state = load_state(strategy_name)\n if state.graduated:\n return True, state.progress_summary()\n if state.ready:\n state.graduated = True\n state.graduated_ts = time.time()\n save_state(state)\n return True, state.progress_summary()\n return False, state.progress_summary()\n\n\ndef get_allowed_size_multiplier(strategy_name: str) -> float:\n \"\"\"\n Returns sizing multiplier:\n - 0.0 if no paper trades yet (blocked)\n - 0.1 during micro-trade phase (10% of spec size)\n - 1.0 if graduated (full size)\n \"\"\"\n state = load_state(strategy_name)\n if state.graduated:\n return 1.0\n if state.paper_count >= MIN_PAPER_TRADES:\n return 0.1 # micro-trade phase\n return 0.0 # paper-only phase\n","content_type":"text/x-python; charset=utf-8","language":"python","size":4252,"content_sha256":"e2e720dfcde064f9593cd1272e62a4a89e6a680094eb3049f37d70546f968fc7"},{"filename":"plugin.yaml","content":"schema_version: 1\nname: starter-coach\nversion: \"1.0.0\"\ndescription: \"Starter Coach V2 — conversational 6-step skill that guides users to build their own automated DEX spot-trading bot on OKX DEX. Onboard → Profile → Build Strategy → Paper Trade → Go Live. Uses OnchainOS CLI for all on-chain data and execution. No freeform trading code — emits validated JSON strategy specs.\"\nauthor:\n name: \"VibeCodeDaddy\"\n github: \"VibeCodeDaddy69\"\nlicense: MIT\ncategory: strategy\ntags:\n - trading-bot\n - strategy-builder\n - coaching\n - paper-trade\n - backtesting\n - dex\n - onchainos\n - solana\n - ethereum\ncomponents:\n skill:\n dir: .\napi_calls: []\ntype: community-developer\n","content_type":"application/yaml; charset=utf-8","language":"yaml","size":687,"content_sha256":"7583187866acb78651c5b8fb054bd8ec55b6bfce11d82baf2606e492183fb0bb"},{"filename":"primitives/__init__.py","content":"\"\"\"Starter Coach V2 — Primitive evaluators.\"\"\"\n","content_type":"text/x-python; charset=utf-8","language":"python","size":49,"content_sha256":"ed66424c4935ba650b9ea7fffc96c1cb961acc1b865c43648ad7f65013ffccc0"},{"filename":"primitives/entry.py","content":"\"\"\"\nEntry trigger evaluators — 12 primitives.\n\nEach function: evaluate_\u003ctype>(ctx, params) -> bool\n ctx: MarketContext with OHLCV bars, indicators, OnchainOS data\n params: dict from the spec's entry block (minus the \"type\" key)\n\"\"\"\nfrom __future__ import annotations\n\nfrom dataclasses import dataclass, field\nfrom typing import Any\nimport time as _time\n\nfrom onchainos import OnchainOS, WalletEvent, RankingItem\n\n\n@dataclass\nclass Bar:\n ts: float # unix seconds\n open: float\n high: float\n low: float\n close: float\n volume: float\n\n\n@dataclass\nclass MarketContext:\n \"\"\"Shared context passed to all evaluators.\"\"\"\n bars: list[Bar] = field(default_factory=list) # newest last\n current_price: float = 0.0\n timeframe: str = \"5m\"\n chain: str = \"solana\"\n token: str = \"\"\n # Pre-computed indicators (populated by engine before eval)\n ema: dict[int, list[float]] = field(default_factory=dict) # period -> values\n sma: dict[int, list[float]] = field(default_factory=dict)\n rsi: dict[int, list[float]] = field(default_factory=dict) # period -> values\n macd: dict[str, list[float]] = field(default_factory=dict) # \"macd\"/\"signal\"/\"hist\"\n bbands: dict[str, list[float]] = field(default_factory=dict) # \"upper\"/\"middle\"/\"lower\"\n # OnchainOS live data (populated for live_only triggers)\n onchainos: OnchainOS | None = None\n wallet_events: list[WalletEvent] = field(default_factory=list)\n ranking_items: list[RankingItem] = field(default_factory=list)\n\n\n# ── Backtestable entries ─────────────────────────────────────────────────────\n\ndef evaluate_price_drop(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-01: Fire when price drops pct% from lookback high.\"\"\"\n pct = params[\"pct\"]\n lookback = params[\"lookback_bars\"]\n if len(ctx.bars) \u003c lookback:\n return False\n window = ctx.bars[-lookback:]\n high = max(b.high for b in window)\n if high == 0:\n return False\n drop = (high - ctx.current_price) / high * 100\n return drop >= pct\n\n\ndef evaluate_price_breakout(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-02: Fire when price breaks above lookback high (or below low).\"\"\"\n direction = params[\"direction\"]\n lookback = params[\"lookback_bars\"]\n confirm_pct = params.get(\"confirm_pct\", 0)\n if len(ctx.bars) \u003c lookback + 1:\n return False\n window = ctx.bars[-(lookback + 1):-1] # exclude current bar\n if direction == \"up\":\n level = max(b.high for b in window)\n threshold = level * (1 + confirm_pct / 100)\n return ctx.current_price > threshold\n else:\n level = min(b.low for b in window)\n threshold = level * (1 - confirm_pct / 100)\n return ctx.current_price \u003c threshold\n\n\ndef evaluate_ma_cross(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-03: Fire when fast MA crosses above slow MA (golden cross).\"\"\"\n fast_p = params[\"fast_period\"]\n slow_p = params[\"slow_period\"]\n ma_type = params.get(\"ma_type\", \"EMA\")\n source = ctx.ema if ma_type == \"EMA\" else ctx.sma\n fast = source.get(fast_p, [])\n slow = source.get(slow_p, [])\n if len(fast) \u003c 2 or len(slow) \u003c 2:\n return False\n # Cross: prev fast \u003c= slow, now fast > slow\n return fast[-2] \u003c= slow[-2] and fast[-1] > slow[-1]\n\n\ndef evaluate_rsi_threshold(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-04: Fire when RSI crosses above/below a level.\"\"\"\n period = params[\"period\"]\n level = params[\"level\"]\n direction = params[\"direction\"]\n rsi = ctx.rsi.get(period, [])\n if len(rsi) \u003c 2:\n return False\n if direction == \"cross_up\":\n return rsi[-2] \u003c= level and rsi[-1] > level\n else: # cross_down\n return rsi[-2] >= level and rsi[-1] \u003c level\n\n\ndef evaluate_volume_spike(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-05: Fire when current bar volume > multiplier * rolling average.\"\"\"\n multiplier = params[\"multiplier\"]\n avg_bars = params[\"avg_bars\"]\n if len(ctx.bars) \u003c avg_bars + 1:\n return False\n avg_window = ctx.bars[-(avg_bars + 1):-1]\n avg_vol = sum(b.volume for b in avg_window) / len(avg_window)\n if avg_vol == 0:\n return False\n return ctx.bars[-1].volume > multiplier * avg_vol\n\n\ndef evaluate_time_schedule(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-06: Fire on fixed cadence. Engine calls this once per interval tick.\"\"\"\n # The engine is responsible for calling this at the right time\n # based on interval and anchor_utc. When called, it always fires.\n return True\n\n\ndef evaluate_macd_cross(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-09: Fire when MACD line crosses signal line.\"\"\"\n direction = params[\"direction\"]\n macd_line = ctx.macd.get(\"macd\", [])\n signal_line = ctx.macd.get(\"signal\", [])\n if len(macd_line) \u003c 2 or len(signal_line) \u003c 2:\n return False\n if direction == \"cross_up\":\n return macd_line[-2] \u003c= signal_line[-2] and macd_line[-1] > signal_line[-1]\n else:\n return macd_line[-2] >= signal_line[-2] and macd_line[-1] \u003c signal_line[-1]\n\n\ndef evaluate_bollinger_touch(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-10: Fire when price touches upper or lower Bollinger Band.\"\"\"\n band = params[\"band\"]\n band_key = band # \"upper\" or \"lower\"\n bb = ctx.bbands.get(band_key, [])\n if not bb:\n return False\n if band == \"lower\":\n return ctx.current_price \u003c= bb[-1]\n else:\n return ctx.current_price >= bb[-1]\n\n\n# ── Live-only entries ────────────────────────────────────────────────────────\n\ndef evaluate_smart_money_buy(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-07: Fire when >= N smart-money wallets buy within window. Live-only.\"\"\"\n min_wallets = params[\"min_wallets\"]\n window_min = params[\"window_min\"]\n min_usd = params.get(\"min_usd_each\", 0)\n now = _time.time()\n cutoff = now - window_min * 60\n sm_buys: set[str] = set()\n for ev in ctx.wallet_events:\n if ev.side == \"buy\" and ev.timestamp >= cutoff and ev.usd_amount >= min_usd:\n sm_buys.add(ev.wallet)\n return len(sm_buys) >= min_wallets\n\n\ndef evaluate_dev_buy(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-08: Fire when deployer wallet buys above threshold. Live-only.\"\"\"\n min_usd = params[\"min_usd\"]\n window_min = params.get(\"window_min\", 1440)\n now = _time.time()\n cutoff = now - window_min * 60\n for ev in ctx.wallet_events:\n if ev.side == \"buy\" and ev.timestamp >= cutoff and ev.usd_amount >= min_usd:\n # Engine must pre-filter wallet_events to dev wallet only\n return True\n return False\n\n\ndef evaluate_ranking_entry(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-11: Fire when token enters top-N of ranking list. Live-only.\"\"\"\n top_n = params[\"top_n\"]\n for item in ctx.ranking_items:\n if item.rank \u003c= top_n:\n return True\n return False\n\n\ndef evaluate_wallet_copy_buy(ctx: MarketContext, params: dict) -> bool:\n \"\"\"E-12: Fire when tracked wallet buys above threshold. Live-only.\"\"\"\n min_usd = params[\"min_usd\"]\n for ev in ctx.wallet_events:\n if ev.side == \"buy\" and ev.usd_amount >= min_usd:\n return True\n return False\n\n\n# ── Dispatcher ───────────────────────────────────────────────────────────────\n\nENTRY_EVALUATORS: dict[str, Any] = {\n \"price_drop\": evaluate_price_drop,\n \"price_breakout\": evaluate_price_breakout,\n \"ma_cross\": evaluate_ma_cross,\n \"rsi_threshold\": evaluate_rsi_threshold,\n \"volume_spike\": evaluate_volume_spike,\n \"time_schedule\": evaluate_time_schedule,\n \"smart_money_buy\": evaluate_smart_money_buy,\n \"dev_buy\": evaluate_dev_buy,\n \"macd_cross\": evaluate_macd_cross,\n \"bollinger_touch\": evaluate_bollinger_touch,\n \"ranking_entry\": evaluate_ranking_entry,\n \"wallet_copy_buy\": evaluate_wallet_copy_buy,\n}\n\n\ndef evaluate_entry(ctx: MarketContext, entry_spec: dict) -> bool:\n \"\"\"Dispatch to the correct entry evaluator.\"\"\"\n entry_type = entry_spec[\"type\"]\n params = {k: v for k, v in entry_spec.items() if k != \"type\"}\n evaluator = ENTRY_EVALUATORS.get(entry_type)\n if evaluator is None:\n raise ValueError(f\"Unknown entry type: {entry_type}\")\n return evaluator(ctx, params)\n","content_type":"text/x-python; charset=utf-8","language":"python","size":8599,"content_sha256":"fc5e3f77aaaf3ac88d6b4f91cce62a4d2aa0652afd7ef43fc9c0fbb5dc646b18"},{"filename":"primitives/exit.py","content":"\"\"\"\nExit condition evaluators — 10 primitives.\n\nG-01: All exits evaluated in parallel every tick. First-to-fire wins.\n On same-tick tie, stop_loss wins (fail-safe).\n\nEach function: evaluate_\u003ctype>(ctx, position, params) -> ExitSignal | None\n\"\"\"\nfrom __future__ import annotations\n\nfrom dataclasses import dataclass\nfrom typing import Any\nimport time as _time\n\nfrom primitives.entry import MarketContext\n\n\n@dataclass\nclass Position:\n \"\"\"Open position state.\"\"\"\n token: str = \"\"\n entry_price: float = 0.0\n entry_ts: float = 0.0 # unix seconds\n entry_bar_idx: int = 0 # bar index at entry\n size_usd: float = 0.0\n peak_price: float = 0.0 # highest price since entry (for trailing)\n tiers_sold: list[int] = None # indices of tiered TP levels already sold\n\n def __post_init__(self):\n if self.tiers_sold is None:\n self.tiers_sold = []\n if self.peak_price == 0:\n self.peak_price = self.entry_price\n\n\n@dataclass\nclass ExitSignal:\n \"\"\"Returned by an exit evaluator when it fires.\"\"\"\n reason: str # primitive type name\n sell_pct: float = 100 # % of position to sell (100 = full close)\n priority: int = 0 # lower = higher priority. stop_loss = 0.\n\n\n# ── Inner exits ──────────────────────────────────────────────────────────────\n\ndef evaluate_stop_loss(ctx: MarketContext, pos: Position, params: dict) -> ExitSignal | None:\n \"\"\"X-01: Fixed % loss from entry. Always required.\"\"\"\n pct = params[\"pct\"]\n if pos.entry_price == 0:\n return None\n loss = (pos.entry_price - ctx.current_price) / pos.entry_price * 100\n if loss >= pct:\n return ExitSignal(reason=\"stop_loss\", sell_pct=100, priority=0)\n return None\n\n\ndef evaluate_take_profit(ctx: MarketContext, pos: Position, params: dict) -> ExitSignal | None:\n \"\"\"X-02: Fixed % gain from entry.\"\"\"\n pct = params[\"pct\"]\n if pos.entry_price == 0:\n return None\n gain = (ctx.current_price - pos.entry_price) / pos.entry_price * 100\n if gain >= pct:\n return ExitSignal(reason=\"take_profit\", sell_pct=100, priority=1)\n return None\n\n\ndef evaluate_trailing_stop(ctx: MarketContext, pos: Position, params: dict) -> ExitSignal | None:\n \"\"\"X-03: Stop trails pct% below peak price since entry.\"\"\"\n pct = params[\"pct\"]\n activate_after = params.get(\"activate_after_pct\", 0)\n if pos.entry_price == 0:\n return None\n # Update peak\n if ctx.current_price > pos.peak_price:\n pos.peak_price = ctx.current_price\n # Check activation threshold\n gain_from_entry = (pos.peak_price - pos.entry_price) / pos.entry_price * 100\n if gain_from_entry \u003c activate_after:\n return None\n # Check trailing stop\n drop_from_peak = (pos.peak_price - ctx.current_price) / pos.peak_price * 100\n if drop_from_peak >= pct:\n return ExitSignal(reason=\"trailing_stop\", sell_pct=100, priority=1)\n return None\n\n\ndef evaluate_tiered_take_profit(ctx: MarketContext, pos: Position, params: dict) -> ExitSignal | None:\n \"\"\"X-04: Multi-level TP ladder. Sells portions at different profit levels.\"\"\"\n tiers = params[\"tiers\"]\n if pos.entry_price == 0:\n return None\n gain = (ctx.current_price - pos.entry_price) / pos.entry_price * 100\n for i, tier in enumerate(tiers):\n if i in pos.tiers_sold:\n continue\n if gain >= tier[\"pct_gain\"]:\n pos.tiers_sold.append(i)\n return ExitSignal(\n reason=\"tiered_take_profit\",\n sell_pct=tier[\"pct_sell\"],\n priority=1,\n )\n return None\n\n\n# ── Other exits (go in exit.other[]) ─────────────────────────────────────────\n\ndef evaluate_time_exit(ctx: MarketContext, pos: Position, params: dict) -> ExitSignal | None:\n \"\"\"X-05: Exit after max_bars from entry (G-06).\"\"\"\n max_bars = params[\"max_bars\"]\n bars_held = len(ctx.bars) - 1 - pos.entry_bar_idx\n if bars_held >= max_bars:\n return ExitSignal(reason=\"time_exit\", sell_pct=100, priority=2)\n return None\n\n\ndef evaluate_indicator_reversal(ctx: MarketContext, pos: Position, params: dict) -> ExitSignal | None:\n \"\"\"X-06: Exit when entry indicator flips. Engine checks entry signal == False.\"\"\"\n # The engine re-evaluates the entry condition. If mirror_entry is true\n # and the entry signal is now False (reversed), exit.\n # This is a meta-evaluator — the engine must wire this up.\n return None # Engine handles via entry re-evaluation\n\n\ndef evaluate_smart_money_sell(ctx: MarketContext, pos: Position, params: dict) -> ExitSignal | None:\n \"\"\"X-07: Exit when >= N smart-money wallets sell. Live-only.\"\"\"\n min_wallets = params[\"min_wallets\"]\n window_min = params[\"window_min\"]\n now = _time.time()\n cutoff = now - window_min * 60\n sm_sells: set[str] = set()\n for ev in ctx.wallet_events:\n if ev.side == \"sell\" and ev.token == pos.token and ev.timestamp >= cutoff:\n sm_sells.add(ev.wallet)\n if len(sm_sells) >= min_wallets:\n return ExitSignal(reason=\"smart_money_sell\", sell_pct=100, priority=1)\n return None\n\n\ndef evaluate_dev_dump(ctx: MarketContext, pos: Position, params: dict) -> ExitSignal | None:\n \"\"\"X-08: Immediate exit when deployer dumps. Live-only, market-order priority.\"\"\"\n min_usd = params[\"min_usd\"]\n for ev in ctx.wallet_events:\n if ev.side == \"sell\" and ev.token == pos.token and ev.usd_amount >= min_usd:\n return ExitSignal(reason=\"dev_dump\", sell_pct=100, priority=0)\n return None\n\n\ndef evaluate_wallet_mirror_sell(ctx: MarketContext, pos: Position, params: dict) -> ExitSignal | None:\n \"\"\"X-09: Exit when copied wallet sells. Live-only.\"\"\"\n target = params[\"target_wallet\"]\n min_pct = params.get(\"min_pct_sold\", 10)\n wallets = [target] if isinstance(target, str) else target\n for ev in ctx.wallet_events:\n if ev.side == \"sell\" and ev.wallet in wallets and ev.token == pos.token:\n return ExitSignal(reason=\"wallet_mirror_sell\", sell_pct=100, priority=1)\n return None\n\n\ndef evaluate_fast_dump_exit(ctx: MarketContext, pos: Position, params: dict) -> ExitSignal | None:\n \"\"\"X-10: Emergency exit if price drops X% within N seconds. Live-only.\"\"\"\n drop_pct = params[\"drop_pct\"]\n window_sec = params[\"window_sec\"]\n if len(ctx.bars) \u003c 2:\n return None\n # Look at recent bars within the window\n now_ts = ctx.bars[-1].ts\n cutoff_ts = now_ts - window_sec\n recent_high = ctx.current_price\n for bar in reversed(ctx.bars):\n if bar.ts \u003c cutoff_ts:\n break\n if bar.high > recent_high:\n recent_high = bar.high\n if recent_high == 0:\n return None\n drop = (recent_high - ctx.current_price) / recent_high * 100\n if drop >= drop_pct:\n return ExitSignal(reason=\"fast_dump_exit\", sell_pct=100, priority=0)\n return None\n\n\n# ── Dispatcher ───────────────────────────────────────────────────────────────\n\nINNER_EXIT_EVALUATORS: dict[str, Any] = {\n \"stop_loss\": evaluate_stop_loss,\n \"take_profit\": evaluate_take_profit,\n \"trailing_stop\": evaluate_trailing_stop,\n \"tiered_take_profit\": evaluate_tiered_take_profit,\n}\n\nOTHER_EXIT_EVALUATORS: dict[str, Any] = {\n \"time_exit\": evaluate_time_exit,\n \"indicator_reversal\": evaluate_indicator_reversal,\n \"smart_money_sell\": evaluate_smart_money_sell,\n \"dev_dump\": evaluate_dev_dump,\n \"wallet_mirror_sell\": evaluate_wallet_mirror_sell,\n \"fast_dump_exit\": evaluate_fast_dump_exit,\n}\n\n\ndef evaluate_all_exits(\n ctx: MarketContext, pos: Position, exit_spec: dict\n) -> ExitSignal | None:\n \"\"\"\n G-01: Evaluate all exits in parallel. Return the first-to-fire.\n On tie, lowest priority value wins (stop_loss = 0).\n \"\"\"\n signals: list[ExitSignal] = []\n\n # Inner exits\n for key, evaluator in INNER_EXIT_EVALUATORS.items():\n if key in exit_spec:\n sig = evaluator(ctx, pos, exit_spec[key])\n if sig:\n signals.append(sig)\n\n # Other exits\n for other in exit_spec.get(\"other\", []):\n etype = other.get(\"type\", \"\")\n evaluator = OTHER_EXIT_EVALUATORS.get(etype)\n if evaluator:\n params = {k: v for k, v in other.items() if k != \"type\"}\n sig = evaluator(ctx, pos, params)\n if sig:\n signals.append(sig)\n\n if not signals:\n return None\n\n # G-01: On tie, lowest priority wins (stop_loss priority=0 always wins)\n signals.sort(key=lambda s: s.priority)\n return signals[0]\n","content_type":"text/x-python; charset=utf-8","language":"python","size":8888,"content_sha256":"5538e460a5c7cc77359b30871cad392210d98e1f53f81a3ae49ce0f417db0a79"},{"filename":"primitives/filter.py","content":"\"\"\"\nFilter evaluators — 10 market-condition + 13 token-safety = 23 primitives.\n\nEach function: evaluate_\u003ctype>(ctx, params) -> bool\n True = filter passes (entry allowed), False = filter blocks entry.\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import Any\nfrom datetime import datetime, timezone\n\nfrom primitives.entry import MarketContext\nfrom onchainos import SafetyTags\n\n\n# ── Market-condition filters ─────────────────────────────────────────────────\n\ndef evaluate_time_window(ctx: MarketContext, params: dict) -> bool:\n \"\"\"MF-01: Only trade during specified UTC hours.\"\"\"\n start = params[\"start_hour\"]\n end = params[\"end_hour\"]\n weekdays_only = params.get(\"weekdays_only\", False)\n now = datetime.now(timezone.utc)\n hour = now.hour\n day = now.weekday() # 0=Mon, 6=Sun\n if weekdays_only and day >= 5:\n return False\n if start \u003c= end:\n return start \u003c= hour \u003c end\n else: # wraps midnight\n return hour >= start or hour \u003c end\n\n\ndef evaluate_volatility_range(ctx: MarketContext, params: dict) -> bool:\n \"\"\"MF-02: Only trade when ATR% is in [min, max].\"\"\"\n atr_period = params[\"atr_period\"]\n min_pct = params.get(\"min_pct\", 0)\n max_pct = params.get(\"max_pct\", 999)\n if len(ctx.bars) \u003c atr_period + 1:\n return False\n # Compute ATR\n trs: list[float] = []\n for i in range(-atr_period, 0):\n bar = ctx.bars[i]\n prev = ctx.bars[i - 1]\n tr = max(bar.high - bar.low, abs(bar.high - prev.close), abs(bar.low - prev.close))\n trs.append(tr)\n atr = sum(trs) / len(trs)\n atr_pct = (atr / ctx.current_price * 100) if ctx.current_price > 0 else 0\n return min_pct \u003c= atr_pct \u003c= max_pct\n\n\ndef evaluate_volume_minimum(ctx: MarketContext, params: dict) -> bool:\n \"\"\"MF-03: Only trade when 24h volume > threshold.\"\"\"\n min_usd = params[\"min_usd_24h\"]\n # Approximate: sum volume of recent bars covering ~24h\n # Engine should provide a pre-computed 24h volume if available\n if not ctx.bars:\n return False\n # Heuristic: sum all available bar volumes as approximation\n total = sum(b.volume for b in ctx.bars[-288:]) # 288 x 5m = 24h\n return total >= min_usd\n\n\ndef evaluate_cooldown(ctx: MarketContext, params: dict) -> bool:\n \"\"\"MF-04: Wait N bars after last trade close.\n Engine must track last_trade_bar_idx in ctx and pass it here.\n For now, always passes — engine enforces.\n \"\"\"\n return True # Engine tracks cooldown state externally\n\n\ndef evaluate_market_regime(ctx: MarketContext, params: dict) -> bool:\n \"\"\"MF-05: Only trade in specified regime (by MA slope).\"\"\"\n regime = params[\"regime\"]\n if regime == \"any\":\n return True\n ma_period = params.get(\"ma_period\", 200)\n sma = ctx.sma.get(ma_period, [])\n if len(sma) \u003c 2:\n return False\n slope = sma[-1] - sma[-2]\n if regime == \"up\":\n return slope > 0\n elif regime == \"down\":\n return slope \u003c 0\n else: # range\n return abs(slope) / (sma[-1] if sma[-1] > 0 else 1) \u003c 0.001\n\n\ndef evaluate_price_range(ctx: MarketContext, params: dict) -> bool:\n \"\"\"MF-06: Only trade when price in [min, max] USD.\"\"\"\n min_p = params.get(\"min_price\", 0)\n max_p = params.get(\"max_price\", float(\"inf\"))\n return min_p \u003c= ctx.current_price \u003c= max_p\n\n\ndef evaluate_btc_overlay(ctx: MarketContext, params: dict) -> bool:\n \"\"\"MF-07: Only trade alts when BTC is favorable.\n Engine must provide BTC data in ctx. Stub: always passes.\n \"\"\"\n # TODO: engine provides BTC MA / candle data\n return True\n\n\ndef evaluate_top_zone_guard(ctx: MarketContext, params: dict) -> bool:\n \"\"\"MF-08: Don't buy if price is in top X% of recent range.\"\"\"\n max_zone_pct = params[\"max_zone_pct\"]\n lookback = params[\"lookback_bars\"]\n if len(ctx.bars) \u003c lookback:\n return False\n window = ctx.bars[-lookback:]\n hi = max(b.high for b in window)\n lo = min(b.low for b in window)\n if hi == lo:\n return True\n zone_pct = (ctx.current_price - lo) / (hi - lo) * 100\n return zone_pct \u003c= max_zone_pct\n\n\ndef evaluate_mcap_range(ctx: MarketContext, params: dict) -> bool:\n \"\"\"MF-09: Only trade when token mcap in range. Live-only (OnchainOS).\"\"\"\n min_usd = params.get(\"min_usd\", 0)\n max_usd = params.get(\"max_usd\", float(\"inf\"))\n if ctx.onchainos is None:\n return True # Can't check without OnchainOS\n tags = ctx.onchainos.get_safety_tags(ctx.token)\n return min_usd \u003c= tags.mcap_usd \u003c= max_usd\n\n\ndef evaluate_launch_age(ctx: MarketContext, params: dict) -> bool:\n \"\"\"MF-10: Filter by token age since launch. Live-only (OnchainOS).\"\"\"\n min_h = params.get(\"min_hours\", 0)\n max_h = params.get(\"max_hours\", float(\"inf\"))\n if ctx.onchainos is None:\n return True\n tags = ctx.onchainos.get_safety_tags(ctx.token)\n return min_h \u003c= tags.launch_age_hours \u003c= max_h\n\n\n# ── Token-safety filters (all live_only, all OnchainOS-backed) ───────────────\n\ndef _get_tags(ctx: MarketContext) -> SafetyTags | None:\n if ctx.onchainos is None:\n return None\n return ctx.onchainos.get_safety_tags(ctx.token)\n\n\ndef evaluate_honeypot_check(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-01: Reject if honeypot.\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False # Fail-safe: block if we can't check\n return not tags.honeypot\n\n\ndef evaluate_lp_locked(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-02: LP must be locked above threshold.\"\"\"\n min_pct = params[\"min_pct_locked\"]\n min_days = params.get(\"min_lock_days\", 0)\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return tags.lp_locked_pct >= min_pct and tags.lp_lock_days >= min_days\n\n\ndef evaluate_buy_tax_max(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-03: Reject if buy tax exceeds threshold.\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return tags.buy_tax_pct \u003c= params[\"max_pct\"]\n\n\ndef evaluate_sell_tax_max(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-04: Reject if sell tax exceeds threshold.\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return tags.sell_tax_pct \u003c= params[\"max_pct\"]\n\n\ndef evaluate_liquidity_min(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-05: Minimum pool liquidity.\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return tags.liquidity_usd >= params[\"min_usd\"]\n\n\ndef evaluate_top_holders_max(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-06: Reject if top-N holders too concentrated.\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return tags.top_holders_pct \u003c= params[\"max_pct\"]\n\n\ndef evaluate_bundler_ratio_max(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-07: Reject if bundler ratio too high.\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return tags.bundler_ratio_pct \u003c= params[\"max_pct\"]\n\n\ndef evaluate_dev_holding_max(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-08: Reject if dev holds too much.\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return tags.dev_holding_pct \u003c= params[\"max_pct\"]\n\n\ndef evaluate_insider_holding_max(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-09: Reject if insiders hold too much.\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return tags.insider_holding_pct \u003c= params[\"max_pct\"]\n\n\ndef evaluate_fresh_wallet_ratio_max(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-10: Reject if too many fresh wallets.\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return tags.fresh_wallet_ratio_pct \u003c= params[\"max_pct\"]\n\n\ndef evaluate_smart_money_present_min(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-11: Require >= N smart-money wallets currently holding. State check (G-05).\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return tags.smart_money_count >= params[\"min_wallets\"]\n\n\ndef evaluate_phishing_exclude(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-12: Reject if on phishing blacklist.\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return not tags.phishing_flagged\n\n\ndef evaluate_whale_concentration_max(ctx: MarketContext, params: dict) -> bool:\n \"\"\"TF-13: Reject if single whale too large.\"\"\"\n tags = _get_tags(ctx)\n if tags is None:\n return False\n return tags.whale_max_pct \u003c= params[\"max_pct\"]\n\n\n# ── Dispatcher ───────────────────────────────────────────────────────────────\n\nFILTER_EVALUATORS: dict[str, Any] = {\n \"time_window\": evaluate_time_window,\n \"volatility_range\": evaluate_volatility_range,\n \"volume_minimum\": evaluate_volume_minimum,\n \"cooldown\": evaluate_cooldown,\n \"market_regime\": evaluate_market_regime,\n \"price_range\": evaluate_price_range,\n \"btc_overlay\": evaluate_btc_overlay,\n \"top_zone_guard\": evaluate_top_zone_guard,\n \"mcap_range\": evaluate_mcap_range,\n \"launch_age\": evaluate_launch_age,\n \"honeypot_check\": evaluate_honeypot_check,\n \"lp_locked\": evaluate_lp_locked,\n \"buy_tax_max\": evaluate_buy_tax_max,\n \"sell_tax_max\": evaluate_sell_tax_max,\n \"liquidity_min\": evaluate_liquidity_min,\n \"top_holders_max\": evaluate_top_holders_max,\n \"bundler_ratio_max\": evaluate_bundler_ratio_max,\n \"dev_holding_max\": evaluate_dev_holding_max,\n \"insider_holding_max\": evaluate_insider_holding_max,\n \"fresh_wallet_ratio_max\": evaluate_fresh_wallet_ratio_max,\n \"smart_money_present_min\": evaluate_smart_money_present_min,\n \"phishing_exclude\": evaluate_phishing_exclude,\n \"whale_concentration_max\": evaluate_whale_concentration_max,\n}\n\n\ndef evaluate_filters(ctx: MarketContext, filters: list[dict]) -> tuple[bool, list[str]]:\n \"\"\"\n Evaluate all filters. ALL must pass for entry to be allowed.\n Returns (all_pass, list_of_failing_filter_types).\n \"\"\"\n failures: list[str] = []\n for filt in filters:\n ftype = filt.get(\"type\", \"\")\n evaluator = FILTER_EVALUATORS.get(ftype)\n if evaluator is None:\n failures.append(f\"unknown:{ftype}\")\n continue\n params = {k: v for k, v in filt.items() if k != \"type\"}\n if not evaluator(ctx, params):\n failures.append(ftype)\n return len(failures) == 0, failures\n","content_type":"text/x-python; charset=utf-8","language":"python","size":10590,"content_sha256":"ae9442e8aeacb7c621dba8cca9af53c762ddf3690ef28bffb91899366368fac3"},{"filename":"primitives/risk.py","content":"\"\"\"\nRisk overlay evaluators — 5 primitives.\n\nEach function: check_overlay(state, params) -> bool\n True = OK to proceed, False = blocked by overlay.\n\"\"\"\nfrom __future__ import annotations\n\nfrom dataclasses import dataclass, field\nfrom typing import Any\nimport time as _time\n\n\n@dataclass\nclass PortfolioState:\n \"\"\"Tracked by the engine across trades.\"\"\"\n trades_today: int = 0\n open_positions: int = 0\n open_tokens: list[str] = field(default_factory=list)\n equity_usd: float = 0.0\n peak_equity_usd: float = 0.0\n consecutive_losses: int = 0\n session_start_ts: float = 0.0\n\n\ndef check_max_daily_trades(state: PortfolioState, params: dict) -> bool:\n \"\"\"R-01: Hard cap on entries per 24h.\"\"\"\n return state.trades_today \u003c params[\"n\"]\n\n\ndef check_max_concurrent_positions(state: PortfolioState, params: dict) -> bool:\n \"\"\"R-02: Max open positions at once.\"\"\"\n return state.open_positions \u003c params[\"n\"]\n\n\ndef check_drawdown_pause(state: PortfolioState, params: dict) -> bool:\n \"\"\"R-03: Pause if equity drawdown exceeds threshold.\"\"\"\n pause_pct = params[\"pause_pct\"]\n if state.peak_equity_usd == 0:\n return True\n dd = (state.peak_equity_usd - state.equity_usd) / state.peak_equity_usd * 100\n return dd \u003c pause_pct\n\n\ndef check_correlation_cap(state: PortfolioState, params: dict) -> bool:\n \"\"\"R-04: v1.0 same_token_dedupe only. Prevent duplicate token positions.\"\"\"\n max_corr = params[\"max_correlated\"]\n # Count how many open positions share the same token\n from collections import Counter\n counts = Counter(state.open_tokens)\n for token, count in counts.items():\n if count >= max_corr:\n return False\n return True\n\n\ndef check_session_loss_pause(state: PortfolioState, params: dict) -> bool:\n \"\"\"R-05: Pause after N consecutive losses.\"\"\"\n max_losses = params[\"max_consecutive_losses\"]\n session_hours = params.get(\"session_hours\", 24)\n # Check if we're still in the session window\n now = _time.time()\n if now - state.session_start_ts > session_hours * 3600:\n # Session expired, reset\n state.consecutive_losses = 0\n state.session_start_ts = now\n return True\n return state.consecutive_losses \u003c max_losses\n\n\nRISK_EVALUATORS: dict[str, Any] = {\n \"max_daily_trades\": check_max_daily_trades,\n \"max_concurrent_positions\": check_max_concurrent_positions,\n \"drawdown_pause\": check_drawdown_pause,\n \"correlation_cap\": check_correlation_cap,\n \"session_loss_pause\": check_session_loss_pause,\n}\n\n\ndef check_all_overlays(\n state: PortfolioState, overlays: list[dict]\n) -> tuple[bool, list[str]]:\n \"\"\"\n Check all risk overlays. ALL must pass.\n Returns (all_pass, list_of_blocking_overlay_types).\n \"\"\"\n blockers: list[str] = []\n for overlay in overlays:\n otype = overlay.get(\"type\", \"\")\n checker = RISK_EVALUATORS.get(otype)\n if checker is None:\n blockers.append(f\"unknown:{otype}\")\n continue\n params = {k: v for k, v in overlay.items() if k != \"type\"}\n if not checker(state, params):\n blockers.append(otype)\n return len(blockers) == 0, blockers\n","content_type":"text/x-python; charset=utf-8","language":"python","size":3174,"content_sha256":"5bee928b2e87b225ec04f51539cceaf8878c50997d88c79a279c282a7b3668a0"},{"filename":"primitives/sizing.py","content":"\"\"\"\nSizing evaluators — 3 primitives.\n\nEach function: compute_size(equity_usd, ctx, params) -> float (USD to trade)\nL3 hard bound: max 10% of equity per trade.\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import Any\nfrom primitives.entry import MarketContext\n\nL3_MAX_PCT = 10.0 # hard bound\n\n\ndef compute_fixed_pct(equity_usd: float, ctx: MarketContext, params: dict) -> float:\n \"\"\"S-01: Fixed % of current equity.\"\"\"\n pct = min(params[\"pct\"], L3_MAX_PCT)\n return equity_usd * pct / 100\n\n\ndef compute_fixed_usd(equity_usd: float, ctx: MarketContext, params: dict) -> float:\n \"\"\"S-02: Fixed USD amount.\"\"\"\n usd = params[\"usd\"]\n max_allowed = equity_usd * L3_MAX_PCT / 100\n return min(usd, max_allowed)\n\n\ndef compute_volatility_scaled(equity_usd: float, ctx: MarketContext, params: dict) -> float:\n \"\"\"S-03: Size inversely scaled by ATR. Smaller in volatile markets.\"\"\"\n target_risk = params[\"target_risk_pct\"]\n atr_period = params.get(\"atr_period\", 14)\n if len(ctx.bars) \u003c atr_period + 1:\n return 0.0\n # Compute ATR\n trs: list[float] = []\n for i in range(-atr_period, 0):\n bar = ctx.bars[i]\n prev = ctx.bars[i - 1]\n tr = max(bar.high - bar.low, abs(bar.high - prev.close), abs(bar.low - prev.close))\n trs.append(tr)\n atr = sum(trs) / len(trs)\n if atr == 0 or ctx.current_price == 0:\n return 0.0\n atr_pct = atr / ctx.current_price * 100\n # Position size = (target_risk% of equity) / ATR%\n raw_size = (equity_usd * target_risk / 100) / (atr_pct / 100)\n max_allowed = equity_usd * L3_MAX_PCT / 100\n return min(raw_size, max_allowed)\n\n\nSIZING_EVALUATORS: dict[str, Any] = {\n \"fixed_pct\": compute_fixed_pct,\n \"fixed_usd\": compute_fixed_usd,\n \"volatility_scaled\": compute_volatility_scaled,\n}\n\n\ndef compute_size(equity_usd: float, ctx: MarketContext, sizing_spec: dict) -> float:\n \"\"\"Dispatch to the correct sizing function.\"\"\"\n stype = sizing_spec[\"type\"]\n params = {k: v for k, v in sizing_spec.items() if k != \"type\"}\n fn = SIZING_EVALUATORS.get(stype)\n if fn is None:\n raise ValueError(f\"Unknown sizing type: {stype}\")\n size = fn(equity_usd, ctx, params)\n # Final L3 clamp\n max_allowed = equity_usd * L3_MAX_PCT / 100\n return max(0.0, min(size, max_allowed))\n","content_type":"text/x-python; charset=utf-8","language":"python","size":2314,"content_sha256":"60a28e026ef4a1e5293398add8fb22184962a4405b4ec67a7a714c8b6c47f003"},{"filename":"README.md","content":"# Starter Coach — Your On-Chain Trading Bot Builder\n\nA conversational 6-step skill that guides any user — beginner or experienced — to design, backtest, paper trade, and deploy their own automated DEX spot-trading bot on OKX DEX.\n\n你的链上交易机器人构建教练 — 从零到上线,一次对话搞定。\n\n## How It Works / 使用流程\n\n```\nStep 1: Onboard — Welcome + legal disclaimer\nStep 2: Profile — Risk appetite, budget, chain preference\nStep 3: Build Strategy — Choose goal, configure entry/exit rules\nStep 4: Backtest — Validate strategy on historical data\nStep 5: Paper Trade — Simulate live trades with zero risk\nStep 6: Go Live — Explicit CONFIRM gate + live execution\n```\n\n## Features / 功能\n\n- **7 Goal Archetypes / 7 种策略目标** — Stack Sats, Buy the Dip, Follow Smart Money, Copy-trade Wallet, Snipe Tokens, Ride the Trend, Something Else\n- **Bilingual / 双语支持** — English and Chinese, auto-detected\n- **Backtest Engine / 回测引擎** — Historical validation with performance disclaimer\n- **Paper Trade Gate / 纸盘门槛** — Must pass paper trading before live mode unlocks\n- **Live Mode Safeguard / 上线确认机制** — Requires explicit \"CONFIRM\", logged to audit file\n- **Kelly Sizing / Kelly 仓位管理** — Configurable risk-adjusted position sizing\n- **TEE Signing / TEE 签名** — All trades via OnchainOS Agentic Wallet, no API key needed\n\n## Install / 安装\n\n```bash\nplugin-store install starter-coach\n```\n\nOr:\n\n```bash\nnpx skills add okx/plugin-store --skill starter-coach\n```\n\n## Usage / 使用方法\n\nOnce installed, just tell your AI agent:\n\n```\nStart starter coach\n```\n\n```\n帮我创建一个交易机器人\n```\n\nThe coach will guide you through the full flow interactively.\n\n## Risk Warning / 风险提示\n\n> DEX trading involves significant financial risk. Past backtest performance does not guarantee future results. Always complete paper trading before going live. Never invest more than you can afford to lose.\n\n> DEX 交易存在重大财务风险。历史回测结果不代表未来收益。请务必先完成纸盘交易再上线。切勿投入超出承受能力的资金。\n\n## License\n\nMIT\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":2225,"content_sha256":"bf7f65cf3ce334570515fb26646f34e2dafaaa72413173b57e15ac7078bf9664"},{"filename":"schema.json","content":"{\n \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n \"$id\": \"https://okx.com/dex/starter-coach/v1.0/strategy-spec.schema.json\",\n \"title\": \"Starter Coach V2 — Strategy Spec\",\n \"description\": \"Authoritative grammar for LLM-generated DEX spot-trading strategies. The LLM host (Claude / OpenCloud / Hermes agents) emits a JSON object matching this schema; the harness validates before any backtest or paper-trade gate. Long-only, no perps, no shorts. All on-chain tags (smart_money, dev, bundler, fresh_wallet, honeypot, lp_locked, taxes, top_holders) resolve via OnchainOS CLI at runtime.\",\n \"type\": \"object\",\n \"required\": [\"meta\", \"instrument\", \"entry\", \"exit\", \"sizing\"],\n \"additionalProperties\": false,\n \"x-version\": \"1.0\",\n \"x-primitive-count\": 53,\n \"x-meta-templates\": [\"grid\"],\n\n \"properties\": {\n \"meta\": { \"$ref\": \"#/$defs/Meta\" },\n \"instrument\": { \"$ref\": \"#/$defs/Instrument\" },\n \"universe\": { \"$ref\": \"#/$defs/Universe\" },\n \"entry\": { \"$ref\": \"#/$defs/Entry\" },\n \"exit\": { \"$ref\": \"#/$defs/Exit\" },\n \"sizing\": { \"$ref\": \"#/$defs/Sizing\" },\n \"filters\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/Filter\" },\n \"maxItems\": 40\n },\n \"risk_overlays\": {\n \"type\": \"array\",\n \"items\": { \"$ref\": \"#/$defs/RiskOverlay\" },\n \"maxItems\": 10\n },\n \"grid\": {\n \"$ref\": \"#/$defs/GridMetaTemplate\",\n \"description\": \"Meta-template. If present, the harness expands it into primitives at validation time. Cannot coexist with entry/exit/sizing at top level.\"\n }\n },\n\n \"allOf\": [\n {\n \"description\": \"G-02 · If instrument.symbol is '*', the universe block is required (dynamic universe strategy)\",\n \"if\": {\n \"properties\": { \"instrument\": { \"properties\": { \"symbol\": { \"const\": \"*\" } } } }\n },\n \"then\": { \"required\": [\"universe\"] }\n },\n {\n \"description\": \"Grid meta-template and explicit entry/exit/sizing are mutually exclusive\",\n \"if\": { \"required\": [\"grid\"] },\n \"then\": {\n \"not\": {\n \"anyOf\": [\n { \"required\": [\"entry\"] },\n { \"required\": [\"exit\"] },\n { \"required\": [\"sizing\"] }\n ]\n }\n }\n }\n ],\n\n \"$defs\": {\n\n \"Meta\": {\n \"type\": \"object\",\n \"required\": [\"name\"],\n \"additionalProperties\": false,\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"pattern\": \"^[a-z0-9_]{3,64}$\",\n \"description\": \"snake_case identifier, 3–64 chars\"\n },\n \"version\": {\n \"type\": \"string\",\n \"default\": \"1.0\"\n },\n \"risk_tier\": {\n \"type\": \"string\",\n \"enum\": [\"passive\", \"conservative\", \"moderate\", \"aggressive\", \"speculative\"]\n },\n \"description\": {\n \"type\": \"string\",\n \"description\": \"Plain-English intent, shown back to the user\"\n },\n \"author_intent\": {\n \"type\": \"string\",\n \"description\": \"User's original natural-language request, verbatim\"\n },\n \"live_only\": {\n \"type\": \"boolean\",\n \"default\": false,\n \"description\": \"Set to true if any primitive in the spec is live_only. Harness auto-derives this but LLM may pre-flag.\"\n }\n }\n },\n\n \"Instrument\": {\n \"type\": \"object\",\n \"required\": [\"symbol\", \"timeframe\"],\n \"additionalProperties\": false,\n \"properties\": {\n \"symbol\": {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"pattern\": \"^[A-Z0-9]{1,16}(-[A-Z0-9]{1,16}){1,2}$\",\n \"description\": \"Single-symbol strategy, e.g. SOL-USDT\"\n },\n {\n \"const\": \"*\",\n \"description\": \"Dynamic universe — requires universe block\"\n }\n ]\n },\n \"timeframe\": {\n \"type\": \"string\",\n \"enum\": [\"1m\", \"5m\", \"15m\", \"1H\", \"4H\", \"1D\"]\n }\n }\n },\n\n \"Universe\": {\n \"type\": \"object\",\n \"required\": [\"selector\", \"chain\"],\n \"additionalProperties\": false,\n \"description\": \"G-02 · Dynamic universe block for * symbol strategies\",\n \"properties\": {\n \"selector\": {\n \"type\": \"string\",\n \"enum\": [\"ranking_entry\", \"wallet_copy_buy\", \"smart_money_buy\"],\n \"description\": \"Entry primitive whose firing produces the token set\"\n },\n \"chain\": {\n \"type\": \"string\",\n \"enum\": [\"solana\", \"ethereum\", \"base\", \"arbitrum\", \"optimism\", \"polygon\", \"bnb\", \"avalanche\"]\n }\n }\n },\n\n \"Entry\": {\n \"description\": \"Exactly one entry primitive. Discriminated by `type`.\",\n \"oneOf\": [\n { \"$ref\": \"#/$defs/entry_price_drop\" },\n { \"$ref\": \"#/$defs/entry_price_breakout\" },\n { \"$ref\": \"#/$defs/entry_ma_cross\" },\n { \"$ref\": \"#/$defs/entry_rsi_threshold\" },\n { \"$ref\": \"#/$defs/entry_volume_spike\" },\n { \"$ref\": \"#/$defs/entry_time_schedule\" },\n { \"$ref\": \"#/$defs/entry_smart_money_buy\" },\n { \"$ref\": \"#/$defs/entry_dev_buy\" },\n { \"$ref\": \"#/$defs/entry_macd_cross\" },\n { \"$ref\": \"#/$defs/entry_bollinger_touch\" },\n { \"$ref\": \"#/$defs/entry_ranking_entry\" },\n { \"$ref\": \"#/$defs/entry_wallet_copy_buy\" }\n ]\n },\n\n \"Exit\": {\n \"type\": \"object\",\n \"required\": [\"stop_loss\"],\n \"additionalProperties\": false,\n \"description\": \"G-01 · All exit entries are evaluated in parallel every tick. First-to-fire wins. On tie, stop_loss wins (fail-safe). H-01 · stop_loss is always required.\",\n \"properties\": {\n \"stop_loss\": { \"$ref\": \"#/$defs/exit_stop_loss_inner\" },\n \"take_profit\": { \"$ref\": \"#/$defs/exit_take_profit_inner\" },\n \"trailing_stop\": { \"$ref\": \"#/$defs/exit_trailing_stop_inner\" },\n \"tiered_take_profit\": { \"$ref\": \"#/$defs/exit_tiered_take_profit_inner\" },\n \"other\": {\n \"type\": \"array\",\n \"description\": \"Additional exit conditions. Each item is a discriminated primitive.\",\n \"maxItems\": 10,\n \"items\": {\n \"oneOf\": [\n { \"$ref\": \"#/$defs/exit_time_exit\" },\n { \"$ref\": \"#/$defs/exit_indicator_reversal\" },\n { \"$ref\": \"#/$defs/exit_smart_money_sell\" },\n { \"$ref\": \"#/$defs/exit_dev_dump\" },\n { \"$ref\": \"#/$defs/exit_wallet_mirror_sell\" },\n { \"$ref\": \"#/$defs/exit_fast_dump_exit\" }\n ]\n }\n }\n }\n },\n\n \"Sizing\": {\n \"description\": \"Exactly one sizing primitive. Discriminated by `type`. L3 hard bound: max 10% per trade.\",\n \"oneOf\": [\n { \"$ref\": \"#/$defs/sizing_fixed_pct\" },\n { \"$ref\": \"#/$defs/sizing_fixed_usd\" },\n { \"$ref\": \"#/$defs/sizing_volatility_scaled\" }\n ]\n },\n\n \"Filter\": {\n \"description\": \"Market-condition or token-safety filter. Discriminated by `type`. All filters must pass for entry to fire.\",\n \"oneOf\": [\n { \"$ref\": \"#/$defs/filter_time_window\" },\n { \"$ref\": \"#/$defs/filter_volatility_range\" },\n { \"$ref\": \"#/$defs/filter_volume_minimum\" },\n { \"$ref\": \"#/$defs/filter_cooldown\" },\n { \"$ref\": \"#/$defs/filter_market_regime\" },\n { \"$ref\": \"#/$defs/filter_price_range\" },\n { \"$ref\": \"#/$defs/filter_btc_overlay\" },\n { \"$ref\": \"#/$defs/filter_top_zone_guard\" },\n { \"$ref\": \"#/$defs/filter_mcap_range\" },\n { \"$ref\": \"#/$defs/filter_launch_age\" },\n { \"$ref\": \"#/$defs/filter_honeypot_check\" },\n { \"$ref\": \"#/$defs/filter_lp_locked\" },\n { \"$ref\": \"#/$defs/filter_buy_tax_max\" },\n { \"$ref\": \"#/$defs/filter_sell_tax_max\" },\n { \"$ref\": \"#/$defs/filter_liquidity_min\" },\n { \"$ref\": \"#/$defs/filter_top_holders_max\" },\n { \"$ref\": \"#/$defs/filter_bundler_ratio_max\" },\n { \"$ref\": \"#/$defs/filter_dev_holding_max\" },\n { \"$ref\": \"#/$defs/filter_insider_holding_max\" },\n { \"$ref\": \"#/$defs/filter_fresh_wallet_ratio_max\" },\n { \"$ref\": \"#/$defs/filter_smart_money_present_min\" },\n { \"$ref\": \"#/$defs/filter_phishing_exclude\" },\n { \"$ref\": \"#/$defs/filter_whale_concentration_max\" }\n ]\n },\n\n \"RiskOverlay\": {\n \"description\": \"Portfolio-level cap. Layered on top of harness L3.\",\n \"oneOf\": [\n { \"$ref\": \"#/$defs/risk_max_daily_trades\" },\n { \"$ref\": \"#/$defs/risk_max_concurrent_positions\" },\n { \"$ref\": \"#/$defs/risk_drawdown_pause\" },\n { \"$ref\": \"#/$defs/risk_correlation_cap\" },\n { \"$ref\": \"#/$defs/risk_session_loss_pause\" }\n ]\n },\n\n \"GridMetaTemplate\": {\n \"type\": \"object\",\n \"description\": \"Grid meta-template. Expands at validation time into: time_schedule (entry) + price_range (filter) + fixed_usd (sizing) + per-slot take_profit + portfolio stop_loss + max_concurrent_positions risk overlay.\",\n \"required\": [\"price_min\", \"price_max\", \"levels\", \"usd_per_level\"],\n \"additionalProperties\": false,\n \"properties\": {\n \"price_min\": { \"type\": \"number\", \"exclusiveMinimum\": 0 },\n \"price_max\": { \"type\": \"number\", \"exclusiveMinimum\": 0 },\n \"levels\": { \"type\": \"integer\", \"minimum\": 2, \"maximum\": 50 },\n \"usd_per_level\": { \"type\": \"number\", \"minimum\": 10, \"maximum\": 10000 },\n \"take_profit_per_level_pct\": { \"type\": \"number\", \"minimum\": 0.5, \"maximum\": 20, \"default\": 3 },\n \"portfolio_stop_loss_pct\": { \"type\": \"number\", \"minimum\": 5, \"maximum\": 50, \"default\": 20 }\n }\n },\n\n \"_primitive_entry_base\": {\n \"description\": \"Shared shape for entry primitives. Each concrete def overrides `type` with a const and adds its own params.\"\n },\n\n \"entry_price_drop\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"pct\", \"lookback_bars\"],\n \"properties\": {\n \"type\": { \"const\": \"price_drop\" },\n \"pct\": { \"type\": \"number\", \"minimum\": 1, \"maximum\": 30 },\n \"lookback_bars\": { \"type\": \"integer\", \"minimum\": 6, \"maximum\": 720 }\n },\n \"x-live-only\": false\n },\n\n \"entry_price_breakout\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"direction\", \"lookback_bars\"],\n \"properties\": {\n \"type\": { \"const\": \"price_breakout\" },\n \"direction\": { \"enum\": [\"up\", \"down\"] },\n \"lookback_bars\": { \"type\": \"integer\", \"minimum\": 6, \"maximum\": 720 },\n \"confirm_pct\": { \"type\": \"number\", \"minimum\": 0, \"maximum\": 5 }\n },\n \"x-live-only\": false\n },\n\n \"entry_ma_cross\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"fast_period\", \"slow_period\"],\n \"properties\": {\n \"type\": { \"const\": \"ma_cross\" },\n \"fast_period\": { \"type\": \"integer\", \"minimum\": 5, \"maximum\": 50 },\n \"slow_period\": { \"type\": \"integer\", \"minimum\": 10, \"maximum\": 200 },\n \"ma_type\": { \"enum\": [\"SMA\", \"EMA\"], \"default\": \"EMA\" }\n },\n \"x-live-only\": false\n },\n\n \"entry_rsi_threshold\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"period\", \"level\", \"direction\"],\n \"properties\": {\n \"type\": { \"const\": \"rsi_threshold\" },\n \"period\": { \"type\": \"integer\", \"minimum\": 7, \"maximum\": 30 },\n \"level\": { \"type\": \"number\", \"minimum\": 10, \"maximum\": 90 },\n \"direction\": { \"enum\": [\"cross_up\", \"cross_down\"] }\n },\n \"x-live-only\": false\n },\n\n \"entry_volume_spike\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"multiplier\", \"avg_bars\"],\n \"properties\": {\n \"type\": { \"const\": \"volume_spike\" },\n \"multiplier\": { \"type\": \"number\", \"minimum\": 1.5, \"maximum\": 10 },\n \"avg_bars\": { \"type\": \"integer\", \"minimum\": 12, \"maximum\": 168 }\n },\n \"x-live-only\": false\n },\n\n \"entry_time_schedule\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"interval\"],\n \"properties\": {\n \"type\": { \"const\": \"time_schedule\" },\n \"interval\": { \"enum\": [\"1H\", \"4H\", \"1D\", \"1W\"] },\n \"anchor_utc\": {\n \"type\": \"string\",\n \"pattern\": \"^([01]\\\\d|2[0-3]):[0-5]\\\\d$\",\n \"description\": \"HH:MM in UTC\"\n }\n },\n \"x-live-only\": false\n },\n\n \"entry_smart_money_buy\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"min_wallets\", \"window_min\"],\n \"description\": \"Event-driven: fires on SM buy transaction (G-05). Compare with filter smart_money_present_min (state check).\",\n \"properties\": {\n \"type\": { \"const\": \"smart_money_buy\" },\n \"min_wallets\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 20 },\n \"window_min\": { \"type\": \"integer\", \"minimum\": 5, \"maximum\": 1440 },\n \"min_usd_each\": { \"type\": \"number\", \"minimum\": 100, \"maximum\": 1000000 }\n },\n \"x-live-only\": true\n },\n\n \"entry_dev_buy\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"min_usd\"],\n \"properties\": {\n \"type\": { \"const\": \"dev_buy\" },\n \"min_usd\": { \"type\": \"number\", \"minimum\": 100, \"maximum\": 1000000 },\n \"window_min\": { \"type\": \"integer\", \"minimum\": 5, \"maximum\": 1440 }\n },\n \"x-live-only\": true\n },\n\n \"entry_macd_cross\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"fast_period\", \"slow_period\", \"signal_period\", \"direction\"],\n \"properties\": {\n \"type\": { \"const\": \"macd_cross\" },\n \"fast_period\": { \"type\": \"integer\", \"minimum\": 5, \"maximum\": 20 },\n \"slow_period\": { \"type\": \"integer\", \"minimum\": 15, \"maximum\": 50 },\n \"signal_period\": { \"type\": \"integer\", \"minimum\": 5, \"maximum\": 20 },\n \"direction\": { \"enum\": [\"cross_up\", \"cross_down\"] }\n },\n \"x-live-only\": false\n },\n\n \"entry_bollinger_touch\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"period\", \"std_dev\", \"band\"],\n \"properties\": {\n \"type\": { \"const\": \"bollinger_touch\" },\n \"period\": { \"type\": \"integer\", \"minimum\": 10, \"maximum\": 50 },\n \"std_dev\": { \"type\": \"number\", \"minimum\": 1.5, \"maximum\": 3.0 },\n \"band\": { \"enum\": [\"upper\", \"lower\"] }\n },\n \"x-live-only\": false\n },\n\n \"entry_ranking_entry\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"list_name\", \"top_n\"],\n \"properties\": {\n \"type\": { \"const\": \"ranking_entry\" },\n \"list_name\": { \"enum\": [\"gainers\", \"volume\", \"trending\", \"new\"] },\n \"top_n\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 50 },\n \"timeframe\": { \"enum\": [\"1H\", \"4H\", \"24H\"] }\n },\n \"x-live-only\": true\n },\n\n \"entry_wallet_copy_buy\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"target_wallet\", \"min_usd\", \"mirror_mode\"],\n \"properties\": {\n \"type\": { \"const\": \"wallet_copy_buy\" },\n \"target_wallet\": {\n \"oneOf\": [\n { \"type\": \"string\", \"pattern\": \"^(0x[a-fA-F0-9]{40}|[1-9A-HJ-NP-Za-km-z]{32,44})$\" },\n {\n \"type\": \"array\",\n \"items\": { \"type\": \"string\" },\n \"minItems\": 1,\n \"maxItems\": 20\n }\n ]\n },\n \"min_usd\": { \"type\": \"number\", \"minimum\": 10, \"maximum\": 100000 },\n \"mirror_mode\": { \"enum\": [\"instant\", \"mcap_target\"] }\n },\n \"x-live-only\": true\n },\n\n \"exit_stop_loss_inner\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"pct\"],\n \"description\": \"H-01 · Always required. Fixed % loss from entry.\",\n \"properties\": {\n \"pct\": { \"type\": \"number\", \"minimum\": 1, \"maximum\": 20 }\n }\n },\n\n \"exit_take_profit_inner\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"pct\"],\n \"properties\": {\n \"pct\": { \"type\": \"number\", \"minimum\": 2, \"maximum\": 100 }\n }\n },\n\n \"exit_trailing_stop_inner\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"pct\"],\n \"properties\": {\n \"pct\": { \"type\": \"number\", \"minimum\": 1, \"maximum\": 20 },\n \"activate_after_pct\": { \"type\": \"number\", \"minimum\": 0, \"maximum\": 50 }\n }\n },\n\n \"exit_tiered_take_profit_inner\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"tiers\"],\n \"properties\": {\n \"tiers\": {\n \"type\": \"array\",\n \"minItems\": 2,\n \"maxItems\": 5,\n \"items\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"pct_gain\", \"pct_sell\"],\n \"properties\": {\n \"pct_gain\": { \"type\": \"number\", \"minimum\": 2, \"maximum\": 1000 },\n \"pct_sell\": { \"type\": \"number\", \"minimum\": 5, \"maximum\": 100 }\n }\n }\n },\n \"runner_mode\": { \"enum\": [\"hold\", \"trail\"] }\n }\n },\n\n \"exit_time_exit\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"max_bars\"],\n \"description\": \"G-06 · Max-hold duration from entry time (not absolute clock). Bars on instrument.timeframe.\",\n \"properties\": {\n \"type\": { \"const\": \"time_exit\" },\n \"max_bars\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 720 }\n },\n \"x-live-only\": false\n },\n\n \"exit_indicator_reversal\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"mirror_entry\"],\n \"properties\": {\n \"type\": { \"const\": \"indicator_reversal\" },\n \"mirror_entry\": { \"type\": \"boolean\" }\n },\n \"x-live-only\": false\n },\n\n \"exit_smart_money_sell\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"min_wallets\", \"window_min\"],\n \"properties\": {\n \"type\": { \"const\": \"smart_money_sell\" },\n \"min_wallets\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 20 },\n \"window_min\": { \"type\": \"integer\", \"minimum\": 5, \"maximum\": 1440 }\n },\n \"x-live-only\": true\n },\n\n \"exit_dev_dump\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"min_usd\"],\n \"properties\": {\n \"type\": { \"const\": \"dev_dump\" },\n \"min_usd\": { \"type\": \"number\", \"minimum\": 100, \"maximum\": 1000000 },\n \"min_pct_of_holding\": { \"type\": \"number\", \"minimum\": 1, \"maximum\": 100 }\n },\n \"x-live-only\": true\n },\n\n \"exit_wallet_mirror_sell\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"target_wallet\"],\n \"properties\": {\n \"type\": { \"const\": \"wallet_mirror_sell\" },\n \"target_wallet\": {\n \"oneOf\": [\n { \"type\": \"string\" },\n { \"type\": \"array\", \"items\": { \"type\": \"string\" }, \"minItems\": 1 }\n ]\n },\n \"min_pct_sold\": { \"type\": \"number\", \"minimum\": 10, \"maximum\": 100 }\n },\n \"x-live-only\": true\n },\n\n \"exit_fast_dump_exit\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"drop_pct\", \"window_sec\"],\n \"properties\": {\n \"type\": { \"const\": \"fast_dump_exit\" },\n \"drop_pct\": { \"type\": \"number\", \"minimum\": 3, \"maximum\": 50 },\n \"window_sec\": { \"type\": \"integer\", \"minimum\": 5, \"maximum\": 300 }\n },\n \"x-live-only\": true\n },\n\n \"filter_time_window\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"start_hour\", \"end_hour\"],\n \"properties\": {\n \"type\": { \"const\": \"time_window\" },\n \"start_hour\": { \"type\": \"integer\", \"minimum\": 0, \"maximum\": 23 },\n \"end_hour\": { \"type\": \"integer\", \"minimum\": 0, \"maximum\": 23 },\n \"weekdays_only\": { \"type\": \"boolean\", \"default\": false }\n },\n \"x-live-only\": false\n },\n\n \"filter_volatility_range\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"atr_period\"],\n \"properties\": {\n \"type\": { \"const\": \"volatility_range\" },\n \"atr_period\": { \"type\": \"integer\", \"minimum\": 7, \"maximum\": 50 },\n \"min_pct\": { \"type\": \"number\", \"minimum\": 0, \"maximum\": 20 },\n \"max_pct\": { \"type\": \"number\", \"minimum\": 0, \"maximum\": 50 }\n },\n \"x-live-only\": false\n },\n\n \"filter_volume_minimum\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"min_usd_24h\"],\n \"properties\": {\n \"type\": { \"const\": \"volume_minimum\" },\n \"min_usd_24h\": { \"type\": \"number\", \"minimum\": 100000 }\n },\n \"x-live-only\": false\n },\n\n \"filter_cooldown\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"bars\"],\n \"properties\": {\n \"type\": { \"const\": \"cooldown\" },\n \"bars\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 168 }\n },\n \"x-live-only\": false\n },\n\n \"filter_market_regime\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"regime\"],\n \"properties\": {\n \"type\": { \"const\": \"market_regime\" },\n \"regime\": { \"enum\": [\"up\", \"down\", \"range\", \"any\"] },\n \"ma_period\": { \"type\": \"integer\", \"minimum\": 50, \"maximum\": 500 }\n },\n \"x-live-only\": false\n },\n\n \"filter_price_range\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\"],\n \"anyOf\": [\n { \"required\": [\"min_price\"] },\n { \"required\": [\"max_price\"] }\n ],\n \"properties\": {\n \"type\": { \"const\": \"price_range\" },\n \"min_price\": { \"type\": \"number\", \"exclusiveMinimum\": 0 },\n \"max_price\": { \"type\": \"number\", \"exclusiveMinimum\": 0 }\n },\n \"x-live-only\": false\n },\n\n \"filter_btc_overlay\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"condition\"],\n \"properties\": {\n \"type\": { \"const\": \"btc_overlay\" },\n \"condition\": { \"enum\": [\"above_ma\", \"green_candle\", \"uptrend\"] },\n \"ma_period\": { \"type\": \"integer\", \"minimum\": 20, \"maximum\": 200 }\n },\n \"x-live-only\": false\n },\n\n \"filter_top_zone_guard\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"max_zone_pct\", \"lookback_bars\"],\n \"properties\": {\n \"type\": { \"const\": \"top_zone_guard\" },\n \"max_zone_pct\": { \"type\": \"number\", \"minimum\": 50, \"maximum\": 95 },\n \"lookback_bars\": { \"type\": \"integer\", \"minimum\": 12, \"maximum\": 720 }\n },\n \"x-live-only\": false\n },\n\n \"filter_mcap_range\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\"],\n \"anyOf\": [\n { \"required\": [\"min_usd\"] },\n { \"required\": [\"max_usd\"] }\n ],\n \"properties\": {\n \"type\": { \"const\": \"mcap_range\" },\n \"min_usd\": { \"type\": \"number\", \"minimum\": 1000, \"maximum\": 100000000000 },\n \"max_usd\": { \"type\": \"number\", \"minimum\": 1000, \"maximum\": 100000000000 }\n },\n \"x-live-only\": true\n },\n\n \"filter_launch_age\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\"],\n \"anyOf\": [\n { \"required\": [\"min_hours\"] },\n { \"required\": [\"max_hours\"] }\n ],\n \"properties\": {\n \"type\": { \"const\": \"launch_age\" },\n \"min_hours\": { \"type\": \"number\", \"minimum\": 0, \"maximum\": 8760 },\n \"max_hours\": { \"type\": \"number\", \"minimum\": 1, \"maximum\": 8760 }\n },\n \"x-live-only\": true\n },\n\n \"filter_honeypot_check\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\"],\n \"properties\": {\n \"type\": { \"const\": \"honeypot_check\" },\n \"required\": { \"const\": true, \"default\": true }\n },\n \"x-live-only\": true\n },\n\n \"filter_lp_locked\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"min_pct_locked\"],\n \"properties\": {\n \"type\": { \"const\": \"lp_locked\" },\n \"min_pct_locked\": { \"type\": \"number\", \"minimum\": 50, \"maximum\": 100 },\n \"min_lock_days\": { \"type\": \"integer\", \"minimum\": 7, \"maximum\": 3650 }\n },\n \"x-live-only\": true\n },\n\n \"filter_buy_tax_max\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"max_pct\"],\n \"properties\": {\n \"type\": { \"const\": \"buy_tax_max\" },\n \"max_pct\": { \"type\": \"number\", \"minimum\": 0, \"maximum\": 15 }\n },\n \"x-live-only\": true\n },\n\n \"filter_sell_tax_max\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"max_pct\"],\n \"properties\": {\n \"type\": { \"const\": \"sell_tax_max\" },\n \"max_pct\": { \"type\": \"number\", \"minimum\": 0, \"maximum\": 15 }\n },\n \"x-live-only\": true\n },\n\n \"filter_liquidity_min\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"min_usd\"],\n \"properties\": {\n \"type\": { \"const\": \"liquidity_min\" },\n \"min_usd\": { \"type\": \"number\", \"minimum\": 5000, \"maximum\": 10000000 }\n },\n \"x-live-only\": true\n },\n\n \"filter_top_holders_max\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"top_n\", \"max_pct\"],\n \"properties\": {\n \"type\": { \"const\": \"top_holders_max\" },\n \"top_n\": { \"type\": \"integer\", \"minimum\": 5, \"maximum\": 20 },\n \"max_pct\": { \"type\": \"number\", \"minimum\": 15, \"maximum\": 60 }\n },\n \"x-live-only\": true\n },\n\n \"filter_bundler_ratio_max\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"max_pct\"],\n \"properties\": {\n \"type\": { \"const\": \"bundler_ratio_max\" },\n \"max_pct\": { \"type\": \"number\", \"minimum\": 5, \"maximum\": 50 }\n },\n \"x-live-only\": true\n },\n\n \"filter_dev_holding_max\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"max_pct\"],\n \"properties\": {\n \"type\": { \"const\": \"dev_holding_max\" },\n \"max_pct\": { \"type\": \"number\", \"minimum\": 0, \"maximum\": 20 }\n },\n \"x-live-only\": true\n },\n\n \"filter_insider_holding_max\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"max_pct\"],\n \"properties\": {\n \"type\": { \"const\": \"insider_holding_max\" },\n \"max_pct\": { \"type\": \"number\", \"minimum\": 0, \"maximum\": 30 }\n },\n \"x-live-only\": true\n },\n\n \"filter_fresh_wallet_ratio_max\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"max_pct\"],\n \"properties\": {\n \"type\": { \"const\": \"fresh_wallet_ratio_max\" },\n \"max_pct\": { \"type\": \"number\", \"minimum\": 20, \"maximum\": 80 },\n \"fresh_def\": { \"enum\": [\"age_days\", \"tx_count\"] }\n },\n \"x-live-only\": true\n },\n\n \"filter_smart_money_present_min\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"min_wallets\"],\n \"description\": \"State check at entry-candidate time (G-05). Compare with entry smart_money_buy (event-driven).\",\n \"properties\": {\n \"type\": { \"const\": \"smart_money_present_min\" },\n \"min_wallets\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 20 }\n },\n \"x-live-only\": true\n },\n\n \"filter_phishing_exclude\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\"],\n \"properties\": {\n \"type\": { \"const\": \"phishing_exclude\" },\n \"required\": { \"const\": true, \"default\": true }\n },\n \"x-live-only\": true\n },\n\n \"filter_whale_concentration_max\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"max_pct\"],\n \"properties\": {\n \"type\": { \"const\": \"whale_concentration_max\" },\n \"max_pct\": { \"type\": \"number\", \"minimum\": 3, \"maximum\": 25 }\n },\n \"x-live-only\": true\n },\n\n \"sizing_fixed_pct\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"pct\"],\n \"properties\": {\n \"type\": { \"const\": \"fixed_pct\" },\n \"pct\": { \"type\": \"number\", \"minimum\": 0.5, \"maximum\": 10, \"description\": \"L3 hard bound: 10% max per trade\" }\n },\n \"x-live-only\": false\n },\n\n \"sizing_fixed_usd\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"usd\"],\n \"properties\": {\n \"type\": { \"const\": \"fixed_usd\" },\n \"usd\": { \"type\": \"number\", \"minimum\": 10, \"maximum\": 10000 }\n },\n \"x-live-only\": false\n },\n\n \"sizing_volatility_scaled\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"target_risk_pct\"],\n \"properties\": {\n \"type\": { \"const\": \"volatility_scaled\" },\n \"target_risk_pct\": { \"type\": \"number\", \"minimum\": 0.1, \"maximum\": 2 },\n \"atr_period\": { \"type\": \"integer\", \"minimum\": 7, \"maximum\": 50 }\n },\n \"x-live-only\": false\n },\n\n \"risk_max_daily_trades\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"n\"],\n \"properties\": {\n \"type\": { \"const\": \"max_daily_trades\" },\n \"n\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 50 }\n }\n },\n\n \"risk_max_concurrent_positions\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"n\"],\n \"properties\": {\n \"type\": { \"const\": \"max_concurrent_positions\" },\n \"n\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 10 }\n }\n },\n\n \"risk_drawdown_pause\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"pause_pct\"],\n \"properties\": {\n \"type\": { \"const\": \"drawdown_pause\" },\n \"pause_pct\": { \"type\": \"number\", \"minimum\": 3, \"maximum\": 15 },\n \"resume_pct\": { \"type\": \"number\", \"minimum\": 0, \"maximum\": 10 }\n }\n },\n\n \"risk_correlation_cap\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"mode\", \"max_correlated\"],\n \"description\": \"G-04 · v1.0 narrowed: same_token_dedupe only. Return-correlation grouping deferred to v1.2.\",\n \"properties\": {\n \"type\": { \"const\": \"correlation_cap\" },\n \"mode\": {\n \"const\": \"same_token_dedupe\",\n \"description\": \"Only value accepted in v1.0\"\n },\n \"max_correlated\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 5 }\n }\n },\n\n \"risk_session_loss_pause\": {\n \"type\": \"object\", \"additionalProperties\": false,\n \"required\": [\"type\", \"max_consecutive_losses\"],\n \"properties\": {\n \"type\": { \"const\": \"session_loss_pause\" },\n \"max_consecutive_losses\": { \"type\": \"integer\", \"minimum\": 2, \"maximum\": 10 },\n \"session_hours\": { \"type\": \"integer\", \"minimum\": 1, \"maximum\": 24 }\n }\n }\n\n }\n}\n","content_type":"application/json; charset=utf-8","language":"json","size":30663,"content_sha256":"4d4e8b891f567ae226057134aac7fbc02e000932a17abfb90638b4fa5ca90a7b"},{"filename":"SUMMARY.md","content":"## Overview\n\nStarter Coach is a conversational 6-step skill that guides users to design, backtest, paper trade, and deploy their own automated DEX spot-trading bot on OKX DEX.\n\nCore operations:\n\n- Guide users through a 6-step flow: Onboard → Profile → Build Strategy → Backtest → Paper Trade → Go Live\n- Generate a validated JSON strategy spec from user inputs (goal, risk, budget, chain)\n- Run backtests on historical data with performance metrics and disclaimer\n- Gate live mode behind paper trading requirement and explicit CONFIRM acknowledgment\n- Execute DEX spot trades via onchainos Agentic Wallet (TEE signing)\n\nTags: `trading-bot` `strategy-builder` `dex` `onchainos` `solana` `ethereum` `paper-trade`\n\n## Prerequisites\n\n- No IP/region restrictions\n- Supported chains: Solana, Ethereum, and all OKX DEX-supported chains\n- Supported tokens: All tokens available on OKX DEX\n- onchainos CLI installed and authenticated (`onchainos --version` and `onchainos wallet status`)\n- Python 3.8+ (standard library only — no `pip install` required)\n- Funded wallet on your chosen chain for live trading\n\n## Quick Start\n\n1. **Install the skill**: `plugin-store install starter-coach`\n2. **Start the coach**: Tell your agent \"Start starter coach\" or \"帮我创建一个交易机器人\"\n3. **Complete the 6-step flow**: Answer questions about your goal, risk appetite, budget, and chain preference — the coach builds your strategy spec interactively\n4. **Review backtest results**: The coach runs a backtest and shows win rate, P&L, and trade count — past performance does not guarantee future results\n5. **Paper trade first**: Simulate live trades with zero risk to validate your strategy before going live\n6. **Go live**: Type `CONFIRM` when prompted — your acknowledgment is logged and live execution begins via onchainos Agentic Wallet\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":1851,"content_sha256":"200051f1056be7307d7b4f06a8a9361ad6fa9d8ae7d948e26968a99d72124275"},{"filename":"templates/__init__.py","content":"\"\"\"Starter Coach V2 — Meta-templates.\"\"\"\n","content_type":"text/x-python; charset=utf-8","language":"python","size":43,"content_sha256":"9bb074b9593781252b30eba99092fdcd87c037177aff38ab3f1d0db6d4d85202"},{"filename":"templates/grid.py","content":"\"\"\"\nGrid meta-template expansion.\n\nExpands a grid shorthand into a full strategy spec with composed primitives:\n - time_schedule (entry)\n - price_range (filter)\n - fixed_usd (sizing)\n - per-slot take_profit\n - portfolio stop_loss\n - max_concurrent_positions risk overlay\n\"\"\"\nfrom __future__ import annotations\n\n\ndef grid_expand(grid_params: dict, meta: dict, instrument: dict) -> dict:\n \"\"\"\n Expand grid meta-template into a full spec.\n\n grid_params: {price_min, price_max, levels, usd_per_level,\n take_profit_per_level_pct?, portfolio_stop_loss_pct?}\n \"\"\"\n price_min = grid_params[\"price_min\"]\n price_max = grid_params[\"price_max\"]\n levels = grid_params[\"levels\"]\n usd_per_level = grid_params[\"usd_per_level\"]\n tp_pct = grid_params.get(\"take_profit_per_level_pct\", 3)\n sl_pct = grid_params.get(\"portfolio_stop_loss_pct\", 20)\n\n # Each grid level buys at a different price in the range\n # and takes profit tp_pct% above that level's buy price.\n # The overall portfolio stop is sl_pct%.\n\n return {\n \"meta\": {\n **meta,\n \"description\": meta.get(\"description\", f\"Grid: {levels} levels, ${price_min}-${price_max}\"),\n },\n \"instrument\": instrument,\n \"entry\": {\n \"type\": \"time_schedule\",\n \"interval\": \"1H\",\n },\n \"exit\": {\n \"stop_loss\": {\"pct\": min(sl_pct, 20)}, # L3 capped at 20\n \"take_profit\": {\"pct\": tp_pct},\n },\n \"sizing\": {\n \"type\": \"fixed_usd\",\n \"usd\": usd_per_level,\n },\n \"filters\": [\n {\n \"type\": \"price_range\",\n \"min_price\": price_min,\n \"max_price\": price_max,\n },\n ],\n \"risk_overlays\": [\n {\n \"type\": \"max_concurrent_positions\",\n \"n\": min(levels, 10), # schema max is 10\n },\n ],\n }\n","content_type":"text/x-python; charset=utf-8","language":"python","size":1950,"content_sha256":"f3cb180925045369e041a6c42180c651d7007dceb9d20cbe83b80a931dab3870"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Starter Coach V2","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"Generate safe, backtestable DEX spot-trading strategy specs from natural language.","type":"text"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Scope","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DEX spot only","type":"text","marks":[{"type":"strong"}]},{"text":" — long-only, no perps, no shorts, no margin","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Blue-chip (ETH/SOL/BTC) through meme-token trading","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"OKX DEX venue","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"On-chain data backbone: ","type":"text"},{"text":"OnchainOS CLI","type":"text","marks":[{"type":"strong"}]},{"text":" (sole source for smart_money, dev, bundler, fresh_wallet, honeypot, lp_locked, taxes, top_holders tags)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"You emit a JSON strategy spec conforming to ","type":"text"},{"text":"schema.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" (Draft 2020-12). The harness validates it before any backtest or live execution. ","type":"text"},{"text":"You never write freeform trading code.","type":"text","marks":[{"type":"strong"}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"0. Coaching Journey — 6 Steps","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill follows a structured coaching journey. Never skip steps. Never deploy to live without paper-trade graduation. One step at a time.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Rendering Environment Detection","type":"text"}]},{"type":"paragraph","content":[{"text":"The coach runs in many environments. Detect the environment and use the correct render function:","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Environment","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Card function","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Why","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Claude Code (terminal / CLI)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"render_strategy_card()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Monospace font, box drawing renders correctly","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Claude.ai web app","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"render_strategy_card_md()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Proportional font, box chars crack","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Telegram bot","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"render_strategy_card_md()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No code block monospace guarantee","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"OpenClaw / Hermes / other agents","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"render_strategy_card_md()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Unknown rendering, use safe markdown","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Unknown","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"render_strategy_card_md()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Default to safe","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Detection heuristics:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If the conversation context suggests a terminal/CLI (user mentions \"terminal\", \"Claude Code\", command-line usage) → use ","type":"text"},{"text":"render_strategy_card()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If the context suggests web UI, mobile, Telegram, or any non-terminal agent → use ","type":"text"},{"text":"render_strategy_card_md()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When in doubt, use ","type":"text"},{"text":"render_strategy_card_md()","type":"text","marks":[{"type":"code_inline"}]},{"text":" — it works everywhere","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Tone & Presentation Rules","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Language detection.","type":"text","marks":[{"type":"strong"}]},{"text":" Detect the user's language from their first message. If Chinese, use ","type":"text"},{"text":"question_zh","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"label_zh","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"tag_zh","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"guidance_zh","type":"text","marks":[{"type":"code_inline"}]},{"text":", and ","type":"text"},{"text":"WELCOME_MESSAGE_ZH","type":"text","marks":[{"type":"code_inline"}]},{"text":". If English (or unclear), use the default English fields and ","type":"text"},{"text":"WELCOME_MESSAGE_EN","type":"text","marks":[{"type":"code_inline"}]},{"text":". Call ","type":"text"},{"text":"render_options(question, lang=\"zh\")","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"render_options(question, lang=\"en\")","type":"text","marks":[{"type":"code_inline"}]},{"text":" accordingly. Never mix both languages.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Casual, chill, gamified.","type":"text","marks":[{"type":"strong"}]},{"text":" You're a vibe trading assistant, not a finance textbook.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never show step numbers.","type":"text","marks":[{"type":"strong"}]},{"text":" The user should feel like a conversation, not a form.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never show internal state.","type":"text","marks":[{"type":"strong"}]},{"text":" No \"Step 2 of 6\", no JSON, no spec until the user asks.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"One question at a time.","type":"text","marks":[{"type":"strong"}]},{"text":" Don't dump all questions at once. Weave them into conversation.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Use markdown formatting.","type":"text","marks":[{"type":"strong"}]},{"text":" Bold for emphasis, italic for flavor text.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Options in bordered boxes.","type":"text","marks":[{"type":"strong"}]},{"text":" Present choices using the exact format from ","type":"text"},{"text":"render_options()","type":"text","marks":[{"type":"code_inline"}]},{"text":" — emoji icon + text inside a box-drawing border (","type":"text"},{"text":"┌─┐│└─┘","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Always include the freeform hint below the box.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Respond to freeform input.","type":"text","marks":[{"type":"strong"}]},{"text":" If the user doesn't pick an option, parse their intent and map it.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Keep it short.","type":"text","marks":[{"type":"strong"}]},{"text":" 2-4 sentences per message max, unless explaining strategy details.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 1: Onboarding & User Activation","type":"text"}]},{"type":"paragraph","content":[{"text":"Open with the welcome message from ","type":"text"},{"text":"coach.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" (","type":"text"},{"text":"WELCOME_MESSAGE","type":"text","marks":[{"type":"code_inline"}]},{"text":"). The vibe:","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"Welcome Builder! I see that you have made your way here, which means you need my help. Don't worry, I am here to help. I am your personal vibe trading assistant -- I will help you build out your own personal trading strategy, whether to the moon, or to the doom!\"","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Then immediately flow into the first profiling question. No bullet-point feature list. No corporate pitch.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 2: User Profiling","type":"text"}]},{"type":"paragraph","content":[{"text":"Ask these questions to build a ","type":"text"},{"text":"Trader Profile","type":"text","marks":[{"type":"strong"}]},{"text":". Adapt the language to the user's experience level. Don't ask all at once — weave them into conversation.","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Question","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"What it determines","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Options / Guidance","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Q1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"What do you want the bot to do?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Entry primitive selection","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DCA / buy dips / follow smart money / copy wallets / snipe new tokens / grid trade / trend follow","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Q2","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"How much per trade?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Sizing method","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Suggest $20-$200 for beginners. Flag >$500 as potentially risky for new users.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Q3","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"What tokens or chains?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Instrument + chain","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SOL, ETH, BTC, meme coins, \"whatever's trending\". Default to Solana if unsure.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Q4","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"How hands-on do you want to be?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Automation level + alerts","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"A: Fully auto (bot decides everything). B: Semi-auto (bot suggests, you approve). C: Manual signals only.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Q5","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"What's your risk comfort?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Stop-loss %, sizing %, max drawdown","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Conservative (max -8% SL, 2% sizing). Moderate (max -15% SL, 5% sizing). Aggressive (max -20% SL, 10% sizing).","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Q6","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Trading experience?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Complexity of suggested strategy","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Beginner: simple templates (DCA, dip buyer). Intermediate: indicator-based (MA cross, RSI). Advanced: full primitive composition.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Q7","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"Any specific wallets to follow?\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Copy-trade setup (optional)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Only ask if Q1 suggests copy-trading. Up to 3 wallet addresses.","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Profile output (stored as JSON):","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"goal\": \"dip_buy\",\n \"budget_per_trade\": 100,\n \"token\": \"SOL\",\n \"chain\": \"solana\",\n \"automation\": \"A\",\n \"risk_level\": \"moderate\",\n \"experience\": \"beginner\",\n \"target_wallets\": []\n}","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 3: Customize & Build Trading Strategy","type":"text"}]},{"type":"paragraph","content":[{"text":"Based on the profile, generate a strategy spec:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Suggest 1-3 approaches","type":"text","marks":[{"type":"strong"}]},{"text":" — map the user's goal to entry primitives using the heuristics in Section 3.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Let the user pick","type":"text","marks":[{"type":"strong"}]},{"text":" — explain each option in plain language (\"This one buys SOL whenever it drops 5% in an hour\").","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Generate the JSON spec","type":"text","marks":[{"type":"strong"}]},{"text":" — call ","type":"text"},{"text":"generate_strategy_spec(profile)","type":"text","marks":[{"type":"code_inline"}]},{"text":" from ","type":"text"},{"text":"llm_strategy.py","type":"text","marks":[{"type":"code_inline"}]},{"text":". This function is hallucination-hardened:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Auto-normalization pass","type":"text","marks":[{"type":"strong"}]},{"text":" (","type":"text"},{"text":"_normalize_spec","type":"text","marks":[{"type":"code_inline"}]},{"text":"): before any validation, common structural errors are auto-repaired silently — wrong exit placement, missing universe block, tiered TP percentages that don't sum to 100, etc.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Harness retry loop","type":"text","marks":[{"type":"strong"}]},{"text":" (max 3 attempts): after normalization, ","type":"text"},{"text":"validate_spec()","type":"text","marks":[{"type":"code_inline"}]},{"text":" runs. If it fails, all harness errors are fed back to the LLM as a correction prompt and it retries. The LLM sees its own mistakes and self-corrects.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fallback","type":"text","marks":[{"type":"strong"}]},{"text":": if all 3 attempts fail, ","type":"text"},{"text":"generate_strategy_spec()","type":"text","marks":[{"type":"code_inline"}]},{"text":" returns the best attempt + the remaining errors. In that case, fall back to the deterministic template via ","type":"text"},{"text":"get_fallback_theme()","type":"text","marks":[{"type":"code_inline"}]},{"text":" and tell the user: \"I used a safe template for your strategy type — it's been verified.\"","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validate via harness","type":"text","marks":[{"type":"strong"}]},{"text":" — ","type":"text"},{"text":"validate_spec(spec)","type":"text","marks":[{"type":"code_inline"}]},{"text":" is already called inside ","type":"text"},{"text":"generate_strategy_spec()","type":"text","marks":[{"type":"code_inline"}]},{"text":". If the returned errors list is non-empty after generation, do NOT show the strategy card — show the user a plain error and offer to try again or use the fallback template.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run OnchainOS live data verification","type":"text","marks":[{"type":"strong"}]},{"text":" — BEFORE showing the strategy card, call OnchainOS to verify all data sources are live and show the user real data. This step is MANDATORY. Every claim in the strategy card must be backed by a real OnchainOS call.","type":"text"}]},{"type":"paragraph","content":[{"text":"Prefer workflow commands","type":"text","marks":[{"type":"strong"}]},{"text":" (v2.5.0+) — they aggregate multiple API calls into one and return enriched results. Fall back to individual calls only if workflow fails.","type":"text"}]},{"type":"paragraph","content":[{"text":"For meme/sniper strategies:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"PRIMARY","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"oc.workflow_new_tokens(chain=chain, stage=\"MIGRATED\")","type":"text","marks":[{"type":"code_inline"}]},{"text":" → returns top 10 new migrated tokens with safety enrichment already done. Show 3–5 real candidates to the user.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"FALLBACK","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"onchainos token hot-tokens --chain \u003cchain> --ranking-type 4","type":"text","marks":[{"type":"code_inline"}]},{"text":" then individual token-dev-info, token-bundle-info, security token-scan per token.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Summarize: \"Here's what your safety filters would say about [TOKEN] right now: ✅ Honeypot: clean · ✅ Tax: 0% · ⚠️ Dev: 2 prior launches · ✅ Bundler: 3%\"","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"For smart money / copy-trade strategies:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"PRIMARY","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"oc.workflow_smart_money(chain=chain)","type":"text","marks":[{"type":"code_inline"}]},{"text":" → returns tokens aggregated by wallet buy signals with per-token due diligence already attached. Show top 3 tokens + wallet count + safety summary.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"FALLBACK","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"onchainos signal list --chain \u003cchain> --wallet-type 1","type":"text","marks":[{"type":"code_inline"}]},{"text":" then ","type":"text"},{"text":"onchainos token holders","type":"text","marks":[{"type":"code_inline"}]},{"text":" on a signaled token.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For copy-trade: also run ","type":"text"},{"text":"oc.workflow_wallet_analysis(address=wallet_addr, chain=chain)","type":"text","marks":[{"type":"code_inline"}]},{"text":" → show the user 7d/30d performance of the wallet they're copying (win rate, avg PnL, recent trades).","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"For DCA / trend / dip-buy strategies (fixed token):","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"PRIMARY","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"oc.workflow_token_research(address=token_addr, chain=chain)","type":"text","marks":[{"type":"code_inline"}]},{"text":" → returns price, security, holders, signals all in one call. Show price + safety summary + any active signals.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"FALLBACK","type":"text","marks":[{"type":"strong"}]},{"text":": ","type":"text"},{"text":"onchainos token price-info","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"onchainos market kline","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"onchainos security token-scan","type":"text","marks":[{"type":"code_inline"}]},{"text":" separately.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"All strategies — always run:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"oc.swap_quote(USDC_addr, token_addr, str(sizing_usd), WALLET_ADDRESS)","type":"text","marks":[{"type":"code_inline"}]},{"text":" → show the user a real swap quote so they know execution works and what slippage looks like.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Show the results inline in plain language before the strategy card. Never skip this step. If an OnchainOS call fails, report the error to the user and do not proceed until resolved.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Show the strategy card","type":"text","marks":[{"type":"strong"}]},{"text":" — use ","type":"text"},{"text":"render_strategy_card()","type":"text","marks":[{"type":"code_inline"}]},{"text":" (terminal) or ","type":"text"},{"text":"render_strategy_card_md()","type":"text","marks":[{"type":"code_inline"}]},{"text":" (all other environments). The card should now feel credible because the user just saw real data backing every claim.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Show the Congrats message","type":"text","marks":[{"type":"strong"}]},{"text":" — immediately after the strategy card, always send a celebratory message. Tone: warm, hype, casual. Make the user feel proud and capable. Key points to hit:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"They just built a real trading strategy — that's actually impressive","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"It wasn't hard — most people think this is complicated but they just did it in minutes","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The strategy is theirs — personalized to their goal, risk level, and budget","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"They're not done yet (paper trade next) but this is a huge first step","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Example (adapt to their specific goal/tokens, never copy-paste verbatim):","type":"text"}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"🎉 ","type":"text"},{"text":"You just built a trading strategy.","type":"text","marks":[{"type":"strong"}]},{"text":" Seriously — that's it. Most people think algo trading is for quants with PhDs. You just proved it's not. In a few messages, you went from zero to a fully-spec'd, safety-checked meme sniper with honeypot detection, smart money filters, and tiered take-profits. That's yours. Nobody else has that exact setup. Now let's make sure it actually works before we put real money on it 👇","type":"text"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Ask how they want to run it","type":"text","marks":[{"type":"strong"}]},{"text":" — check ","type":"text"},{"text":"get_current_step_info()","type":"text","marks":[{"type":"code_inline"}]},{"text":" for ","type":"text"},{"text":"needs_run_mode: True","type":"text","marks":[{"type":"code_inline"}]},{"text":", then present the run-mode question using ","type":"text"},{"text":"render_options()","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"💬 Trade in chat — I'll guide every move, just talk to me\n🖥️ Python bot — Generate a script I can run 24/7","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If user picks ","type":"text"},{"text":"chat","type":"text","marks":[{"type":"strong"}]},{"text":": call ","type":"text"},{"text":"set_run_mode(state, \"chat\")","type":"text","marks":[{"type":"code_inline"}]},{"text":" → advance to Step 4 chat mode","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If user picks ","type":"text"},{"text":"Python bot","type":"text","marks":[{"type":"strong"}]},{"text":": call ","type":"text"},{"text":"set_run_mode(state, \"python\")","type":"text","marks":[{"type":"code_inline"}]},{"text":" then:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Call ","type":"text"},{"text":"generate_bot_script(state)","type":"text","marks":[{"type":"code_inline"}]},{"text":" → get ","type":"text"},{"text":"(filename, script_content)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Write the file to disk","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"MANDATORY: call ","type":"text","marks":[{"type":"strong"}]},{"text":"verify_bot_script(filepath, code)","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" — three-layer harness: syntax check → OnchainOS method validation → dashboard smoke-test. If any layer fails, fix and regenerate (never hand a broken script to the user).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Only after the syntax check passes: show the user the filename + ","type":"text"},{"text":"python3 \u003cfilename>","type":"text","marks":[{"type":"code_inline"}]},{"text":" quick-start command","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Advance to Step 4","type":"text"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Welcome message:","type":"text","marks":[{"type":"strong"}]},{"text":" Use ","type":"text"},{"text":"WELCOME_MESSAGE_EN_PLAIN","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"WELCOME_MESSAGE_ZH_PLAIN","type":"text","marks":[{"type":"code_inline"}]},{"text":" in non-terminal environments (Claude app, web, Telegram). Use ","type":"text"},{"text":"WELCOME_MESSAGE_EN","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"WELCOME_MESSAGE_ZH","type":"text","marks":[{"type":"code_inline"}]},{"text":" (with ASCII art) only in terminal/Claude Code.","type":"text"}]},{"type":"paragraph","content":[{"text":"Goal-to-template mapping:","type":"text","marks":[{"type":"strong"}]}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User goal","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Suggested entry","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Suggested exit stack","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Key filters","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DCA / passive","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"time_schedule","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"trailing_stop","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"None needed","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Buy the dip","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"price_drop","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"take_profit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"time_window","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"cooldown","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Trend follow","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ma_cross","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"macd_cross","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"trailing_stop","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"market_regime","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"btc_overlay","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mean revert","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"rsi_threshold","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"bollinger_touch","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"take_profit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"volatility_range","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Copy wallet","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet_copy_buy","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet_mirror_sell","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"dev_dump","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Safety stack","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Smart money","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"smart_money_buy","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"smart_money_sell","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"smart_money_present_min","type":"text","marks":[{"type":"code_inline"}]},{"text":" + safety stack","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Meme sniper","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ranking_entry","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tiered_take_profit","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"fast_dump_exit","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full safety stack","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Grid trade","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"grid","type":"text","marks":[{"type":"code_inline"}]},{"text":" meta-template","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Auto-composed","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"price_range","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"For beginners","type":"text","marks":[{"type":"strong"}]},{"text":": default to conservative params, add ","type":"text"},{"text":"cooldown","type":"text","marks":[{"type":"code_inline"}]},{"text":" filter, add ","type":"text"},{"text":"session_loss_pause","type":"text","marks":[{"type":"code_inline"}]},{"text":" overlay. ","type":"text"},{"text":"For meme/live_only","type":"text","marks":[{"type":"strong"}]},{"text":": always add the full safety filter stack (TF-01 through TF-13).","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 4: Paper Trade or Backtest","type":"text"}]},{"type":"paragraph","content":[{"text":"Route based on ","type":"text"},{"text":"both","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"run_mode","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"meta.live_only","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"paragraph","content":[{"text":"If ","type":"text","marks":[{"type":"strong"}]},{"text":"run_mode == \"python\"","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":":","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"The bot script is already generated. Tell the user: \"Run ","type":"text"},{"text":"python3 \u003cfilename>","type":"text","marks":[{"type":"code_inline"}]},{"text":" — it starts in paper mode by default (","type":"text"},{"text":"PAPER_TRADE = True","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Watch the output for signals.\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Guide them to observe a few paper trades, then move to Step 5 when ready.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If ","type":"text","marks":[{"type":"strong"}]},{"text":"run_mode == \"chat\"","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":":","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Walk through live trades using OnchainOS MCP tools inline. Every step is a real OnchainOS call — nothing is simulated or fabricated.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Signal check","type":"text","marks":[{"type":"strong"}]},{"text":": run ","type":"text"},{"text":"onchainos token hot-tokens","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"onchainos signal list","type":"text","marks":[{"type":"code_inline"}]},{"text":" → find a real candidate that matches the spec's entry criteria right now","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Safety scan","type":"text","marks":[{"type":"strong"}]},{"text":": run the full filter stack on that candidate — ","type":"text"},{"text":"token-dev-info","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"token-bundle-info","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"security token-scan","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"token holders","type":"text","marks":[{"type":"code_inline"}]},{"text":" — show pass/fail per filter","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Entry","type":"text","marks":[{"type":"strong"}]},{"text":": run ","type":"text"},{"text":"onchainos swap quote","type":"text","marks":[{"type":"code_inline"}]},{"text":" → show the user the exact quote, price impact, and route → ask \"Want to enter at this price?\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Position monitoring","type":"text","marks":[{"type":"strong"}]},{"text":": run ","type":"text"},{"text":"onchainos token price-info","type":"text","marks":[{"type":"code_inline"}]},{"text":" to show current P&L vs entry","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Exit","type":"text","marks":[{"type":"strong"}]},{"text":": when exit condition triggers, run ","type":"text"},{"text":"onchainos swap quote","type":"text","marks":[{"type":"code_inline"}]},{"text":" on the exit leg → confirm with user → execute","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"After each completed trade cycle, show a plain-language summary: entry price, exit price, P&L, which exit triggered.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"After 2-3 completed trade cycles, ask if they're ready to go live.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Route also based on ","type":"text"},{"text":"meta.live_only","type":"text","marks":[{"type":"code_inline"}]},{"text":":","type":"text"}]},{"type":"paragraph","content":[{"text":"If backtestable","type":"text","marks":[{"type":"strong"}]},{"text":" (no live_only primitives):","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Fetch historical candles via OnchainOS: ","type":"text"},{"text":"onchainos market kline --address \u003ctoken> --chain \u003cchain> --bar \u003ctimeframe> --limit 299","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"backtest_engine.run_backtest(spec, bars)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Present results in plain language:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Over the last 30 days, your strategy made 24 trades. 15 won, 9 lost. Net profit: +$127 (+12.7%). Max drawdown: -6.2%. Sharpe ratio: 1.4.\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Compare to buy-and-hold: \"If you just held SOL, you'd be up 8%. Your strategy beat buy-and-hold by 4.7%.\"","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If results are poor (Sharpe \u003c 0.5, drawdown > 15%, win rate \u003c 35%), suggest ONE improvement at a time and re-run.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"If live_only","type":"text","marks":[{"type":"strong"}]},{"text":" (any live_only primitive):","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Explain: \"This strategy uses real-time on-chain data that can't be replayed historically. We'll paper-trade first.\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Enter paper-trade mode via ","type":"text"},{"text":"paper_gate.record_paper_trade()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Paper-trade graduation requirements:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"= 10 paper trades completed","type":"text"}]}]}]},{"type":"list_item","content":[{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"= 5 live micro-trades (10% of spec size)","type":"text"}]}]}]},{"type":"list_item","content":[{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"= 7 calendar days observed","type":"text"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"0 harness breaches","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Show progress: ","type":"text"},{"text":"paper_gate.check_graduation(strategy_name)","type":"text","marks":[{"type":"code_inline"}]},{"text":" → progress summary","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 5: Go Live (User's Choice)","type":"text"}]},{"type":"paragraph","content":[{"text":"Only proceed when:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Backtestable","type":"text","marks":[{"type":"strong"}]},{"text":": backtest shows positive expectancy (Sharpe >= 0.8, max DD \u003c= 15%)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Live_only","type":"text","marks":[{"type":"strong"}]},{"text":": paper-trade graduation gate passed","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Deployment steps:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pre-flight check via OnchainOS","type":"text","marks":[{"type":"strong"}]},{"text":" — run ALL of these before asking the user to go live:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"onchainos wallet balance --chain \u003cchain>","type":"text","marks":[{"type":"code_inline"}]},{"text":" → confirm wallet has enough balance to fund at least 3 trades","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"onchainos swap quote --from \u003cUSDC_addr> --to \u003ctoken_addr> --readable-amount \u003csizing_usd> --chain \u003cchain>","type":"text","marks":[{"type":"code_inline"}]},{"text":" → confirm swap execution path works","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"onchainos signal list --chain \u003cchain>","type":"text","marks":[{"type":"code_inline"}]},{"text":" → confirm live signals are flowing (data feed is healthy)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Show the user the results: \"✅ Wallet funded · ✅ Swap route verified · ✅ Signal feed live\"","type":"text"}]}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Confirm with user: \"Everything checks out. Want to go live with real money?\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Execute trades via: ","type":"text"},{"text":"onchainos swap execute --from \u003cquote> --to \u003ctoken> --readable-amount \u003camt> --chain \u003cchain> --wallet \u003caddr>","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"After each live trade, run ","type":"text"},{"text":"onchainos token price-info","type":"text","marks":[{"type":"code_inline"}]},{"text":" to show current position P&L","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Explain the safety net: stop-loss, risk overlays, daily trade caps","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Never auto-deploy without explicit user consent.","type":"text","marks":[{"type":"strong"}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Step 6: Auto-Evolve Engine (Optional)","type":"text"}]},{"type":"paragraph","content":[{"text":"Unlock criteria","type":"text","marks":[{"type":"strong"}]},{"text":" (all must be met):","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"30+ live trades completed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Positive expectancy (strategy is profitable)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"User explicitly opts in (\"I want auto-evolve\" — never auto-enabled)","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"What it does","type":"text","marks":[{"type":"strong"}]},{"text":" — daily 5-phase cycle:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Collect","type":"text","marks":[{"type":"strong"}]},{"text":" — last 24h trade data + market data via OnchainOS","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Research","type":"text","marks":[{"type":"strong"}]},{"text":" — current market regime, volatility, volume patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Reflect","type":"text","marks":[{"type":"strong"}]},{"text":" — compare recent performance to baseline; compute confidence score (0.0–1.0)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Adjust","type":"text","marks":[{"type":"strong"}]},{"text":" — if confidence >= 0.6, propose parameter tweaks within harness bounds. If \u003c 0.6, do nothing.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Report","type":"text","marks":[{"type":"strong"}]},{"text":" — daily summary to user of what happened and any changes","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Boundaries:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CAN tune: stop-loss %, take-profit %, RSI levels, position size, cooldown bars (all within schema bounds)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"CANNOT change: strategy type, add/remove primitives, switch chains, increase beyond L3 limits","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Strategy type changes require user initiation and a new backtest cycle","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Side Note: OnchainOS Full API Reference","type":"text"}]},{"type":"paragraph","content":[{"text":"All data and execution flows through OnchainOS CLI (","type":"text"},{"text":"onchainos.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" wrapper). Every method below is implemented in ","type":"text"},{"text":"onchainos.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" — use it, never call raw CLI directly.","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Wallet & Auth","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Need","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Method","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CLI Command","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Check login status","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.wallet_status()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet status","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Resolve wallet address","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_wallet_address()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet addresses --chain \u003cid>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All token balances","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_all_balances()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet balance --chain \u003cid>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Single token balance","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_token_balance(addr)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet balance --chain \u003cid> --token-address \u003caddr>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Transaction history","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_wallet_history(limit)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet history --chain \u003cid> --limit \u003cn>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Confirm tx status","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_tx_detail(tx_hash, addr)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet history --tx-hash \u003chash>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TEE sign + broadcast","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.wallet_contract_call(to, unsigned_tx)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet contract-call --chain \u003cid> --to \u003caddr> --unsigned-tx \u003cdata>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Portfolio balances","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_portfolio_balances()","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"portfolio all-balances --chain \u003cid>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Token PnL","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_portfolio_token_pnl(wallet, token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"market portfolio-token-pnl --chain \u003cid> --address \u003cwallet> --token \u003caddr>","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Token Data","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Need","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Method","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CLI Command","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Price, mcap, volume","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_price_info(token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"token price-info --address \u003caddr> --chain \u003cchain>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Advanced info (risk, age, dev)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_advanced_info(token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"token advanced-info --address \u003caddr> --chain \u003cchain>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Basic info (name, symbol)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_basic_info(token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"token info --address \u003caddr> --chain \u003cchain>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"LP pool / liquidity","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_token_liquidity(token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"token liquidity --address \u003caddr> --chain \u003cchain>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Holders by tag","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_holders(token, tag_filter)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"token holders --address \u003caddr> --chain \u003cchain> --tag-filter \u003cn>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Recent trades","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_token_trades(token, limit)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"token trades --address \u003caddr> --chain \u003cchain> --limit \u003cn>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Full safety tags","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_safety_tags(token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"composite (security + advanced + holders + bundle)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Security / honeypot","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.security_scan(token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"security token-scan --tokens \"chainId:addr\"","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Batch prices","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_batch_prices([(addr,chain),...])","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"market prices --tokens \"chainId:addr,...\"","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Rankings & Discovery","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Need","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Method","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CLI Command","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Trending / gainers / volume","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_token_trending(sort_by, time_frame)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"token trending --chain \u003cchain> --sort-by \u003csort> --time-frame \u003ctf>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Hot tokens score","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_hot_tokens(ranking_type, top_n)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"token hot-tokens --chain \u003cchain> --ranking-type \u003cn>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"New pump.fun launches","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_memepump_tokens(stage, **filters)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"memepump tokens --chain \u003cchain> --stage bonding","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Token full details","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_memepump_token_details(token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"memepump token-details --chain \u003cchain> --address \u003caddr>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dev history / rugs","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_dev_info(token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"memepump token-dev-info --chain \u003cchain> --address \u003caddr>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Bundle / sniper %","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_bundle_info(token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"memepump token-bundle-info --chain \u003cchain> --address \u003caddr>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Co-invested wallets","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_aped_wallets(token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"memepump aped-wallet --chain \u003cchain> --address \u003caddr>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Same-dev tokens","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_similar_tokens(token)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"memepump similar-tokens --chain \u003cchain> --address \u003caddr>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Spec list_name routing","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.subscribe_ranking(list_name, top_n)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"routes to trending/memepump/hot-tokens automatically","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Signals & Tracking","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Need","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Method","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CLI Command","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Smart money buy signals","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_signals(wallet_type=1)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"signal list --chain \u003cchain> --wallet-type 1","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"KOL signals","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_signals(wallet_type=2)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"signal list --chain \u003cchain> --wallet-type 2","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Whale signals","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_signals(wallet_type=3)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"signal list --chain \u003cchain> --wallet-type 3","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Track smart money activity","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.track_smart_money(trade_type)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tracker activities --tracker-type smart_money","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Track KOL activity","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.track_kol(trade_type)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tracker activities --tracker-type kol","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Track custom wallets","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.track_wallets(wallets, trade_type)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tracker activities --tracker-type multi_address --wallet-address \u003caddrs>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Track with filters","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.track_with_filters(tracker_type, **filters)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tracker activities","type":"text","marks":[{"type":"code_inline"}]},{"text":" with all filter flags","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Market Data","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Need","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Method","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CLI Command","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Candle / OHLCV","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.get_candles(token, bar, limit)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"market kline --address \u003caddr> --chain \u003cchain> --bar \u003cbar> --limit \u003cn>","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Execution","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Need","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Method","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"CLI Command","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Swap quote (no execution)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.swap_quote(from, to, amount)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"swap quote --from \u003caddr> --to \u003caddr> --readable-amount \u003camt>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Execute swap","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.swap_execute(from, to, amount, wallet)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"swap execute --from \u003caddr> --to \u003caddr> --readable-amount \u003camt> --chain \u003cchain> --wallet \u003caddr>","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Execute with MEV protection","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"oc.swap_execute(..., mev_protection=True)","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"swap execute ... --mev-protection","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"paragraph","content":[{"text":"Tag filter values for ","type":"text","marks":[{"type":"strong"}]},{"text":"get_holders()","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" / ","type":"text","marks":[{"type":"strong"}]},{"text":"get_token_trades()","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":":","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"1=KOL 2=Developer 3=Smart Money 4=Whale 5=Fresh Wallet 6=Insider 7=Sniper 8=Phishing 9=Bundler","type":"text","marks":[{"type":"code_inline"}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"1. Spec Shape","type":"text"}]},{"type":"paragraph","content":[{"text":"Every spec is a JSON object with these top-level keys:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"{\n \"meta\": { name, version?, risk_tier?, description?, author_intent?, live_only? },\n \"instrument\": { symbol, timeframe },\n \"universe\": { selector, chain } // required only when symbol is \"*\"\n \"entry\": { type: \"...\", ...params }, // exactly 1 entry primitive\n \"exit\": { stop_loss: {pct}, ...}, // stop_loss always required (H-01)\n \"sizing\": { type: \"...\", ...params }, // exactly 1 sizing primitive\n \"filters\": [ {type: \"...\", ...}, ... ], // 0+ filter primitives\n \"risk_overlays\": [ {type: \"...\", ...}, ... ], // 0+ risk overlay primitives\n \"grid\": { ... } // meta-template, mutually exclusive with entry/exit/sizing\n}","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Key structural rules:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"meta.name","type":"text","marks":[{"type":"code_inline"}]},{"text":" must be ","type":"text"},{"text":"^[a-z0-9_]{3,64}$","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"instrument.symbol","type":"text","marks":[{"type":"code_inline"}]},{"text":" is either ","type":"text"},{"text":"\"TOKEN-QUOTE\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" (e.g. ","type":"text"},{"text":"SOL-USDC","type":"text","marks":[{"type":"code_inline"}]},{"text":") or ","type":"text"},{"text":"\"*\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" for dynamic-universe strategies","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When ","type":"text"},{"text":"symbol","type":"text","marks":[{"type":"code_inline"}]},{"text":" is ","type":"text"},{"text":"\"*\"","type":"text","marks":[{"type":"code_inline"}]},{"text":", the ","type":"text"},{"text":"universe","type":"text","marks":[{"type":"code_inline"}]},{"text":" block is ","type":"text"},{"text":"required","type":"text","marks":[{"type":"strong"}]},{"text":" (G-02) with ","type":"text"},{"text":"selector","type":"text","marks":[{"type":"code_inline"}]},{"text":" naming the entry primitive that produces tokens and ","type":"text"},{"text":"chain","type":"text","marks":[{"type":"code_inline"}]},{"text":" specifying the chain","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"instrument.timeframe","type":"text","marks":[{"type":"code_inline"}]},{"text":" is one of: ","type":"text"},{"text":"1m","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"5m","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"15m","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"1H","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"4H","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"1D","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"exit.stop_loss","type":"text","marks":[{"type":"code_inline"}]},{"text":" is always required (H-01). Other exits are optional.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Inner exits (","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"take_profit","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"trailing_stop","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"tiered_take_profit","type":"text","marks":[{"type":"code_inline"}]},{"text":") go directly on the ","type":"text"},{"text":"exit","type":"text","marks":[{"type":"code_inline"}]},{"text":" object","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Additional exits go in ","type":"text"},{"text":"exit.other[]","type":"text","marks":[{"type":"code_inline"}]},{"text":" array (for: ","type":"text"},{"text":"time_exit","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"indicator_reversal","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"smart_money_sell","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"dev_dump","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"wallet_mirror_sell","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"fast_dump_exit","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"2. Primitive Library — 53 Primitives","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2.1 Entry Triggers (12) — pick exactly 1","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Params (bold = required)","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Live-only","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-01","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"price_drop","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct","type":"text","marks":[{"type":"strong"}]},{"text":" [1,30], ","type":"text"},{"text":"lookback_bars","type":"text","marks":[{"type":"strong"}]},{"text":" [6,720]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dip buyer","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-02","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"price_breakout","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"direction","type":"text","marks":[{"type":"strong"}]},{"text":" (up|down), ","type":"text"},{"text":"lookback_bars","type":"text","marks":[{"type":"strong"}]},{"text":" [6,720], confirm_pct [0,5]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Momentum","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-03","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ma_cross","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fast_period","type":"text","marks":[{"type":"strong"}]},{"text":" [5,50], ","type":"text"},{"text":"slow_period","type":"text","marks":[{"type":"strong"}]},{"text":" [10,200], ma_type (SMA|EMA)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Trend","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-04","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"rsi_threshold","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"period","type":"text","marks":[{"type":"strong"}]},{"text":" [7,30], ","type":"text"},{"text":"level","type":"text","marks":[{"type":"strong"}]},{"text":" [10,90], ","type":"text"},{"text":"direction","type":"text","marks":[{"type":"strong"}]},{"text":" (cross_up|cross_down)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mean-revert","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-05","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"volume_spike","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"multiplier","type":"text","marks":[{"type":"strong"}]},{"text":" [1.5,10], ","type":"text"},{"text":"avg_bars","type":"text","marks":[{"type":"strong"}]},{"text":" [12,168]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Smart-money footprint","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-06","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"time_schedule","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"interval","type":"text","marks":[{"type":"strong"}]},{"text":" (1H|4H|1D|1W), anchor_utc \"HH:MM\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DCA / grid","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-07","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"smart_money_buy","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"min_wallets","type":"text","marks":[{"type":"strong"}]},{"text":" [1,20], ","type":"text"},{"text":"window_min","type":"text","marks":[{"type":"strong"}]},{"text":" [5,1440], min_usd_each [100,1M]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Event-driven on SM buy tx. See G-05.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-08","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dev_buy","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"min_usd","type":"text","marks":[{"type":"strong"}]},{"text":" [100,1M], window_min [5,1440]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Deployer re-commit","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-09","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"macd_cross","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fast_period","type":"text","marks":[{"type":"strong"}]},{"text":" [5,20], ","type":"text"},{"text":"slow_period","type":"text","marks":[{"type":"strong"}]},{"text":" [15,50], ","type":"text"},{"text":"signal_period","type":"text","marks":[{"type":"strong"}]},{"text":" [5,20], ","type":"text"},{"text":"direction","type":"text","marks":[{"type":"strong"}]},{"text":" (cross_up|cross_down)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Momentum indicator","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-10","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bollinger_touch","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"period","type":"text","marks":[{"type":"strong"}]},{"text":" [10,50], ","type":"text"},{"text":"std_dev","type":"text","marks":[{"type":"strong"}]},{"text":" [1.5,3.0], ","type":"text"},{"text":"band","type":"text","marks":[{"type":"strong"}]},{"text":" (upper|lower)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mean-revert / breakout","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-11","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ranking_entry","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"list_name","type":"text","marks":[{"type":"strong"}]},{"text":" (gainers|volume|trending|new), ","type":"text"},{"text":"top_n","type":"text","marks":[{"type":"strong"}]},{"text":" [1,50], timeframe (1H|4H|24H)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"List-snipe","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"E-12","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet_copy_buy","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"target_wallet","type":"text","marks":[{"type":"strong"}]},{"text":" (string or string[]), ","type":"text"},{"text":"min_usd","type":"text","marks":[{"type":"strong"}]},{"text":" [10,100k], ","type":"text"},{"text":"mirror_mode","type":"text","marks":[{"type":"strong"}]},{"text":" (instant|mcap_target)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Copy-trade","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2.2 Exit Conditions (10) — stop_loss always required","type":"text"}]},{"type":"paragraph","content":[{"text":"Inner exits","type":"text","marks":[{"type":"strong"}]},{"text":" (direct keys on ","type":"text"},{"text":"exit","type":"text","marks":[{"type":"code_inline"}]},{"text":" object):","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Params","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Live-only","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"X-01","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct","type":"text","marks":[{"type":"strong"}]},{"text":" [1,20]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Required","type":"text","marks":[{"type":"strong"}]},{"text":" (H-01). Fixed % loss from entry.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"X-02","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"take_profit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct","type":"text","marks":[{"type":"strong"}]},{"text":" [2,100]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fixed % gain from entry.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"X-03","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"trailing_stop","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct","type":"text","marks":[{"type":"strong"}]},{"text":" [1,20], activate_after_pct [0,50]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Trails below peak.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"X-04","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tiered_take_profit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tiers","type":"text","marks":[{"type":"strong"}]},{"text":" [{","type":"text"},{"text":"pct_gain","type":"text","marks":[{"type":"strong"}]},{"text":" [2,1000], ","type":"text"},{"text":"pct_sell","type":"text","marks":[{"type":"strong"}]},{"text":" [5,100]}] min 2 max 5, runner_mode (hold|trail)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Multi-level scale-out.","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Other exits","type":"text","marks":[{"type":"strong"}]},{"text":" (go in ","type":"text"},{"text":"exit.other[]","type":"text","marks":[{"type":"code_inline"}]},{"text":" array):","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Params","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Live-only","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"X-05","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"time_exit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_bars","type":"text","marks":[{"type":"strong"}]},{"text":" [1,720]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Max-hold from entry time (G-06).","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"X-06","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"indicator_reversal","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"mirror_entry","type":"text","marks":[{"type":"strong"}]},{"text":" (bool)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Exit when entry signal flips.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"X-07","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"smart_money_sell","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"min_wallets","type":"text","marks":[{"type":"strong"}]},{"text":" [1,20], ","type":"text"},{"text":"window_min","type":"text","marks":[{"type":"strong"}]},{"text":" [5,1440]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Follow smart-money out.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"X-08","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dev_dump","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"min_usd","type":"text","marks":[{"type":"strong"}]},{"text":" [100,1M], min_pct_of_holding [1,100]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rug-alert, market-order priority.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"X-09","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet_mirror_sell","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"target_wallet","type":"text","marks":[{"type":"strong"}]},{"text":" (string or string[]), min_pct_sold [10,100]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Copy-trade exit.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"X-10","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fast_dump_exit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"drop_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [3,50], ","type":"text"},{"text":"window_sec","type":"text","marks":[{"type":"strong"}]},{"text":" [5,300]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Emergency crash guard.","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2.3 Filters — Market Conditions (10)","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Params","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Live-only","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MF-01","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"time_window","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"start_hour","type":"text","marks":[{"type":"strong"}]},{"text":" [0,23], ","type":"text"},{"text":"end_hour","type":"text","marks":[{"type":"strong"}]},{"text":" [0,23], weekdays_only (bool)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MF-02","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"volatility_range","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"atr_period","type":"text","marks":[{"type":"strong"}]},{"text":" [7,50], min_pct [0,20], max_pct [0,50]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MF-03","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"volume_minimum","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"min_usd_24h","type":"text","marks":[{"type":"strong"}]},{"text":" [100k,+inf]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MF-04","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"cooldown","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bars","type":"text","marks":[{"type":"strong"}]},{"text":" [1,168]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MF-05","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"market_regime","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"regime","type":"text","marks":[{"type":"strong"}]},{"text":" (up|down|range|any), ma_period [50,500]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MF-06","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"price_range","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"min_price (>0), max_price (>0) — at least one required","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MF-07","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"btc_overlay","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"condition","type":"text","marks":[{"type":"strong"}]},{"text":" (above_ma|green_candle|uptrend), ma_period [20,200]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MF-08","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"top_zone_guard","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_zone_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [50,95], ","type":"text"},{"text":"lookback_bars","type":"text","marks":[{"type":"strong"}]},{"text":" [12,720]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MF-09","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"mcap_range","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"min_usd [1k,100B], max_usd [1k,100B] — at least one required","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text","marks":[{"type":"strong"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"MF-10","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"launch_age","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"min_hours [0,8760], max_hours [1,8760] — at least one required","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Yes","type":"text","marks":[{"type":"strong"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2.4 Filters — Token Safety (13, all live_only, all OnchainOS-backed)","type":"text"}]},{"type":"paragraph","content":[{"text":"Auto-skip these for whitelisted blue chips (ETH, SOL, BTC, WBTC, WETH).","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Params","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-01","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"honeypot_check","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"(no params)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Binary pass/fail.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-02","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"lp_locked","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"min_pct_locked","type":"text","marks":[{"type":"strong"}]},{"text":" [50,100], min_lock_days [7,3650]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"LP burned or time-locked.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-03","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"buy_tax_max","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [0,15]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reject if buy tax exceeds threshold.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-04","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"sell_tax_max","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [0,15]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"High sell tax = soft honeypot.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-05","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"liquidity_min","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"min_usd","type":"text","marks":[{"type":"strong"}]},{"text":" [5k,10M]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"On-chain pool liquidity floor.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-06","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"top_holders_max","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"top_n","type":"text","marks":[{"type":"strong"}]},{"text":" [5,20], ","type":"text"},{"text":"max_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [15,60]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Concentration cap.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-07","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bundler_ratio_max","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [5,50]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Sniper guard.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-08","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dev_holding_max","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [0,20]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dev-dump risk.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-09","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"insider_holding_max","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [0,30]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Team-dump risk.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-10","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fresh_wallet_ratio_max","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [20,80], fresh_def (age_days|tx_count)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Wash-trade guard.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-11","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"smart_money_present_min","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"min_wallets","type":"text","marks":[{"type":"strong"}]},{"text":" [1,20]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"State check at entry time (G-05).","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-12","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"phishing_exclude","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"(no params)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Blacklist check. Binary.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"TF-13","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"whale_concentration_max","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [3,25]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Single largest non-LP wallet.","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2.5 Sizing (3) — pick exactly 1, L3 hard bound: max 10% per trade","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Params","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"S-01","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fixed_pct","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pct","type":"text","marks":[{"type":"strong"}]},{"text":" [0.5,10]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"% of current equity.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"S-02","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fixed_usd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"usd","type":"text","marks":[{"type":"strong"}]},{"text":" [10,10000]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fixed dollar amount.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"S-03","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"volatility_scaled","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"target_risk_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [0.1,2], atr_period [7,50]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Smaller in volatile markets.","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2.6 Risk Overlays (5) — 0+ allowed, portfolio-level caps","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"#","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Type","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Params","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Notes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"R-01","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_daily_trades","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"n","type":"text","marks":[{"type":"strong"}]},{"text":" [1,50]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Hard cap on entries per 24h.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"R-02","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_concurrent_positions","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"n","type":"text","marks":[{"type":"strong"}]},{"text":" [1,10]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Max open positions.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"R-03","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"drawdown_pause","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"pause_pct","type":"text","marks":[{"type":"strong"}]},{"text":" [3,15], resume_pct [0,10]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Pause entries on equity drawdown.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"R-04","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"correlation_cap","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"mode","type":"text","marks":[{"type":"strong"}]},{"text":" (same_token_dedupe), ","type":"text"},{"text":"max_correlated","type":"text","marks":[{"type":"strong"}]},{"text":" [1,5]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"v1.0: same_token_dedupe only (G-04).","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"R-05","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"session_loss_pause","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_consecutive_losses","type":"text","marks":[{"type":"strong"}]},{"text":" [2,10], session_hours [1,24]","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tilt guard.","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2.7 Grid Meta-Template","type":"text"}]},{"type":"paragraph","content":[{"text":"Shorthand for grid trading. When ","type":"text"},{"text":"grid","type":"text","marks":[{"type":"code_inline"}]},{"text":" key is present, ","type":"text"},{"text":"entry","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"exit","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"sizing","type":"text","marks":[{"type":"code_inline"}]},{"text":" must NOT be present (mutually exclusive). The harness expands it into composed primitives.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"\"grid\": {\n \"price_min\": 80, // required, > 0\n \"price_max\": 120, // required, > 0\n \"levels\": 10, // required, [2,50]\n \"usd_per_level\": 100, // required, [10,10000]\n \"take_profit_per_level_pct\": 3, // optional, [0.5,20], default 3\n \"portfolio_stop_loss_pct\": 20 // optional, [5,50], default 20\n}","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"3. Primitive Selection Heuristics","type":"text"}]},{"type":"paragraph","content":[{"text":"Use these rules when translating user intent to primitives:","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Entry selection","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User says...","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Why","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"buy the dip\", \"buy when it drops X%\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"price_drop","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Percentage-based dip","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"buy on breakout\", \"new highs\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"price_breakout","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Momentum break","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"golden cross\", \"MA crossover\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ma_cross","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Trend following","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"oversold\", \"RSI below 30\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"rsi_threshold","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mean reversion","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"volume surge\", \"unusual volume\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"volume_spike","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Accumulation signal","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"DCA\", \"buy every week/day\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"time_schedule","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Fixed cadence","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"when smart money buys\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"smart_money_buy","type":"text","marks":[{"type":"code_inline"}]},{"text":" (entry)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Event-driven, live_only","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"only if smart money is already in\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"smart_money_present_min","type":"text","marks":[{"type":"code_inline"}]},{"text":" (filter)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"State check, live_only","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"when the dev buys back\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dev_buy","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Re-commit signal, live_only","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"MACD crossover\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"macd_cross","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Momentum indicator","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"touches lower Bollinger Band\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"bollinger_touch","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Band touch","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"trending tokens\", \"top gainers\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ranking_entry","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"List-snipe, live_only","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"copy this wallet\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet_copy_buy","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mirror trades, live_only","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Exit selection","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User says...","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"stop loss at X%\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]},{"text":" (always add this)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"take profit at X%\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"take_profit","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"trailing stop\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"trailing_stop","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"sell 33% at 2x, 33% at 5x, rest at 10x\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"tiered_take_profit","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"hold for max N hours/bars\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"time_exit","type":"text","marks":[{"type":"code_inline"}]},{"text":" (in ","type":"text"},{"text":"exit.other[]","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"exit when indicator flips\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"indicator_reversal","type":"text","marks":[{"type":"code_inline"}]},{"text":" (in ","type":"text"},{"text":"exit.other[]","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"exit when smart money sells\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"smart_money_sell","type":"text","marks":[{"type":"code_inline"}]},{"text":" (in ","type":"text"},{"text":"exit.other[]","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"bail if dev dumps\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"dev_dump","type":"text","marks":[{"type":"code_inline"}]},{"text":" (in ","type":"text"},{"text":"exit.other[]","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"mirror their sells\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"wallet_mirror_sell","type":"text","marks":[{"type":"code_inline"}]},{"text":" (in ","type":"text"},{"text":"exit.other[]","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"bail if price crashes fast\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fast_dump_exit","type":"text","marks":[{"type":"code_inline"}]},{"text":" (in ","type":"text"},{"text":"exit.other[]","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Sizing selection","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"User says...","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Use","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"$100 per trade\", \"fixed amount\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fixed_usd","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"2% of portfolio per trade\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fixed_pct","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"size based on volatility\", \"risk parity\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"volatility_scaled","type":"text","marks":[{"type":"code_inline"}]}]}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Critical distinction: ","type":"text"},{"text":"smart_money_buy","type":"text","marks":[{"type":"code_inline"}]},{"text":" vs ","type":"text"},{"text":"smart_money_present_min","type":"text","marks":[{"type":"code_inline"}]},{"text":" (G-05)","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"smart_money_buy","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" (E-07, entry trigger): Event-driven. Fires when a SM wallet ","type":"text"},{"text":"executes a buy transaction","type":"text","marks":[{"type":"em"}]},{"text":". Use when the user wants to ","type":"text"},{"text":"react to","type":"text","marks":[{"type":"em"}]},{"text":" SM activity.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"smart_money_present_min","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" (TF-11, filter): State check at entry-candidate time. Checks how many SM wallets ","type":"text"},{"text":"currently hold","type":"text","marks":[{"type":"em"}]},{"text":" the token. Use when the user wants to ","type":"text"},{"text":"confirm","type":"text","marks":[{"type":"em"}]},{"text":" SM presence before entering on a different trigger.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"They can be combined: ","type":"text"},{"text":"smart_money_buy","type":"text","marks":[{"type":"code_inline"}]},{"text":" as entry + ","type":"text"},{"text":"smart_money_present_min","type":"text","marks":[{"type":"code_inline"}]},{"text":" as filter (require >=2 SM holders AND react to a new SM buy).","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"4. Harness Rules","type":"text"}]},{"type":"paragraph","content":[{"text":"The harness validates every spec before execution. Violations are rejected with a plain-English error — fix and regenerate.","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rule","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Name","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Enforcement","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"H-01","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No missing stop_loss","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"exit.stop_loss.pct","type":"text","marks":[{"type":"code_inline"}]},{"text":" is required. A strategy without a stop is gambling.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"H-02","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No martingale","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rejects specs that increase size after a loss or re-enter losing positions at lower prices.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"H-03","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SL must be tighter than TP","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"If ","type":"text"},{"text":"stop_loss.pct >= take_profit.pct","type":"text","marks":[{"type":"code_inline"}]},{"text":", negative asymmetry. Rejected. Does NOT apply when using ","type":"text"},{"text":"tiered_take_profit","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"trailing_stop","type":"text","marks":[{"type":"code_inline"}]},{"text":" instead of ","type":"text"},{"text":"take_profit","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"H-04","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Param bounds respected","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Every param must be within its schema-defined min/max range.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"H-05","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Daily risk cap","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fixed_pct * max_daily_trades.n","type":"text","marks":[{"type":"code_inline"}]},{"text":" must not exceed 20% equity per day.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"H-06","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"No unknown types","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Every ","type":"text"},{"text":"\"type\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" field must reference a primitive in this library. No freeform code.","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"5. Grammar Rules","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rule","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Topic","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Resolution","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"G-01","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Exit semantics","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"All exits evaluated ","type":"text"},{"text":"in parallel every tick","type":"text","marks":[{"type":"strong"}]},{"text":". First-to-fire closes position. On same-tick tie, ","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]},{"text":" wins (fail-safe). No priority ordering.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"G-02","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Dynamic universe","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When ","type":"text"},{"text":"instrument.symbol","type":"text","marks":[{"type":"code_inline"}]},{"text":" is ","type":"text"},{"text":"\"*\"","type":"text","marks":[{"type":"code_inline"}]},{"text":", the ","type":"text"},{"text":"universe","type":"text","marks":[{"type":"code_inline"}]},{"text":" block is required with ","type":"text"},{"text":"selector","type":"text","marks":[{"type":"code_inline"}]},{"text":" (which entry primitive produces the token set) and ","type":"text"},{"text":"chain","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"G-03","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"take_profit_usd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Deferred to v1.1.","type":"text","marks":[{"type":"strong"}]},{"text":" Not available. Use percent or multiplier forms.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"G-04","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"correlation_cap","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"v1.0: only ","type":"text"},{"text":"mode: \"same_token_dedupe\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" accepted. True return-correlation deferred to v1.2.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"G-05","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SM entry vs filter","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"smart_money_buy","type":"text","marks":[{"type":"code_inline"}]},{"text":" = event-driven entry. ","type":"text"},{"text":"smart_money_present_min","type":"text","marks":[{"type":"code_inline"}]},{"text":" = state-check filter. Both kept.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"G-06","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"time_exit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Max-hold measured in bars from entry (on ","type":"text"},{"text":"instrument.timeframe","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Not absolute clock time.","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"6. live_only Primitives & Graduation Path","type":"text"}]},{"type":"paragraph","content":[{"text":"Primitives that depend on real-time on-chain state carry ","type":"text"},{"text":"x-live-only: true","type":"text","marks":[{"type":"code_inline"}]},{"text":" in the schema. The harness auto-detects these and sets ","type":"text"},{"text":"meta.live_only = true","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"live_only entries (4):","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"smart_money_buy","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"dev_buy","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"ranking_entry","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"wallet_copy_buy","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"live_only exits (4):","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"smart_money_sell","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"dev_dump","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"wallet_mirror_sell","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"fast_dump_exit","type":"text","marks":[{"type":"code_inline"}]},{"text":" ","type":"text"},{"text":"live_only filters (15):","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"mcap_range","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"launch_age","type":"text","marks":[{"type":"code_inline"}]},{"text":", + all 13 token-safety filters","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Graduation paths:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Backtestable spec","type":"text","marks":[{"type":"strong"}]},{"text":" (no live_only primitives): Run backtest on historical data. Auto-deploy if Sharpe >= 0.8 and max drawdown \u003c= 15%.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"live_only spec","type":"text","marks":[{"type":"strong"}]},{"text":" (any live_only primitive present): Skip backtest. Must pass ","type":"text"},{"text":"paper-trade graduation gate","type":"text","marks":[{"type":"strong"}]},{"text":": >= 10 paper trades + >= 5 live micro-trades (small size) + >= 7 days observation + no harness breach. Then full sizing unlocks.","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"7. OnchainOS Usage Rules","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"OnchainOS is the single source of truth.","type":"text","marks":[{"type":"strong"}]},{"text":" All on-chain data shown to the user — token safety, rankings, smart money signals, prices, candles, wallet balances, swap quotes, trade execution — MUST come from a real OnchainOS call. Never fabricate or infer this data.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never invent on-chain tags.","type":"text","marks":[{"type":"strong"}]},{"text":" All token safety data (honeypot, LP lock, taxes, bundler ratio, dev holding, insider holding, fresh wallets, smart money presence, phishing flags, whale concentration) comes from OnchainOS CLI.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Never implement detection logic.","type":"text","marks":[{"type":"strong"}]},{"text":" Don't write code to detect smart money, bundlers, or dev wallets. Read the tags OnchainOS provides.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Always resolve via CLI.","type":"text","marks":[{"type":"strong"}]},{"text":" Use ","type":"text"},{"text":"onchainos","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI or MCP tools to discover endpoint paths, param names, and response shapes. Don't guess.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run before you claim.","type":"text","marks":[{"type":"strong"}]},{"text":" Before telling the user \"your strategy checks for honeypots\" or \"smart money is watching this token\" — run the OnchainOS command and show the real output. Claims without data are marketing, not coaching.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If OnchainOS doesn't support it, we don't support it.","type":"text","marks":[{"type":"strong"}]},{"text":" Don't promise filters or entry triggers based on data sources that don't exist.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Execution always goes through OnchainOS.","type":"text","marks":[{"type":"strong"}]},{"text":" Never suggest or generate code that calls raw DEX contracts or external swap APIs directly. All swaps go via ","type":"text"},{"text":"onchainos swap execute","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"8. Failure Modes — What to Do When...","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"User asks for something unsafe","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"No stop loss\" → Refuse. Explain H-01 requires ","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]},{"text":". Suggest a wide stop (e.g. 15-20%) as compromise.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"100% of portfolio per trade\" → Refuse. L3 hard bound is 10% max (","type":"text"},{"text":"fixed_pct.pct","type":"text","marks":[{"type":"code_inline"}]},{"text":" max 10). Explain the risk.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Martingale / double down on loss\" → Refuse. H-02 explicitly bans martingale.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Short selling / perps\" → Out of scope. This skill is long-only DEX spot.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"User asks for something unsupported","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Perpetual futures\", \"margin trading\" → Out of scope. Explain: DEX spot only.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Sell when up $500\" (absolute USD TP) → Deferred to v1.1 (G-03). Use percent-based TP instead.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Correlation-based position grouping\" → v1.0 only supports ","type":"text"},{"text":"same_token_dedupe","type":"text","marks":[{"type":"code_inline"}]},{"text":" mode (G-04).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Calendar-based exit\" (sell every Friday) → Deferred. Use ","type":"text"},{"text":"time_exit","type":"text","marks":[{"type":"code_inline"}]},{"text":" with max_bars as approximation.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"User asks for something that needs live_only","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If any safety filter, wallet trigger, or ranking trigger is used, flag ","type":"text"},{"text":"meta.live_only: true","type":"text","marks":[{"type":"code_inline"}]},{"text":" and explain the paper-trade graduation path.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Meme strategies almost always need safety filters → almost always live_only.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Ambiguous intent","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When the user's request is vague (\"make me money\"), ask clarifying questions: What token? What risk tolerance? DCA or active? Budget per trade?","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When multiple entry triggers could fit, prefer the simplest one that matches intent.","type":"text"}]}]}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"9. Worked Examples","type":"text"}]},{"type":"paragraph","content":[{"text":"These 5 examples show the complete translation from user prompt → JSON spec. All param names match ","type":"text"},{"text":"schema.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" (the Primitive Library is source of truth).","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 1: SOL Dip Buyer — US Hours Only","type":"text"}]},{"type":"paragraph","content":[{"text":"User prompt:","type":"text","marks":[{"type":"strong"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"Buy SOL whenever it drops 5% in the last hour, but only during US trading hours (9:30am-4pm ET), max 3 buys per day, $200 per buy, stop out at 8%, take profit at 10%. Pause the strategy if my week is down more than 10%.\"","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Reasoning:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"5% drop in 1h on 5m bars → ","type":"text"},{"text":"price_drop","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"pct: 5","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"lookback_bars: 12","type":"text","marks":[{"type":"code_inline"}]},{"text":" (12 five-minute bars = 1 hour)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"US trading hours → ","type":"text"},{"text":"time_window","type":"text","marks":[{"type":"code_inline"}]},{"text":" filter with ","type":"text"},{"text":"start_hour: 13","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"end_hour: 20","type":"text","marks":[{"type":"code_inline"}]},{"text":" (UTC, covers 9:30-4pm ET approximately), ","type":"text"},{"text":"weekdays_only: true","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Max 3 buys/day → ","type":"text"},{"text":"max_daily_trades","type":"text","marks":[{"type":"code_inline"}]},{"text":" risk overlay","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"$200 per buy → ","type":"text"},{"text":"fixed_usd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Stop 8%, TP 10% → ","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"take_profit","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Week down 10% → ","type":"text"},{"text":"drawdown_pause","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"pause_pct: 10","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"cooldown","type":"text","marks":[{"type":"code_inline"}]},{"text":" filter (6 bars = 30 min on 5m timeframe) so rapid-fire dips don't exhaust budget","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Spec:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"meta\": {\n \"name\": \"sol_dip_us_hours\",\n \"version\": \"1.0\",\n \"risk_tier\": \"conservative\",\n \"description\": \"Buy SOL on 5% hourly dips during US trading hours\",\n \"author_intent\": \"Buy SOL whenever it drops 5% in the last hour, but only during US trading hours, max 3 buys per day, $200 per buy, stop out at 8%, take profit at 10%. Pause if my week is down more than 10%.\"\n },\n \"instrument\": {\n \"symbol\": \"SOL-USDC\",\n \"timeframe\": \"5m\"\n },\n \"entry\": {\n \"type\": \"price_drop\",\n \"pct\": 5,\n \"lookback_bars\": 12\n },\n \"exit\": {\n \"stop_loss\": { \"pct\": 8 },\n \"take_profit\": { \"pct\": 10 }\n },\n \"sizing\": {\n \"type\": \"fixed_usd\",\n \"usd\": 200\n },\n \"filters\": [\n { \"type\": \"time_window\", \"start_hour\": 13, \"end_hour\": 20, \"weekdays_only\": true },\n { \"type\": \"cooldown\", \"bars\": 6 }\n ],\n \"risk_overlays\": [\n { \"type\": \"max_daily_trades\", \"n\": 3 },\n { \"type\": \"drawdown_pause\", \"pause_pct\": 10 }\n ]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Graduation:","type":"text","marks":[{"type":"strong"}]},{"text":" Backtestable. All primitives are price/time based. Run on 12 months of SOL 5m bars.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 2: BTC Weekly DCA","type":"text"}]},{"type":"paragraph","content":[{"text":"User prompt:","type":"text","marks":[{"type":"strong"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"DCA $100 into BTC every Monday at 9am UTC. No exit — I'm holding. But add a 20% trailing stop just so a flash-crash below my avg cost doesn't wreck me.\"","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Reasoning:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Weekly DCA → ","type":"text"},{"text":"time_schedule","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"interval: \"1W\"","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"anchor_utc: \"09:00\"","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"$100 flat → ","type":"text"},{"text":"fixed_usd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"No exit\" but user asked for trailing stop → ","type":"text"},{"text":"trailing_stop","type":"text","marks":[{"type":"code_inline"}]},{"text":" at 20%. Note: trailing_stop max is 20, fits exactly.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"H-01 requires ","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]},{"text":" → add ","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]},{"text":" at 20% as backstop (same threshold as trailing, so trailing fires first in practice)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"No filters or risk overlays needed — DCA is intentionally simple.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Spec:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"meta\": {\n \"name\": \"btc_weekly_dca\",\n \"version\": \"1.0\",\n \"risk_tier\": \"passive\",\n \"description\": \"Weekly DCA into BTC with trailing stop safety net\",\n \"author_intent\": \"DCA $100 into BTC every Monday at 9am UTC. No exit, but add a 20% trailing stop for flash-crash protection.\"\n },\n \"instrument\": {\n \"symbol\": \"WBTC-USDC\",\n \"timeframe\": \"1D\"\n },\n \"entry\": {\n \"type\": \"time_schedule\",\n \"interval\": \"1W\",\n \"anchor_utc\": \"09:00\"\n },\n \"exit\": {\n \"stop_loss\": { \"pct\": 20 },\n \"trailing_stop\": { \"pct\": 20 }\n },\n \"sizing\": {\n \"type\": \"fixed_usd\",\n \"usd\": 100\n },\n \"filters\": [],\n \"risk_overlays\": []\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Graduation:","type":"text","marks":[{"type":"strong"}]},{"text":" Backtestable. Deterministic schedule + price-based exits. Run on 24 months of BTC daily bars.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 3: Meme Safety-First — Full Safety Stack","type":"text"}]},{"type":"paragraph","content":[{"text":"User prompt:","type":"text","marks":[{"type":"strong"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"I want to snipe new meme coins ranked in the top 20 trending but I don't want to get rugged. Check everything — honeypot, LP burns, taxes under 5%, no bundler pumps, dev holding under 10%, at least one smart money already in. Risk $50 per trade, tiered take-profit at 2x/5x/10x, hard stop at -50%.\"","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Reasoning:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Top 20 trending → ","type":"text"},{"text":"ranking_entry","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"list_name: \"trending\"","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"top_n: 20","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dynamic universe → ","type":"text"},{"text":"symbol: \"*\"","type":"text","marks":[{"type":"code_inline"}]},{"text":", needs ","type":"text"},{"text":"universe","type":"text","marks":[{"type":"code_inline"}]},{"text":" block","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Check everything\" → all 13 safety filters","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Taxes under 5% → separate ","type":"text"},{"text":"buy_tax_max","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"sell_tax_max","type":"text","marks":[{"type":"code_inline"}]},{"text":" at ","type":"text"},{"text":"max_pct: 5","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"At least one smart money in\" → ","type":"text"},{"text":"smart_money_present_min","type":"text","marks":[{"type":"code_inline"}]},{"text":" filter with ","type":"text"},{"text":"min_wallets: 1","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tiered TP at 2x/5x/10x → ","type":"text"},{"text":"tiered_take_profit","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"pct_gain","type":"text","marks":[{"type":"code_inline"}]},{"text":" values of 100/400/900 (2x = +100%, 5x = +400%, 10x = +900%)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Hard stop -50% → ","type":"text"},{"text":"stop_loss.pct: 20","type":"text","marks":[{"type":"code_inline"}]},{"text":" (capped at schema max of 20 — inform user)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Implied: ","type":"text"},{"text":"mcap_range","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"launch_age","type":"text","marks":[{"type":"code_inline"}]},{"text":" for \"new meme coin\"","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"All safety filters + ranking_entry → ","type":"text"},{"text":"live_only: true","type":"text","marks":[{"type":"code_inline"}]}]}]}]},{"type":"paragraph","content":[{"text":"Spec:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"meta\": {\n \"name\": \"meme_safety_first\",\n \"version\": \"1.0\",\n \"risk_tier\": \"aggressive\",\n \"live_only\": true,\n \"description\": \"Snipe trending meme tokens with full safety filter stack\",\n \"author_intent\": \"Snipe new meme coins ranked in the top 20 trending, check everything for safety, $50 per trade, tiered TP at 2x/5x/10x, hard stop at -50%.\"\n },\n \"instrument\": {\n \"symbol\": \"*\",\n \"timeframe\": \"5m\"\n },\n \"universe\": {\n \"selector\": \"ranking_entry\",\n \"chain\": \"solana\"\n },\n \"entry\": {\n \"type\": \"ranking_entry\",\n \"list_name\": \"trending\",\n \"top_n\": 20\n },\n \"exit\": {\n \"stop_loss\": { \"pct\": 20 },\n \"tiered_take_profit\": {\n \"tiers\": [\n { \"pct_gain\": 100, \"pct_sell\": 33 },\n { \"pct_gain\": 400, \"pct_sell\": 33 },\n { \"pct_gain\": 900, \"pct_sell\": 34 }\n ]\n }\n },\n \"sizing\": {\n \"type\": \"fixed_usd\",\n \"usd\": 50\n },\n \"filters\": [\n { \"type\": \"mcap_range\", \"min_usd\": 100000, \"max_usd\": 5000000 },\n { \"type\": \"launch_age\", \"min_hours\": 2, \"max_hours\": 168 },\n { \"type\": \"honeypot_check\" },\n { \"type\": \"lp_locked\", \"min_pct_locked\": 80, \"min_lock_days\": 30 },\n { \"type\": \"buy_tax_max\", \"max_pct\": 5 },\n { \"type\": \"sell_tax_max\", \"max_pct\": 5 },\n { \"type\": \"liquidity_min\", \"min_usd\": 25000 },\n { \"type\": \"top_holders_max\", \"top_n\": 10, \"max_pct\": 35 },\n { \"type\": \"bundler_ratio_max\", \"max_pct\": 10 },\n { \"type\": \"dev_holding_max\", \"max_pct\": 10 },\n { \"type\": \"insider_holding_max\", \"max_pct\": 15 },\n { \"type\": \"fresh_wallet_ratio_max\", \"max_pct\": 25 },\n { \"type\": \"smart_money_present_min\", \"min_wallets\": 1 },\n { \"type\": \"phishing_exclude\" },\n { \"type\": \"whale_concentration_max\", \"max_pct\": 20 }\n ],\n \"risk_overlays\": [\n { \"type\": \"max_concurrent_positions\", \"n\": 5 },\n { \"type\": \"max_daily_trades\", \"n\": 10 },\n { \"type\": \"session_loss_pause\", \"max_consecutive_losses\": 3 }\n ]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Note:","type":"text","marks":[{"type":"strong"}]},{"text":" User asked for -50% stop but ","type":"text"},{"text":"stop_loss.pct","type":"text","marks":[{"type":"code_inline"}]},{"text":" max is 20. Inform the user: \"Schema enforces a maximum 20% stop loss for safety. Your position will be stopped at -20% instead of -50%.\"","type":"text"}]},{"type":"paragraph","content":[{"text":"Graduation:","type":"text","marks":[{"type":"strong"}]},{"text":" live_only. Paper gate: >= 10 paper trades + >= 5 live micro-trades at $5 + >= 7 days observation, then $50 sizing unlocks.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 4: Smart Money Copy-Trade","type":"text"}]},{"type":"paragraph","content":[{"text":"User prompt:","type":"text","marks":[{"type":"strong"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"Copy-trade these 3 wallets on Base: 0xabc..., 0xdef..., 0x123.... When any of them buys a token, I buy the same token with 2% of my portfolio. Mirror their sells too. Also bail immediately if the dev dumps, or if liquidity drops under $50k. Pause the whole thing if I'm down more than 15% this week.\"","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Reasoning:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Named wallets → ","type":"text"},{"text":"wallet_copy_buy","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"target_wallet","type":"text","marks":[{"type":"code_inline"}]},{"text":" as array","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Mirror sells → ","type":"text"},{"text":"wallet_mirror_sell","type":"text","marks":[{"type":"code_inline"}]},{"text":" in ","type":"text"},{"text":"exit.other[]","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Bail if dev dumps\" → ","type":"text"},{"text":"dev_dump","type":"text","marks":[{"type":"code_inline"}]},{"text":" in ","type":"text"},{"text":"exit.other[]","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Bail if price crashes\" (liquidity proxy) → ","type":"text"},{"text":"fast_dump_exit","type":"text","marks":[{"type":"code_inline"}]},{"text":" in ","type":"text"},{"text":"exit.other[]","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pre-entry liquidity gate → ","type":"text"},{"text":"liquidity_min","type":"text","marks":[{"type":"code_inline"}]},{"text":" filter","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"2% of portfolio → ","type":"text"},{"text":"fixed_pct","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Week down 15% → ","type":"text"},{"text":"drawdown_pause","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"pause_pct: 15","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"honeypot_check","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"phishing_exclude","type":"text","marks":[{"type":"code_inline"}]},{"text":" — copy-trading without these is reckless","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Dynamic universe → ","type":"text"},{"text":"symbol: \"*\"","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"universe","type":"text","marks":[{"type":"code_inline"}]},{"text":" block","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Spec:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"meta\": {\n \"name\": \"smart_money_copy\",\n \"version\": \"1.0\",\n \"risk_tier\": \"moderate\",\n \"live_only\": true,\n \"description\": \"Copy-trade 3 wallets on Base with safety exits\",\n \"author_intent\": \"Copy-trade 3 wallets on Base, 2% portfolio per trade, mirror sells, bail on dev dump or liquidity drop, pause at 15% weekly drawdown.\"\n },\n \"instrument\": {\n \"symbol\": \"*\",\n \"timeframe\": \"5m\"\n },\n \"universe\": {\n \"selector\": \"wallet_copy_buy\",\n \"chain\": \"base\"\n },\n \"entry\": {\n \"type\": \"wallet_copy_buy\",\n \"target_wallet\": [\n \"0xabc0000000000000000000000000000000000abc\",\n \"0xdef0000000000000000000000000000000000def\",\n \"0x1230000000000000000000000000000000000123\"\n ],\n \"min_usd\": 100,\n \"mirror_mode\": \"instant\"\n },\n \"exit\": {\n \"stop_loss\": { \"pct\": 15 },\n \"other\": [\n {\n \"type\": \"wallet_mirror_sell\",\n \"target_wallet\": [\n \"0xabc0000000000000000000000000000000000abc\",\n \"0xdef0000000000000000000000000000000000def\",\n \"0x1230000000000000000000000000000000000123\"\n ],\n \"min_pct_sold\": 50\n },\n { \"type\": \"dev_dump\", \"min_usd\": 500 },\n { \"type\": \"fast_dump_exit\", \"drop_pct\": 30, \"window_sec\": 60 }\n ]\n },\n \"sizing\": {\n \"type\": \"fixed_pct\",\n \"pct\": 2\n },\n \"filters\": [\n { \"type\": \"liquidity_min\", \"min_usd\": 50000 },\n { \"type\": \"honeypot_check\" },\n { \"type\": \"phishing_exclude\" }\n ],\n \"risk_overlays\": [\n { \"type\": \"drawdown_pause\", \"pause_pct\": 15 },\n { \"type\": \"max_concurrent_positions\", \"n\": 8 },\n { \"type\": \"correlation_cap\", \"mode\": \"same_token_dedupe\", \"max_correlated\": 3 }\n ]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Graduation:","type":"text","marks":[{"type":"strong"}]},{"text":" live_only. Wallet activity can't be replayed. Paper gate: >= 10 paper + >= 5 live micro + >= 7 days.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":3},"content":[{"text":"Example 5: Launchpad Sniper","type":"text"}]},{"type":"paragraph","content":[{"text":"User prompt:","type":"text","marks":[{"type":"strong"}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"\"Snipe brand-new tokens on the OKX launchpad — only tokens launched in the last 48 hours. Require at least 2 smart-money wallets to be in already, no bundlers over 5%, LP must be locked, mcap between $50k and $2M. $75 per trade, max 3 positions at once. Take profits at 1.5x / 3x / 6x. Bail if price drops more than 40% in 5 minutes.\"","type":"text"}]}]},{"type":"paragraph","content":[{"text":"Reasoning:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Brand-new tokens on launchpad\" → ","type":"text"},{"text":"ranking_entry","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"list_name: \"new\"","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"top_n: 50","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Last 48 hours\" → ","type":"text"},{"text":"launch_age","type":"text","marks":[{"type":"code_inline"}]},{"text":" filter with ","type":"text"},{"text":"max_hours: 48","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"2 smart money in\" → ","type":"text"},{"text":"smart_money_present_min","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"min_wallets: 2","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"No bundlers over 5%\" → ","type":"text"},{"text":"bundler_ratio_max","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"max_pct: 5","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"LP locked\" → ","type":"text"},{"text":"lp_locked","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"min_pct_locked: 80","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Mcap $50k-$2M\" → ","type":"text"},{"text":"mcap_range","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"$75/trade → ","type":"text"},{"text":"fixed_usd","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Max 3 positions → ","type":"text"},{"text":"max_concurrent_positions","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"TP at 1.5x/3x/6x → ","type":"text"},{"text":"tiered_take_profit","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"pct_gain","type":"text","marks":[{"type":"code_inline"}]},{"text":" 50/200/500","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"\"Bail if drops 40% in 5 min\" → ","type":"text"},{"text":"fast_dump_exit","type":"text","marks":[{"type":"code_inline"}]},{"text":" with ","type":"text"},{"text":"drop_pct: 40","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"window_sec: 300","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"stop_loss","type":"text","marks":[{"type":"code_inline"}]},{"text":" backstop at 20% so failed snipes don't bleed forever","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Add ","type":"text"},{"text":"honeypot_check","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"phishing_exclude","type":"text","marks":[{"type":"code_inline"}]},{"text":" as baseline safety","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Spec:","type":"text","marks":[{"type":"strong"}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"meta\": {\n \"name\": \"launchpad_sniper\",\n \"version\": \"1.0\",\n \"risk_tier\": \"speculative\",\n \"live_only\": true,\n \"description\": \"Snipe new tokens under 48h with SM confirmation and safety stack\",\n \"author_intent\": \"Snipe brand-new tokens, last 48 hours, require 2 SM wallets, no bundlers over 5%, LP locked, mcap $50k-$2M, $75/trade, max 3 positions, TP at 1.5x/3x/6x, bail on 40% drop in 5 min.\"\n },\n \"instrument\": {\n \"symbol\": \"*\",\n \"timeframe\": \"1m\"\n },\n \"universe\": {\n \"selector\": \"ranking_entry\",\n \"chain\": \"solana\"\n },\n \"entry\": {\n \"type\": \"ranking_entry\",\n \"list_name\": \"new\",\n \"top_n\": 50\n },\n \"exit\": {\n \"stop_loss\": { \"pct\": 20 },\n \"tiered_take_profit\": {\n \"tiers\": [\n { \"pct_gain\": 50, \"pct_sell\": 40 },\n { \"pct_gain\": 200, \"pct_sell\": 30 },\n { \"pct_gain\": 500, \"pct_sell\": 30 }\n ]\n },\n \"other\": [\n { \"type\": \"fast_dump_exit\", \"drop_pct\": 40, \"window_sec\": 300 }\n ]\n },\n \"sizing\": {\n \"type\": \"fixed_usd\",\n \"usd\": 75\n },\n \"filters\": [\n { \"type\": \"launch_age\", \"max_hours\": 48 },\n { \"type\": \"mcap_range\", \"min_usd\": 50000, \"max_usd\": 2000000 },\n { \"type\": \"lp_locked\", \"min_pct_locked\": 80, \"min_lock_days\": 30 },\n { \"type\": \"bundler_ratio_max\", \"max_pct\": 5 },\n { \"type\": \"smart_money_present_min\", \"min_wallets\": 2 },\n { \"type\": \"honeypot_check\" },\n { \"type\": \"phishing_exclude\" }\n ],\n \"risk_overlays\": [\n { \"type\": \"max_concurrent_positions\", \"n\": 3 },\n { \"type\": \"session_loss_pause\", \"max_consecutive_losses\": 3 }\n ]\n}","type":"text"}]},{"type":"paragraph","content":[{"text":"Graduation:","type":"text","marks":[{"type":"strong"}]},{"text":" live_only. Paper gate: >= 10 paper + >= 5 live micro-trades at $10 + >= 7 days, then $75 sizing unlocks.","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}},{"type":"heading","attrs":{"level":2},"content":[{"text":"10. Example Coverage Scorecard","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Category","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Hit","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Miss (unhit in examples, but available)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Entry","type":"text","marks":[{"type":"strong"}]},{"text":" (12)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"price_drop, time_schedule, ranking_entry, wallet_copy_buy","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"price_breakout, ma_cross, rsi_threshold, volume_spike, smart_money_buy, dev_buy, macd_cross, bollinger_touch","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Exit","type":"text","marks":[{"type":"strong"}]},{"text":" (10)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"stop_loss, take_profit, trailing_stop, tiered_take_profit, wallet_mirror_sell, dev_dump, fast_dump_exit","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"time_exit, indicator_reversal, smart_money_sell","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Filter","type":"text","marks":[{"type":"strong"}]},{"text":" (23)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"time_window, cooldown, mcap_range, launch_age + all 13 safety","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"volatility_range, volume_minimum, market_regime, price_range, btc_overlay, top_zone_guard","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Sizing","type":"text","marks":[{"type":"strong"}]},{"text":" (3)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"fixed_usd, fixed_pct","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"volatility_scaled","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Risk","type":"text","marks":[{"type":"strong"}]},{"text":" (5)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"max_daily_trades, max_concurrent_positions, drawdown_pause, correlation_cap, session_loss_pause","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"(all covered)","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"The 5 examples cover 33/53 primitives. The remaining 20 are straightforward — refer to the primitive tables above for their exact param names and ranges.","type":"text"}]}]},"metadata":{"date":"2026-06-05","name":"starter-coach","author":"@skillopedia","source":{"stars":11,"repo_name":"plugin-store","origin_url":"https://github.com/okx/plugin-store/blob/HEAD/skills/starter-coach/SKILL.md","repo_owner":"okx","body_sha256":"eb00f03b61bd8b63cce4d646e66b8f35f5594aa398feeb5aa7cea5a6ba3abf18","cluster_key":"fd4ed6a9f96126e7e4d98d9fc96571bab64bc43510942e97bc0dab531f784c42","clean_bundle":{"format":"clean-skill-bundle-v1","source":"okx/plugin-store/skills/starter-coach/SKILL.md","attachments":[{"id":"5743a9f5-44e8-5287-8dd6-6a9b0145b32e","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/5743a9f5-44e8-5287-8dd6-6a9b0145b32e/attachment.json","path":".claude-plugin/plugin.json","size":655,"sha256":"8c2496b8cf3cb293a457dc3a8b20603dc95837684afc24d49bfacdb09df206c6","contentType":"application/json; charset=utf-8"},{"id":"52d21fff-2f9c-5745-970a-5e240ea9cf62","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/52d21fff-2f9c-5745-970a-5e240ea9cf62/attachment.md","path":"README.md","size":2225,"sha256":"bf7f65cf3ce334570515fb26646f34e2dafaaa72413173b57e15ac7078bf9664","contentType":"text/markdown; charset=utf-8"},{"id":"9cb8e294-61a5-5721-b3c7-6b692f41de73","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9cb8e294-61a5-5721-b3c7-6b692f41de73/attachment.md","path":"SUMMARY.md","size":1851,"sha256":"200051f1056be7307d7b4f06a8a9361ad6fa9d8ae7d948e26968a99d72124275","contentType":"text/markdown; charset=utf-8"},{"id":"9ae0e743-6ef3-5ee8-9690-79310863100a","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9ae0e743-6ef3-5ee8-9690-79310863100a/attachment.py","path":"backtest_engine.py","size":11291,"sha256":"b941a04916d38097ce290b8d9552e4dacd71e757fc08919e74ef47bf424c3b03","contentType":"text/x-python; charset=utf-8"},{"id":"77733a4e-fba0-5c75-86a2-5fd28f60aa3d","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/77733a4e-fba0-5c75-86a2-5fd28f60aa3d/attachment.py","path":"coach.py","size":102777,"sha256":"79b94cd1a60a7732783ad4c934f51b184554f5bee3b7c90bf3e0122eb792dd3a","contentType":"text/x-python; charset=utf-8"},{"id":"fe7ee780-8adb-5a55-936f-22d8fe4e53b3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fe7ee780-8adb-5a55-936f-22d8fe4e53b3/attachment.py","path":"harness.py","size":7809,"sha256":"c8fcb4fc86b84700737761ac62958d668949c20bba6616e42c61b8d64fcbb4c9","contentType":"text/x-python; charset=utf-8"},{"id":"e73f3e58-f353-5cc6-b80e-8427d5cace13","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e73f3e58-f353-5cc6-b80e-8427d5cace13/attachment.py","path":"live_engine.py","size":23694,"sha256":"3aad5da170b5225543a337171c556a762a7740818a61c145bdd0ce92567c9682","contentType":"text/x-python; charset=utf-8"},{"id":"fe33d53e-64a2-5bd1-8e04-c92edeed53b5","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/fe33d53e-64a2-5bd1-8e04-c92edeed53b5/attachment.py","path":"llm_strategy.py","size":13786,"sha256":"6a5f74add3129edc3745fd33e1a905049d39079dca06cee44017da89144fd7d2","contentType":"text/x-python; charset=utf-8"},{"id":"9b24a4f2-f3a7-5105-967b-8ee7ec1f2fac","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/9b24a4f2-f3a7-5105-967b-8ee7ec1f2fac/attachment.py","path":"onchainos.py","size":41330,"sha256":"60ea75481c82a6cfd6bf517dcc2109239d590887330aa0d6e99e20d4c46fae58","contentType":"text/x-python; charset=utf-8"},{"id":"7b370f6a-aa0e-5d3c-8b9c-7608e4387713","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/7b370f6a-aa0e-5d3c-8b9c-7608e4387713/attachment.py","path":"paper_gate.py","size":4252,"sha256":"e2e720dfcde064f9593cd1272e62a4a89e6a680094eb3049f37d70546f968fc7","contentType":"text/x-python; charset=utf-8"},{"id":"c8fa61fd-c6d5-5bc2-a52f-27cf010126f2","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c8fa61fd-c6d5-5bc2-a52f-27cf010126f2/attachment.yaml","path":"plugin.yaml","size":687,"sha256":"7583187866acb78651c5b8fb054bd8ec55b6bfce11d82baf2606e492183fb0bb","contentType":"application/yaml; charset=utf-8"},{"id":"8ab913ad-609c-52de-8ab6-8e73f7eea82b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/8ab913ad-609c-52de-8ab6-8e73f7eea82b/attachment.py","path":"primitives/__init__.py","size":49,"sha256":"ed66424c4935ba650b9ea7fffc96c1cb961acc1b865c43648ad7f65013ffccc0","contentType":"text/x-python; charset=utf-8"},{"id":"f5cd9708-5e3f-5298-9749-3e752d7634d0","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f5cd9708-5e3f-5298-9749-3e752d7634d0/attachment.py","path":"primitives/entry.py","size":8599,"sha256":"fc5e3f77aaaf3ac88d6b4f91cce62a4d2aa0652afd7ef43fc9c0fbb5dc646b18","contentType":"text/x-python; charset=utf-8"},{"id":"3c1a141f-f6c0-58ab-b626-b9c7d41122de","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3c1a141f-f6c0-58ab-b626-b9c7d41122de/attachment.py","path":"primitives/exit.py","size":8888,"sha256":"5538e460a5c7cc77359b30871cad392210d98e1f53f81a3ae49ce0f417db0a79","contentType":"text/x-python; charset=utf-8"},{"id":"a7711907-afbb-5472-a6ba-1a72d46d4e4b","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a7711907-afbb-5472-a6ba-1a72d46d4e4b/attachment.py","path":"primitives/filter.py","size":10590,"sha256":"ae9442e8aeacb7c621dba8cca9af53c762ddf3690ef28bffb91899366368fac3","contentType":"text/x-python; charset=utf-8"},{"id":"a58a6175-71b6-559f-8cdf-b3a6b0907732","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a58a6175-71b6-559f-8cdf-b3a6b0907732/attachment.py","path":"primitives/risk.py","size":3174,"sha256":"5bee928b2e87b225ec04f51539cceaf8878c50997d88c79a279c282a7b3668a0","contentType":"text/x-python; charset=utf-8"},{"id":"3ec22108-7fc8-5626-96b2-28beacc50a3f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/3ec22108-7fc8-5626-96b2-28beacc50a3f/attachment.py","path":"primitives/sizing.py","size":2314,"sha256":"60a28e026ef4a1e5293398add8fb22184962a4405b4ec67a7a714c8b6c47f003","contentType":"text/x-python; charset=utf-8"},{"id":"4a41727b-49b6-5246-8b96-3fab83675665","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/4a41727b-49b6-5246-8b96-3fab83675665/attachment.json","path":"schema.json","size":30663,"sha256":"4d4e8b891f567ae226057134aac7fbc02e000932a17abfb90638b4fa5ca90a7b","contentType":"application/json; charset=utf-8"},{"id":"f0be3d89-4254-54d3-a13a-94373dde72bf","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/f0be3d89-4254-54d3-a13a-94373dde72bf/attachment.py","path":"templates/__init__.py","size":43,"sha256":"9bb074b9593781252b30eba99092fdcd87c037177aff38ab3f1d0db6d4d85202","contentType":"text/x-python; charset=utf-8"},{"id":"579ef10c-ab8d-5442-93e7-8d82e3647b53","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/579ef10c-ab8d-5442-93e7-8d82e3647b53/attachment.py","path":"templates/grid.py","size":1950,"sha256":"f3cb180925045369e041a6c42180c651d7007dceb9d20cbe83b80a931dab3870","contentType":"text/x-python; charset=utf-8"}],"bundle_sha256":"04c492acf5047e2ac12b9fc539bf025b3397ac110313e21504b9d372a98f7765","attachment_count":20,"text_attachments":20,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/starter-coach/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"testing-qa","category_label":"Testing"},"exact_dupes_collapsed_into_this":0},"updated":"2026-04-30T00:00:00.000Z","version":"v1","category":"testing-qa","import_tag":"clean-skills-v1","description":"Starter Coach V2 — conversational 6-step skill that guides users to build their own automated DEX spot-trading bot on OKX DEX. Onboard → User Profile → Build Strategy → Paper Trade → Go Live. Uses OnchainOS CLI for all on-chain data, backtesting, and trade execution. No freeform trading code — emits validated JSON strategy specs. Triggers: starter coach, trading bot builder, strategy builder, help me build a bot, vibe trading, paper trade, backtest strategy, go live trading, build trading strategy, 交易机器人, 策略构建, 量化策略, 自动交易, 做单机器人, 帮我建策略\n"}},"renderedAt":1782980856504}

Starter Coach V2 Generate safe, backtestable DEX spot-trading strategy specs from natural language. Scope - DEX spot only — long-only, no perps, no shorts, no margin - Blue-chip (ETH/SOL/BTC) through meme-token trading - OKX DEX venue - On-chain data backbone: OnchainOS CLI (sole source for smart money, dev, bundler, fresh wallet, honeypot, lp locked, taxes, top holders tags) You emit a JSON strategy spec conforming to (Draft 2020-12). The harness validates it before any backtest or live execution. You never write freeform trading code. --- 0. Coaching Journey — 6 Steps This skill follows a s…