Senpi-ai

bison-strategy

BISON v3.0.1 — Conviction Holder. Patient multi-asset (BTC/ETH/SOL) conviction trader. MIN_SCORE 11 across 9 components (4H/1H structure, momentum, SM alignment, funding, volume, OI proxy, RSI). Conviction- scaled margin tiers (25%/31%/37%) and 7-10× leverage. DSL is wide by design — time-cuts DISABLED — so a real directional thesis can run multiple days into the move. Few trades. Long holds. Big moves. v3.0.1 ships a race-window dedup cache that eliminates the ENGINE_FAILURE retry noise on already-held assets, plus a doubled DSL exit interval to throttle REDUCE_ONLY spam when runtime position-state lags HL.

Senpi-ai 99 31 Updated 1mo ago

Resources

5
GitHub

Install

npx skillscat add senpi-ai/senpi-skills/bison-strategy

Install via the SkillsCat registry.

SKILL.md

🦬 BISON v3.0.1 — Conviction Holder

Asset whitelist (BTC/ETH/SOL). Conviction floor (minScore 11).
No time-cuts — Phase 1 + Phase 2 ratchet ladder own all exits.

v3.0.1 (2026-05-18) — held-asset dedup race-fix + DSL throttle

Operational reliability patch. NO thesis change. NO scoring change.

Bug observed. Audit on Bison12 (M192943) 2026-05-13 to 2026-05-17
showed 16 create_position ENGINE_FAILUREs carrying
BISON_CONVICTION reasoning text, all for assets the wallet already
held. Each failure was a runtime LLM-gate fire on a producer-emitted
signal that should have been deduped — the producer's on-chain
heldAssets check leaked during the race window between
push_signal() returning OK and the resulting position appearing
in the next-tick clearinghouseState pull.

Fix 1 — recent-signals cache (scripts/bison_config.py).
record_signal(coin) writes {coin: epoch_seconds} to
state/recent-signals.json after every successful push. main()
calls was_recently_signaled(coin) BEFORE build_thesis() and
skips any coin seen within RECENT_SIGNAL_TTL_SEC (default 180s
≈ 3× the typical ALO open-fill window). Skipped coins are echoed
in recently_signaled_skipped in the per-tick output for audit.

Fix 2 — DSL exit interval 30s → 60s (runtime.yaml).
Audit also showed clusters of CLOSE_FEE_OPTIMIZED_FAILED "Reduce only order would increase position" + CLOSE_NO_POSITION errors
firing 1-2 ticks after a successful close. The runtime's view of
the closed position takes longer to refresh than the 30s DSL
interval, so DSL re-fires close on a flat HL position. Doubling
the interval gives the runtime position-state pull room to catch
up and halves the wasted close attempts. Root cause (runtime-
side position-state sync) is escalated to the runtime team

this YAML change is downstream mitigation only.

What this does NOT fix. Phantom orphan positions where DSL
state and HL state are durably out of sync (i.e. runtime thinks a
position is open after a complete external close, or vice versa).
That class of bug needs runtime-side reconciliation; the producer
has no authority over DSL state.

v3.0.0 (2026-05-12) — plumbing-only migration

NO thesis change. v2.1 asset whitelist, minScore 11, conviction-scaled margin tiers, direction waterfall, 9-component scoring, and DSL preset all preserved verbatim. Six-layer plumbing flip:

  1. MCP transport: mcporter subprocess (2.5-5s cold-start per call) → senpi_runtime_helpers.SenpiClient.mcp_call() in-process HTTPS (~280ms).
  2. Signal emit: scanner called create_position directly (Wolverine pattern); producer now emits via push_signal() to runtime /signals. Runtime LLM-gated bison_entry action opens via FEE_OPTIMIZED_LIMIT, passing marginUsd + leverage through from signal data for tier-based sizing.
  3. Reentrancy: no v2 lockfile to replace (Bison v2.x ran on openclaw cron with no producer-side lock); producer_daemon owns the per-tick scanner_lock with stale-PID auto-recovery.
  4. Scheduler: openclaw cron (5 min) → producer_daemon(interval_seconds=300). Long-lived process; zero per-tick cold-start cost.
  5. Risk gates: Python MAX_DAILY_LOSS_PCT, dynamic-slots PnL-based cap, per-asset cooldown state files → declarative risk.guard_rails block. state/trade-counter.json and state/asset-cooldowns.json are vestigial in v3.0 and can be deleted.
  6. Exit fee: DSL exits switched from MARKET (taker, 0.045%) to FEE_OPTIMIZED_LIMIT (maker-first, 0.015%, 60s ALO timeout, taker fallback). Entry exits keep ensure_execution_as_taker: false (v2.1 ALO patience rule).

CRITICAL RULES

RULE 1: Producer enters. DSL exits. Producer NEVER exits positions.

v1.x had thesis re-evaluation that chopped winners. v2.0 removed it. v3.0 makes it structurally impossible — the producer has no close_position call site.

RULE 2: Producer emits signals via push_signal(); runtime opens

The producer scores conviction theses and emits via client.push_signal(). The runtime's LLM-gated bison_entry action opens the position via FEE_OPTIMIZED_LIMIT with ensure_execution_as_taker: false (v2.1 patience rule preserved — entry ALO waits 30s for maker fill).

RULE 3: All signals are score contributors, not hard gates

4H trend, 1H trend, 1H momentum, SM direction, funding — all add/subtract points. The minScore threshold (v2.1+: 11) is the ONLY gate. Nothing kills a signal before scoring.

RULE 4: Asset whitelist enforced (v2.1+)

Only BTC/ETH/SOL by default (configurable via allowedAssets in bison-config.json). Pre-v2.1 fetched top 10 by 24h notional volume — let small-caps that volume-spiked into the universe (e.g. ZEC on May 4) consume the single daily slot.

RULE 5: ensureExecutionAsTaker = false on ENTRIES

Entries use FEE_OPTIMIZED_LIMIT with ensure_execution_as_taker: false (v2.1 patience rule). Exits use ensure_execution_as_taker: true (closes MUST happen — taker fallback is the safety floor).

RULE 6: Use the DSL preset in runtime.yaml — not a custom DSL state file

v2.x shipped a hardcoded DSL state template in scanner output. v3.0 uses the runtime YAML's dsl_preset block as the single source of truth. No scanner-side DSL state.

How BISON v3.0 trades

Entry (all score contributors — preserved from v2.1)

Signal Points Type
4H trend aligned +3 Score
4H trend opposing -1 Score
1H trend agrees +2 Score
1H trend opposing -1 Score
1H strong momentum (≥1%) +2 Score
1H moderate momentum (≥0.5%) +1 Score
1H counter momentum -1 Score
SM aligned +2 Score
SM opposing -2 Score
Funding aligned +2 Score
Funding crowded -1 Score
Volume rising +1 Score
OI growing +1 Score
RSI room +1 Score
RSI extreme -1 Score
4H momentum (>1.5%) +1 Score

Min score: 11. Max possible: ~16.

Direction determination (waterfall)

  1. 4H trend structure → LONG if BULLISH, SHORT if BEARISH
  2. If 4H NEUTRAL → follow SM direction
  3. If SM NEUTRAL → follow 1H momentum (>0.5%)
  4. If all neutral → no signal

Conviction-scaled margin

Score Margin
8-9 25% of account
10-11 31%
12+ 37%

Producer computes marginUsd from tier; signal data carries it; LLM gate passes through to runtime payload. Same pattern for leverage (default 10x, MIN 7x, MAX 10x).

Exit — RatchetStop only

DSL preset wide-by-design for conviction breathing:

  • +10% ROE: no lock (confirms working)
  • +20% / +25% lock
  • +30% / +40% lock
  • +50% / +60% lock
  • +75% / +75% lock
  • +100% ROE: lock 85% — infinite trail

Phase 1 max_loss: 30%. Time-cuts: hard_timeout + dead_weight_cut DISABLED. weak_peak_cut KEPT (self-limiting at 60min/3.0% peak).

Risk gates (runtime.yaml risk.guard_rails)

Gate Setting Replaces
max_entries_per_day 3 v2.1 base daily cap
per_asset_cooldown_minutes 120 v2.1 cooldownMinutes
daily_loss_limit_pct 10 v2.1 MAX_DAILY_LOSS_PCT
max_consecutive_losses 3 v2.1 (implicit)
cooldown_minutes (post-loss) 30 new (fleet-standard)
drawdown_halt_pct 25 new (fleet-standard, Roach lesson)
drawdown_reset_on_day_rollover false fleet-standard

v2.1's PnL-aware dynamic daily cap (3 base / 6 hard cap, tiered by realized PnL) is dropped in favor of static max_entries_per_day=3 + drawdown_halt_pct=25. The conservative ceiling and -25% hard stop are preserved; the "ride the hot hand" tier is sacrificed for state-file-free reliability.

Hardcoded constants (not configurable)

  • MAX_LEVERAGE: 10
  • MIN_LEVERAGE: 7
  • XYZ_BANNED: true (banned at producer scan level)

Operator deployment topology

Bison's runtime + producer pair runs per strategy wallet, not per
operator
. Operators wanting more capital exposure deploy multiple
strategy wallets, each running its own runtime instance and its own
bison-producer daemon. Each instance:

  • Reads its own ${WALLET_ADDRESS} (typically via env var) — the
    runtime YAML's ${WALLET_ADDRESS} placeholder is resolved at
    runtime create time, and the producer falls back to
    bison-config.json's wallet field.
  • Computes held_assets from its own wallet's on-chain
    clearinghouseState — instances do NOT see each other's
    positions.
  • Writes its own state/recent-signals.json (per-instance cache).
  • Reuses the same SENPI_AUTH_TOKEN (operator-scoped) — a single
    operator can run N strategy wallets under one auth token.

Implication: two-wallet deployment doubles capital exposure and
parallelizes asset selection.
Wallet A may take SOL while Wallet
B simultaneously takes BTC, capturing uncorrelated entries. Each
wallet's DSL ratchet is independent.

This topology is the production reality on Bison's live deployment
(see Predator MCP bison-v1-0 slug, strategyWalletCount: 2).
It is NOT a Bison-specific feature — it's how the senpi-trading-
runtime plugin natively scales: one runtime per wallet, multiple
wallets per operator.

Signal cadence (what to expect)

  • Producer tick: every 300s (5 min). Long-lived daemon — no
    per-tick cold-start cost.
  • Universe scanned per tick: ≤ 10 assets, filtered to the
    whitelist (default BTC/ETH/SOL = 3 assets).
  • MCP cost per tick: ~4-7 calls (market_list_instruments,
    leaderboard_get_markets, market_get_asset_data per
    candidate, strategy_get_clearinghouse_state for held check).
  • Emission rate: 0-5 signals per day in typical regimes. The
    MIN_SCORE 11 floor + 2h per_asset_cooldown keeps emission
    selective.
  • Quiet day signature: every tick output ends with "note": "WAITING — no conviction thesis (min score 11)". This is the
    most common state — the producer is alive, the thesis is just
    not firing.
  • Suspected silent producer: check audit_query({tool_name: "market_get_asset_data", user_ids: [<MID>]}) — if entries fall
    to zero for >15 min during market hours, the daemon has died
    and needs disown re-launch (fleet pattern from 2026-05-13).

Reading the decision log

Bison emits per-trade decision telemetry via the runtime LLM gate.
To inspect the reasoning chain for a specific window:

mcp__senpi-prod__audit_query({
  user_ids: ["<your Senpi MID>"],     # e.g. "M192943" for Bison12
  tool_name: "create_position",
  action_type: "create",
  limit: 50
})

Each entry's ai_reasoning field is the LLM gate's reasoning
output — typically "BISON_CONVICTION <ASSET> <DIRECTION> - <top_score_reason>". Successful entries have success: true;
failures carry an error_code and error_message.

Pair with close_position audit queries to see the DSL exit
reasons (weak_peak_cut, dsl_breach, etc).

The audit-query path is the canonical post-hoc validation tool
for Bison — the Predator MCP shows aggregates (PnL, ROI, volume)
but does not expose per-trade reasoning. For "why did this trade
fire" questions, always go to audit_query.

v3.0.0 (2026-05-12) — plumbing-only migration

Hard rule for user-conversation Claude sessions

User-conversation Claude sessions MUST NOT call:
create_position, close_position, edit_position,
ratchet_stop_add, ratchet_stop_edit, ratchet_stop_delete,
cancel_order, strategy_close, strategy_close_positions.

These tools are reserved for the producer daemon (entry path) and the DSL ratchet engine (exit path). User-conversation sessions are read-only.

Operator install

See README.md for fresh-install + migration commands from v2.1.

Changelog

v3.0.0 (2026-05-12) — plumbing-only migration

Migrated to helpers-native (senpi_runtime_helpers). Thesis preserved verbatim. See six-layer flip above.

v2.1 (2026-05-07)

  • Asset whitelist (BTC/ETH/SOL); pre-v2.1 fetched top-10-by-volume which let small-caps like ZEC consume the daily slot
  • minScore 8 → 11; was firing on first mediocre setup post-midnight, now demands real conviction
  • DSL time-cuts disabled in runtime.yaml per single-asset pattern

v2.0 (2026-04-06)

  • ALL hard gates converted to score contributors
  • Thesis exit (evaluate_held_position) REMOVED
  • Scanner calls create_position internally (Wolverine pattern; superseded in v3.0 by helpers-native push_signal)
  • Direction waterfall: 4H → SM → 1H momentum

License

Apache-2.0 — attribution required for derivative works. Copyright 2026 Senpi (https://senpi.ai).

Skill Attribution

When creating a strategy, include skill_name and skill_version in the call. See references/skill-attribution.md for details.