From 6db86fa0e7e69ebc6040fd7582392ce572aa9930 Mon Sep 17 00:00:00 2001 From: Tacit Lab Date: Thu, 16 Apr 2026 03:03:25 +0800 Subject: [PATCH] docs: add step-by-step gate architecture to SKILL.md Adds a complete blueprint for building the lightweight precheck / external-gate system that reduces model cost by 80-95% while keeping deep analysis quality intact. Includes file layout, state schema, pseudocode for precheck and external gate, and cron config examples. --- SKILL.md | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/SKILL.md b/SKILL.md index 09eebbe..1ec0139 100644 --- a/SKILL.md +++ b/SKILL.md @@ -115,6 +115,156 @@ This pattern preserves Telegram auto-delivery from Hermes cron while reducing mo - Add a file lock around the external gate script so overlapping system-cron invocations cannot double-trigger. - Rotate `~/.coinhunter/logs/external_gate.log` with `logrotate` (daily, keep ~14 compressed copies, `copytruncate`) and schedule the rotation a few minutes after the fallback Hermes cron run so they do not overlap. +### Building your own gate (step-by-step) +If you want to replicate this low-cost trigger architecture, here is a complete blueprint. + +#### 1. File layout +Create these files under `~/.hermes/scripts/` and state under `~/.coinhunter/state/`: +``` +~/.hermes/scripts/ + coinhunter_precheck.py # lightweight threshold evaluator + coinhunter_external_gate.py # optional system-crontab wrapper + +~/.coinhunter/state/ + precheck_state.json # last snapshot + trigger flags + external_gate.lock # flock file for external gate +``` + +#### 2. State schema (`precheck_state.json`) +```json +{ + "last_positions_hash": "sha256_of_positions_json", + "last_top_candidates_hash": "sha256_of_top_5_coins", + "last_btc_regime": "bullish|neutral|bearish", + "last_deep_analysis_at": "2026-04-15T11:00:00Z", + "free_usdt": 12.50, + "account_total_usdt": 150.00, + "run_requested_at": null, + "run_acknowledged_at": "2026-04-15T11:05:00Z", + "volatility_session": "low|medium|high", + "staleness_hours": 2.0 +} +``` + +#### 3. Precheck script logic (pseudocode) +```python +import json, hashlib, os, math +from datetime import datetime, timezone + +STATE_PATH = os.path.expanduser("~/.coinhunter/state/precheck_state.json") +POSITIONS_PATH = os.path.expanduser("~/.coinhunter/positions.json") + +def load_json(path): + with open(path) as f: + return json.load(f) + +def save_json(path, obj): + tmp = path + ".tmp" + with open(tmp, "w") as f: + json.dump(obj, f, indent=2) + os.replace(tmp, path) + +def compute_hash(obj): + return hashlib.sha256(json.dumps(obj, sort_keys=True).encode()).hexdigest()[:16] + +def adaptive_thresholds(account_total_usdt, volatility_session): + # Micro accounts get wider thresholds; high volatility narrows them + base_price = 0.03 if account_total_usdt >= 100 else 0.08 + base_pnl = 0.05 if account_total_usdt >= 100 else 0.12 + vol_mult = {"low": 1.2, "medium": 1.0, "high": 0.7}.get(volatility_session, 1.0) + return base_price * vol_mult, base_pnl * vol_mult + +def should_analyze(): + state = load_json(STATE_PATH) if os.path.exists(STATE_PATH) else {} + positions = load_json(POSITIONS_PATH) + # ... fetch current tickers, BTC regime, free USDT here ... + new_pos_hash = compute_hash(positions.get("positions", [])) + new_btc_regime = "neutral" # replace with actual analysis + new_free = positions.get("balances", {}).get("USDT", {}).get("free", 0) + total = positions.get("account_total_usdt", 0) + volatility = "medium" # replace with actual session metric + + price_thr, pnl_thr = adaptive_thresholds(total, volatility) + + triggers = [] + if new_pos_hash != state.get("last_positions_hash"): + triggers.append("position_change") + if new_btc_regime != state.get("last_btc_regime"): + triggers.append("btc_regime_change") + # ... check per-position price/PnL drift vs thresholds ... + # ... check candidate leadership change (skip if free_usdt < min_actionable) ... + staleness = (datetime.now(timezone.utc) - datetime.fromisoformat(state.get("last_deep_analysis_at","2000-01-01T00:00:00+00:00"))).total_seconds() / 3600.0 + max_staleness = 4.0 if total >= 100 else 8.0 + if staleness >= max_staleness: + triggers.append("staleness") + + decision = bool(triggers) + state.update({ + "last_positions_hash": new_pos_hash, + "last_btc_regime": new_btc_regime, + "free_usdt": new_free, + "account_total_usdt": total, + "volatility_session": volatility, + "staleness_hours": staleness, + }) + if decision: + state["run_requested_at"] = datetime.now(timezone.utc).isoformat() + save_json(STATE_PATH, state) + return {"should_analyze": decision, "triggers": triggers, "state": state} + +if __name__ == "__main__": + result = should_analyze() + print(json.dumps(result)) +``` + +#### 4. Hermes cron job configuration +Attach the precheck script as the `script` field of the cron job so its JSON output is injected into the prompt: +```json +{ + "id": "coinhunter-trade", + "schedule": "*/15 * * * *", + "prompt": "You are Coin Hunter. If the injected context says should_analyze=false, respond with exactly [SILENT] and do nothing. Otherwise, read ~/.coinhunter/positions.json, run the scientific checklist, decide HOLD/SELL/REBALANCE/BUY, and execute via smart_executor.py. After finishing, run ~/.hermes/scripts/coinhunter_precheck.py --ack to clear the trigger.", + "script": "~/.hermes/scripts/coinhunter_precheck.py", + "deliver": "telegram", + "model": "kimi-for-coding" +} +``` + +Add an `--ack` handler to the precheck script (or a separate ack script) that sets `run_acknowledged_at` and clears `run_requested_at` so the gate does not re-fire until the next true trigger. + +#### 5. External gate (optional, for even lower cost) +If you want to run the precheck every 5 minutes without waking Hermes at all: + +`coinhunter_external_gate.py` pseudocode: +```python +import fcntl, os, subprocess, json, sys + +LOCK_PATH = os.path.expanduser("~/.coinhunter/state/external_gate.lock") +PRECHECK = os.path.expanduser("~/.hermes/scripts/coinhunter_precheck.py") +JOB_ID = "coinhunter-trade" + +with open(LOCK_PATH, "w") as f: + try: + fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) + except BlockingIOError: + sys.exit(0) # another instance is running + + result = json.loads(os.popen(f"python {PRECHECK}").read()) + if result.get("should_analyze"): + # Trigger Hermes cron only if not already requested + state_path = os.path.expanduser("~/.coinhunter/state/precheck_state.json") + state = json.load(open(state_path)) + if not state.get("run_requested_at"): + subprocess.run(["hermes", "cron", "run", JOB_ID], check=False) +``` + +System crontab entry: +```cron +*/5 * * * * /usr/bin/python3 /home/user/.hermes/scripts/coinhunter_external_gate.py >> /home/user/.coinhunter/logs/external_gate.log 2>&1 +``` + +With this setup, the model is only invoked when a material market change occurs—preserving intelligence while cutting routine cost by 80-95%. + ### Production hardening (mandatory) The live trading stack must include these safeguards: 1. **Idempotency** — every decision carries a `decision_id`. The executor checks `~/.coinhunter/executions.json` before submitting orders to prevent duplicate trades.