Use when you have a written implementation plan to execute in a separate session with review checkpoints
npx skills add clawd-aftermath/senpi-skills-af --skill "fox-strategy"
Install specific skill from multi-skill repository
# Description
>-
# SKILL.md
name: fox-strategy
description: >-
FOX v0.1 β Fully autonomous multi-strategy trading for Aftermath perpetuals via Senpi MCP.
Forked from Wolf v7 + v7.1 data-driven optimizations (14-trade analysis: 2W/12L).
Tighter absolute floor (0.02/lev, ~20% max ROE loss), aggressive Phase 1 timing
(30min hard timeout, 15min weak peak, 10min dead weight), green-in-10 floor tightening,
time-of-day scoring (+1 for 04-14 UTC, -2 for 18-02 UTC), rank jump minimum (β₯15 OR vel>15).
Scoring system (6+ pts), NEUTRAL regime support, tiered margin (6 entries max),
BTC 1h bias alignment, market regime refresh 4h.
8-cron architecture. Independent from Wolf.
Requires Senpi MCP, python3, mcporter CLI, OpenClaw cron system.
license: MIT
compatibility: >-
Requires python3, mcporter (configured with Senpi auth), and OpenClaw cron system.
Aftermath perpetuals only (main dex and xyz dex).
Depends on dsl-dynamic-stop-loss skill for trailing stops.
metadata:
author: jason-goldberg
version: "0.1"
platform: senpi
exchange: aftermath
FOX v0.1 β Autonomous Multi-Strategy Trading
The FOX hunts for its human. It scans, enters, exits, and rotates positions autonomously β no permission needed. When criteria are met, it acts. Speed is edge.
Forked from Wolf v7 + v7.1 data-driven optimizations. Independent strategies, state files, scripts, and cron jobs. Runs in parallel with Wolf without interference.
Quick Start
- Ensure Senpi MCP is connected (
mcporter listshould showsenpi) - Create a custom strategy wallet: use
strategy_create_custom_strategyvia mcporter - Fund the wallet via
strategy_top_upwith your budget - Run setup:
python3 scripts/fox-setup.py --wallet 0x... --strategy-id UUID --budget 6500 --chat-id 12345 - Create the 8 OpenClaw crons using templates from
references/cron-templates.md - The FOX is hunting
To add a second strategy, run fox-setup.py again with a different wallet/budget. It adds to the registry.
Reference Files
| File | Contents |
|---|---|
| references/learnings.md | Proven results, key learnings, known bugs, trading discipline |
| references/cron-setup.md | Cron config, race condition prevention, time-aware scheduling |
| references/cron-templates.md | Ready-to-use cron mandates with proper systemEvent/agentTurn format |
| references/state-schema.md | JSON state file schemas (registry, trade counter, DSL state, scanner config) |
| references/api-tools.md | Senpi API tool quick reference |
| references/senpi-skill-guide.md | Senpi Skill Development Guide (conformance reference) |
Multi-Strategy Architecture
Strategy Registry (fox-strategies.json)
Central config holding all strategies. Created/updated by fox-setup.py.
fox-strategies.json
βββ strategies
β βββ fox-abc123 (Aggressive Momentum, 3 slots, leverage: check maxLeverage, default 10x, min 7x)
β βββ fox-xyz789 (Conservative XYZ, 2 slots, 7x)
βββ global (telegram, workspace)
Per-Strategy State
Each strategy gets its own state directory:
state/
βββ fox-abc123/
β βββ dsl-HYPE.json
β βββ dsl-SOL.json
βββ fox-xyz789/
βββ dsl-HYPE.json # Same asset, different strategy, no collision
βββ dsl-GOLD.json
Signal Routing
When a signal fires, it's routed to the best-fit strategy:
1. Which strategies have empty slots?
2. Does any strategy already hold this asset? (skip within strategy, allow cross-strategy)
3. Which strategy's risk profile matches? (aggressive gets FIRST_JUMPs, conservative gets DEEP_CLIMBERs)
4. Route to best-fit β open on that wallet β create DSL state in that strategy's dir
Adding a Strategy
python3 scripts/fox-setup.py --wallet 0x... --strategy-id UUID --budget 6500 \
--chat-id 12345 --name "Aggressive Momentum" --dsl-preset aggressive
This adds to the registry without disrupting running strategies. Disable with enabled: false in the registry.
FOX v7 Changes Summary (from Wolf v7)
1. Market Regime Refresh (4h)
- New cron job runs
fox-market-regime.pyevery 4 hours - Saves output to
market-regime-last.json - Provides fresh BEARISH/BULLISH/NEUTRAL classification
2. NEUTRAL Regime Support
- BEARISH: SHORT only (unchanged)
- BULLISH: LONG only (unchanged)
- NEUTRAL: Both LONG and SHORT allowed if signal score β₯ 8 (higher bar)
- NEUTRAL definition: market_bull < 60 AND market_bear < 60, OR overall confidence < 50
3. Scoring System (replaces "min 4 reasons")
- FIRST_JUMP: +3 pts (still mandatory)
- IMMEDIATE_MOVER: +2 pts
- contribVelocity > 10: +2 pts
- CONTRIB_EXPLOSION: +2 pts
- DEEP_CLIMBER: +1 pt
- Other reasons (RANK_UP, CLIMBING, ACCEL, STREAK): +1 pt each
- BTC 1h bias alignment: +1 bonus pt
- v0.1 TIME BONUS (04:00β14:00 UTC): +1 pt
- v0.1 TIME PENALTY (18:00β02:00 UTC): -2 pts
- Minimum 6 points to enter (8+ for NEUTRAL regime)
4. Tiered Margin System
- Max entries: 6 (flat, no dynamic slots)
- Entries 1-2: $1450 margin allocation (
collateralChange), $1500 budget - Entries 3-4: $950 margin allocation (
collateralChange), $1000 budget - Entries 5-6: $450 margin allocation (
collateralChange), $500 budget - Scanner reads tier based on current entry count from
fox-trade-counter.json
5. BTC 1h Bias Alignment
- Check BTC hourly trend from regime data before entry
- Aligned signals: +1 bonus point (BTC 1h BULLISH + signal LONG, or BTC 1h BEARISH + signal SHORT)
- Conflicting signals: no penalty, no bonus (regime filter handles hard blocks)
v0.1 Optimizations (Data-Driven from 14 Live Trades: 2W/12L)
These changes are derived from analyzing Wolf v7's live trading results. Every optimization has evidence from real trades.
1. Tighter Absolute Floor (0.02/leverage β ~20% max ROE loss)
| Old (v7) | New (v0.1) | |
|---|---|---|
| Formula (LONG) | entry Γ (1 - 0.03/leverage) |
entry Γ (1 - 0.02/leverage) |
| Formula (SHORT) | entry Γ (1 + 0.03/leverage) |
entry Γ (1 + 0.02/leverage) |
| Max ROE loss | ~30% | ~20% |
Evidence: NEAR lost 23.7% ROE, FARTCOIN 18.4% β both would have been capped earlier at 20%.
Config field: phase1.retraceThreshold (default: 0.02)
2. Aggressive Phase 1 Timing
| Rule | Old (v7) | New (v0.1) |
|---|---|---|
| Hard timeout (never hits Tier 1) | 90 min | 30 min |
| Weak peak cut (peak ROE < 3%, declining) | 45 min | 15 min |
| Dead weight (never positive ROE) | 30 min | 10 min |
Evidence: 100% of winners went positive within minutes. 100% of losers that started negative stayed negative.
Config fields: phase1.hardTimeoutMin (default: 30), phase1.weakPeakCutMin (default: 15), phase1.deadWeightCutMin (default: 10)
Enforcement: These timing rules are checked by the DSL cron mandate (agent-level), NOT by the dsl-v5.py script. The agent reads createdAt from the DSL state file and applies timing logic after parsing dsl-v5.py output.
3. Green-in-10 Rule
New field in DSL state: greenIn10 (default: false)
If a position never shows positive ROE within the first 10 minutes β tighten absoluteFloor to 50% of original distance from entry. This is a "tighten, don't close" rule β the 10min dead weight cut handles the actual close if it stays negative.
For positions that briefly went green then reversed, this rule does NOT apply.
Config field: phase1.greenIn10TightenPct (default: 50 β tighten floor to 50% of original distance)
4. Time-of-Day Scoring
| Time Window (UTC) | Modifier | Rationale |
|---|---|---|
| 04:00β14:00 | +1 pt | Historically where winners occur |
| 14:00β18:00 | 0 pts | Neutral window |
| 18:00β02:00 | -2 pts | 0% win rate across 6 evening trades |
| 02:00β04:00 | 0 pts | Neutral window |
Effect: Evening entries effectively need score β₯ 8 to pass the 6-point threshold.
Config fields: scoring.timeBonusHoursUTC (default: [4,14]), scoring.timePenaltyHoursUTC (default: [18,2]), scoring.timeBonusPts (default: 1), scoring.timePenaltyPts (default: -2)
5. Rank Jump Minimum
New filter: rankJumpThisScan β₯ 15 OR contribVelocity > 15
Old rule: just needed isFirstJump=true (any 10+ rank jump from #25+). Now requires either a massive jump (β₯15 ranks) or strong velocity (>15).
Evidence: Both winners had massive jumps. Small jumps (#30β#20, #35β#21) all lost.
Config fields: filters.minRankJump (default: 15), filters.minVelocityOverride (default: 15)
6. Conviction-Scaled Phase 1 Tolerance (v7.2)
Finding: Direction was right 85% of the time (11/13 trades) but we still lost $785 on correct-direction trades and left $8,000+ on the table. The problem is timing/stops, not signal quality.
Solution: Score at entry determines how much room Phase 1 gets. High-conviction trades survive initial volatility; low-conviction trades get cut fast.
| Score | Absolute Floor | Hard Timeout | Weak Peak | Dead Weight |
|---|---|---|---|---|
| 6-7 | 0.02/lev (~20%) | 30 min | 15 min | 10 min |
| 8-9 | 0.025/lev (~25%) | 45 min | 20 min | 15 min |
| 10+ | 0.03/lev (~30%) | 60 min | 30 min | 20 min |
Implementation: Entry score is stored in DSL state (score field). The DSL cron mandate reads the score and selects the matching tolerance tier from phase1.convictionTiers config. If no score is present, defaults to the 6-7 tier (tightest).
Config field: phase1.convictionTiers (array of {minScore, retraceThreshold, hardTimeoutMin, weakPeakCutMin, deadWeightCutMin})
7. Re-Entry Rule (v7.2)
When we exit a Phase 1 trade and the asset continues in our original direction, we can re-enter with guardrails.
Re-entry checklist (ALL must pass):
1. Asset still in top 20 with same direction
2. contribVelocity > 5 (momentum continuing)
3. Price has moved FURTHER in our original direction since exit
4. Within 2 hours of original exit
5. First attempt did NOT lose > 15% ROE (too volatile = skip)
6. Score minimum: 5 pts (relaxed from 6 β direction already validated)
7. isFirstJump NOT required (it already jumped once)
Sizing: 75% of normal margin tier (reduced risk on 2nd attempt)
State tracking: DSL state gets isReentry: true and reentryOf: "<original_trade_id>". Trade counter logs isReentry: true.
Config fields: reentry.enabled (default: true), reentry.marginPct (default: 75), reentry.minScore (default: 5), reentry.maxOriginalLossROE (default: 15), reentry.windowMin (default: 120), reentry.minContribVelocity (default: 5)
Entry Philosophy β THE Most Important Section
Enter before the peak, not at the top.
Leaderboard rank confirmation LAGS price. When an asset jumps from #31β#16 in one scan, the price is moving NOW. By the time it reaches #7 with clean history, the move is over. Speed is edge.
Core principle: 2 reasons at rank #25 with a big jump = ENTER. 4+ reasons at rank #5 = SKIP (already peaked).
Entry Signals β Priority Order
1. FIRST_JUMP (Highest Priority)
What: Asset jumps 10+ ranks from #25+ in ONE scan AND was not in previous scan's top 50 (or was at rank >= #30).
Action: Enter IMMEDIATELY. This is the money signal. Route to best-fit strategy with available slots.
Checklist:
- isFirstJump: true in scanner output
- v0.1 Rank jump minimum: rankJumpThisScan β₯ 15 OR contribVelocity > 15 (small jumps like #30β#20 all lost)
- 2+ reasons is enough (don't require 4+)
- vel > 0 is sufficient (velocity hasn't had time to build on a first jump)
- Max leverage >= 7x (check max-leverage.json)
- Slot available in target strategy (or rotation justified)
- >= 10 SM traders (crypto); for XYZ equities, ignore trader count
What to ignore:
- Erratic rank history β the scanner excludes the current jump from erratic checks
- Low velocity β first jumps haven't had time to build velocity
If CONTRIB_EXPLOSION accompanies it: Double confirmation. Even stronger entry.
2. CONTRIB_EXPLOSION
What: 3x+ contribution increase in one scan from asset at rank #20+.
Action: Enter even if rank history looks "erratic." The contrib spike IS the signal regardless of prior rank bouncing.
Never downgraded for erratic history. Often accompanies FIRST_JUMP for double confirmation.
3. DEEP_CLIMBER
What: Steady climb from #30+, positive velocity (>= 0.03), 3+ reasons, clean rank history.
Action: Enter when it crosses into top 20. Route to conservative strategy if available.
4. NEW_ENTRY_DEEP
What: Appears in top 20 from nowhere (wasn't in top 50 last scan).
Action: Instant entry.
5. Opportunity Scanner (Score 175+)
Runs every 15min. v6 scanner with BTC macro context, hourly trend classification, and hard disqualifiers. Complements Emerging Movers as a secondary signal source for deeper technical analysis.
Anti-Patterns β When NOT to Enter
- NEVER enter assets already at #1-10. That's the top, not the entry. Rank = what already happened.
- NEVER wait for a signal to "clean up." By the time rank history is smooth and velocity is high, the move is priced in.
- 4+ reasons at rank #5 = SKIP. The asset already peaked.
- 2 reasons at rank #25 with a big jump = ENTER. The move is just starting.
- Leaderboard rank != future price direction. Rank reflects past trader concentration. Price moves first, rank follows.
- Negative velocity + no jump = skip. Slow bleeders going nowhere.
- Oversold shorts (RSI < 30 + extended 24h move) = skip.
Late Entry Anti-Pattern
The pattern: Scanner fires FIRST_JUMP for ASSET at #25β#14. You hesitate. Next scan it's #10. Next scan #7 with 5 reasons. NOW it looks "safe." You enter. It reverses from #5.
The fix: Enter on the FIRST signal or don't enter at all. If you missed it, wait for the next asset. There's always another FIRST_JUMP coming.
Rule: If an asset has been in the top 10 for 2+ scans already, it's too late. Move on.
Pre-Entry Validation (Aftermath)
Before placing any order, preview it via POST /api/perpetuals/account/previews/place-limit-order (or place-market-order).
Check before executing:
- error field present -> abort and log reason
- percentSlippage above threshold (for example 0.5%) -> abort or reduce size
- collateralChange breaches per-position risk cap -> reduce size
- hasPosition: true when expecting a fresh entry -> refresh state and re-evaluate
Preview is free (no gas cost, no state change). Always preview before committing real funds.
Architecture β 8 Cron Jobs
| # | Job | Interval | Session | Script | Purpose |
|---|---|---|---|---|---|
| 1 | Emerging Movers | 3min | main | scripts/fox-emerging-movers.py |
Hunt FIRST_JUMP signals with v7 scoring |
| 2 | DSL v5.3.1 | 3min | isolated | dsl-v5.py (per-strategy cron) |
Trailing stop exits, Aftermath SL sync |
| 3 | SM Flip Detector | 5min | isolated | scripts/fox-sm-flip-check.py |
Conviction collapse cuts |
| 4 | Watchdog | 5min | isolated | scripts/fox-monitor.py |
Per-strategy margin buffer, liq distances |
| 5 | Portfolio Update | 15min | isolated | (agent-driven) | Per-strategy PnL reporting |
| 6 | Opportunity Scanner | 15min | main | scripts/fox-opportunity-scan-v6.py |
4-pillar scoring, BTC macro, hourly trend |
| 7 | Market Regime | 4h | isolated | scripts/fox-market-regime.py |
Regime classification |
| 8 | Health Check | 10min | isolated | scripts/fox-health-check.py |
Orphan DSL, state validation |
All scripts read fox-strategies.json and iterate all enabled Fox strategies.
See references/cron-setup.md for detailed cron configuration, race condition prevention, and time-aware scheduling.
Model Selection Per Cron β 3-Tier Approach
| Tier | Role | Crons | Example Model IDs |
|---|---|---|---|
| Primary | Complex judgment, multi-strategy routing | Emerging Movers, Opportunity Scanner | Your configured model (runs on main session) |
| Mid | Structured tasks, script output parsing | DSL v5.3.1, Portfolio Update, Health Check, Market Regime | model configured in OpenClaw |
| Budget | Simple threshold checks, binary decisions | SM Flip, Watchdog | model configured in OpenClaw |
Do NOT create crons yet β the main agent will set these up when activating the strategy.
Cron Setup
Critical: Crons are OpenClaw crons, NOT senpi crons. FOX uses two session types:
- Main session (systemEvent): Emerging Movers + Opportunity Scanner. These share the primary session context for accumulated routing knowledge.
- Isolated session (agentTurn): All others. Each runs in its own session β no context pollution, enables cheaper model tiers.
Key rules (per Senpi Skill Guide Β§7):
- systemEvent uses "text" key; agentTurn uses "message" key β wrong key = silent failure
- Budget/Mid mandates have explicit if/then per output field β never "apply rules from SKILL.md"
- Slot guard pattern: check anySlotsAvailable BEFORE any entry
- One set of crons β scripts iterate all strategies internally
See references/cron-templates.md for exact payloads.
Autonomy Rules
The FOX operates autonomously by default. The agent does NOT ask permission to:
- Open a position when entry checklist passes
- Close a position when DSL triggers or conviction collapses
- Rotate out of weak positions into stronger signals
- Cut dead weight (SM conv 0, negative ROE, 10+ min)
The agent DOES notify the user (via Telegram) after every action β but ONLY actions.
Notification Policy (Strict)
ONLY send Telegram when:
- Position OPENED (asset, direction, leverage, margin, score, reasons)
- Position CLOSED (asset, direction, PnL, close reason, hold time)
- Risk guardian triggered (gate closed, cooldown started, force close)
- Copy trading alert (-20% drawdown, strategy inactive, auto-rotate fired)
- Critical error (3+ consecutive DSL failures, MCP auth expired)
NEVER send Telegram for:
- Scanner ran and found nothing (HEARTBEAT_OK silently)
- Scanner found signals but all were filtered out
- DSL checked positions and nothing changed
- Health check passed
- Watchdog checked margins and everything is fine
- SM flip check found no flips
- Any reasoning, thinking, or narration about what the agent considered
Rule: If you didn't open, close, or force-close a position, and nothing is broken, the user should not hear from you. Silence = everything is working.
Phase 1 Auto-Cut (v0.1 β Conviction-Scaled Timing)
Positions that never gain momentum get cut automatically. Timing is scaled by entry score (v7.2): high-conviction trades get more room, low-conviction trades get cut fast.
Default rules (score 6-7, configurable via phase1 fields in DSL state):
- 30-minute hard timeout (hardTimeoutMin: 30). If ROE never hits Tier 1 (5%) in 30 min, close.
- 15-minute weak peak cut (weakPeakCutMin: 15). If peak ROE was < 3% and declining β close after 15 min.
- ~~Dead weight cut~~ (REMOVED v1.1). Was cutting positions that would have worked. Hard timeout handles real duds.
- Green-in-10 floor tightening (greenIn10TightenPct: 50). If never green in 10 min but not yet cut β tighten absoluteFloor to 50% of original distance from entry.
Conviction scaling (v7.2):
| Score | Floor | Hard Timeout | Weak Peak | Dead Weight |
|---|---|---|---|---|
| 6-7 | 0.02/lev | 30 min | 15 min | 10 min |
| 8-9 | 0.025/lev | 45 min | 20 min | 15 min |
| 10+ | 0.03/lev | 60 min | 30 min | 20 min |
The DSL cron mandate reads score from the DSL state file and applies the matching tier. No score = defaults to 6-7 tier.
Enforcement: These timing rules are applied by the DSL cron mandate (agent reads createdAt and score from state file), NOT by dsl-v5.py itself.
Why: Direction was right 85% of the time. We lost money because stops were too tight on high-conviction trades. Score-scaling gives good trades room to breathe while still cutting bad ones fast.
Exit Rules
1. DSL v5.3.1 Mechanical Exit (Trailing Stops)
All trailing stops handled automatically by DSL v5.3.1 per-strategy crons. SL synced to Aftermath for instant execution.
2. SM Conviction Collapse
Conv drops to 0 or 4β1 with mass trader exodus β instant cut.
3. Dead Weight
Conv 0, negative ROE, 10+ min β instant cut (v0.1: tightened from 30min).
4. SM Flip
Conviction 4+ in the OPPOSITE direction with 100+ traders β cut immediately.
5. Race Condition Prevention
When ANY job closes a position β immediately:
1. Set DSL state active: false in dsl/{strategyId}/{ASSET}.json (or DSL v5.3.1 auto-reconciles via clearinghouse)
2. Alert user
3. Evaluate: empty slot in that strategy for next signal?
DSL v5.3.1 β Trailing Stop System
Uses the official DSL v5.3.1 skill at /data/workspace/skills/dsl-dynamic-stop-loss/. See that skill's SKILL.md for full details.
Phase 1 (Pre-Tier 1): Absolute floor (v0.1 tightened)
- LONG floor =
entry Γ (1 - 0.02/leverage)β caps max loss at ~20% ROE (v0.1: was 0.03/~30%) - SHORT floor =
entry Γ (1 + 0.02/leverage) - 2% retrace threshold, 3 consecutive breaches β close
- Max duration: 30 minutes (v0.1: was 90min β see Phase 1 Auto-Cut above)
- Green-in-10: If never positive ROE in 10min β floor tightens to 50% of original distance
Phase 2 (Tier 1+): 9-Tier Aggressive Trailing
| Tier | ROE Trigger | Lock % | Breaches |
|---|---|---|---|
| 1 | 5% | 2% | 2 |
| 2 | 10% | 5% | 2 |
| 3 | 20% | 14% | 2 |
| 4 | 30% | 24% | 2 |
| 5 | 40% | 34% | 1 |
| 6 | 50% | 44% | 1 |
| 7 | 65% | 56% | 1 |
| 8 | 80% | 72% | 1 |
| 9 | 100% | 90% | 1 |
Phase 2: 1.5% retrace threshold, 2 consecutive breaches required.
DSL v5.3.1 State Creation (on position entry)
Create directory dsl/{strategyId}/ if needed. Write state file with ALL required fields:
{
"active": true,
"asset": "ASSET",
"direction": "SHORT",
"leverage": 10,
"entryPrice": "<entry>",
"size": "<size>",
"wallet": "<strategy_wallet>",
"strategyId": "<strategy_uuid>",
"phase": 1,
"phase1": {
"retraceThreshold": 0.02,
"consecutiveBreachesRequired": 3,
"absoluteFloor": "<calculated>",
"hardTimeoutMin": 30,
"weakPeakCutMin": 15,
"deadWeightCutMin": 10,
"greenIn10TightenPct": 50
},
"greenIn10": false,
"score": "<entry_score>",
"isReentry": false,
"reentryOf": null,
"phase2TriggerTier": 0,
"phase2": {
"retraceThreshold": 0.015,
"consecutiveBreachesRequired": 2
},
"tiers": [
{"triggerPct": 5, "lockPct": 2},
{"triggerPct": 10, "lockPct": 5},
{"triggerPct": 20, "lockPct": 14},
{"triggerPct": 30, "lockPct": 24},
{"triggerPct": 40, "lockPct": 34},
{"triggerPct": 50, "lockPct": 44},
{"triggerPct": 65, "lockPct": 56},
{"triggerPct": 80, "lockPct": 72},
{"triggerPct": 100, "lockPct": 90}
],
"currentTierIndex": -1,
"tierFloorPrice": null,
"highWaterPrice": "<entryPrice>",
"floorPrice": "<absoluteFloor>",
"currentBreachCount": 0,
"createdAt": "<ISO 8601>"
}
absoluteFloor: LONG: entry Γ (1 - 0.02/leverage), SHORT: entry Γ (1 + 0.02/leverage). Caps max loss at ~20% ROE (v0.1: tightened from 0.03/~30%).
Filename: Main dex: {ASSET}.json. XYZ dex: xyz--SYMBOL.json (colon β double-dash).
DSL v5.3.1 Cron Management
One cron per strategy. Created when first position opens. Removed on strategy_inactive.
DSL_STATE_DIR=/data/workspace/dsl DSL_STRATEGY_ID={strategyId} PYTHONUNBUFFERED=1 python3 /data/workspace/skills/dsl-dynamic-stop-loss/scripts/dsl-v5.py
Schedule: */3 * * * * (every 3 min), isolated session, mid-tier model.
Opportunity Scanner v6
4-stage funnel with 6 hard disqualifiers:
1. Counter-trend on hourly (the "$346 lesson")
2. Extreme RSI (<20 shorts, >80 longs)
3. Counter-trend on 4h with strength >50
4. Volume dying (<0.5x on both timeframes)
5. Heavy unfavorable funding (>50% annualized)
6. BTC macro headwind >30 points
Disqualified assets appear in output with reason and wouldHaveScored for transparency.
Budget Scaling v7 β Tiered Margin System
v7 uses tiered margin based on entry count (not budget-based slots):
| Entry Count | Margin per Trade | Budget per Strategy | Leverage | Notes |
|---|---|---|---|---|
| 1-2 | $1,450 | $1,500 | Check maxLeverage (default 10x, min 7x) |
Higher margin for early entries |
| 3-4 | $950 | $1,000 | Check maxLeverage (default 10x, min 7x) |
Medium margin for mid entries |
| 5-6 | $450 | $500 | Check maxLeverage (default 10x, min 7x) |
Lower margin for final entries |
Max entries per day: 6 (flat limit, no dynamic slots)
How it works:
1. Scanner reads current entry count from fox-trade-counter.json
2. Finds matching tier from marginTiers array
3. Uses that tier's margin as isolated collateral allocation (collateralChange) for position sizing
4. Checks POST /api/perpetuals/account/max-order-size before entry to confirm sufficient unallocated collateral
Minimum leverage: 7x. If max leverage for an asset is below 7x, skip it.
Auto-Delever: If a strategy's account drops below its autoDeleverThreshold β reduce max slots by 1, close weakest in that strategy.
Rotation Rules
When slots are full in a strategy and a new FIRST_JUMP or IMMEDIATE fires:
- Rotate if: new signal is FIRST_JUMP or has 3+ reasons + positive velocity AND weakest position in that strategy is flat/negative ROE with SM conv 0-1
- Hold if: current position in Tier 2+ or trending up with SM conv 3+
- Cross-strategy: If one strategy is full but another has slots, route to the available strategy instead of rotating
Position Lifecycle
Aftermath Entry Optimization
Use Aftermath native preview + inline SL/TP so positions are protected the moment they are opened:
- Preview the order first (
/api/perpetuals/account/previews/place-limit-orderor market preview) to validate margin sufficiency and expected slippage. - Place the entry with inline
slTpin the same transaction and set the initial SL to the Phase 1 floor. - LONG baseline floor:
entry * (1 - 0.02 / leverage) - SHORT baseline floor:
entry * (1 + 0.02 / leverage) - DSL cron then takes over trailing management from that initial SL and continues updating as floors ratchet.
This removes the ~3 minute unprotected window between entry and first DSL sync.
Opening
- Signal fires β validate checklist β route to best-fit strategy
create_positionon that strategy's wallet with isolated margin args on every market:- set
leverageType: "ISOLATED" - set
collateralChangeto the selected margin tier amount ($1450 / $950 / $450) - check
POST /api/perpetuals/account/max-order-sizefirst to verify unallocated collateral supports the order - Create DSL v5.3.1 state file in
dsl/{strategyId}/{ASSET}.json - Ensure DSL v5.3.1 cron exists for this strategy (create if first position)
- Update
fox-trade-counter.json - Alert user
Closing
- Close via
close_position(or DSL v5.3.1 auto-closes on breach/SL hit) - DSL v5.3.1 script auto-deletes state file and reconciles via clearinghouse
- Alert user with strategy name for context
- On
strategy_inactiveoutput β remove the DSL v5.3.1 cron for that strategy - Evaluate: empty slot in that strategy for next signal?
- Re-entry evaluation (v7.2): If closed from Phase 1, log exit details (asset, direction, exit price, ROE, exit time) for re-entry window tracking
Re-Entry (v7.2)
- Emerging Movers detects asset still in top 20 with same direction,
contribVelocity > 5 - Validate: within 2h of original exit, original loss β€ 15% ROE, price moved further in original direction
- Score β₯ 5 pts (relaxed threshold β direction already validated)
- Size at 75% of normal margin tier
- Create DSL state with
isReentry: true,reentryOf: "<original_trade_id>" - Alert user: "RE-ENTRY: {ASSET} {DIR} β direction confirmed, 2nd attempt at 75% size"
Margin Types
- Aftermath is isolated-only for all markets (crypto and XYZ)
- Always set
leverageType: "ISOLATED"and pass explicitcollateralChangeper new position - Unallocated collateral is not protective until allocated; verify capacity with
POST /api/perpetuals/account/max-order-size
XYZ Equities
- Ignore trader count. XYZ equities often have low SM trader counts β this doesn't invalidate the signal.
- Use reason count + rank velocity as primary quality filter instead.
- Always use isolated margin (
leverageType: "ISOLATED",dex: "xyz") with explicitcollateralChange. - Check max leverage β many XYZ assets cap at 5x or 3x. If below 7x, skip.
Token Optimization & Context Management
Model tiers: See Architecture table. Primary for main-session crons, Mid/Budget for isolated.
Heartbeat policy: If script output contains no actionable signals, output HEARTBEAT_OK immediately. Do not reason about what wasn't found.
Context isolation (multi-signal runs): Read fox-strategies.json ONCE per cron run. Build complete action plan before executing. Send ONE consolidated Telegram per run.
Skip rules: Skip redundant checks when data < 3 min old. If all slots full and no FIRST_JUMPs β skip scanner processing.
API Dependencies
All MCP calls go through fox_config.mcporter_call() β no direct subprocess invocations. See references/api-tools.md for the full tool reference.
| Action | Tool | Used By |
|---|---|---|
| Create strategy wallet | strategy_create_custom_strategy |
Setup |
| Fund wallet | strategy_top_up |
Setup |
| Open position | create_position |
Emerging Movers, Opp Scanner |
| Close position | close_position |
DSL v5.3.1, SM Flip, Watchdog |
| Sync stop loss to HL | edit_position |
DSL v5.3.1 |
| Check positions/PnL | strategy_get_clearinghouse_state |
Watchdog, Portfolio, Health Check |
| Check strategy status | strategy_get |
DSL v5.3.1 |
| Check open orders | strategy_get_open_orders |
DSL v5.3.1 |
| Smart money data | leaderboard_get_markets |
Emerging Movers, SM Flip, Opp Scanner |
| Top traders | leaderboard_get_top |
Opp Scanner |
| Asset candles | market_get_asset_data |
Opp Scanner, Market Regime |
| Market prices | market_get_prices |
DSL v5.3.1 |
| All instruments | market_list_instruments |
Opp Scanner, Setup |
Never use: dryRun: true (actually executes), strategy_close_strategy (closes entire strategy irreversibly).
Scripts Reference
| Script | Purpose |
|---|---|
scripts/fox-setup.py |
Setup wizard β adds strategy to registry from budget |
scripts/fox_config.py |
Shared config loader β all Fox scripts import this |
scripts/fox-emerging-movers.py |
Emerging Movers v4 scanner (FIRST_JUMP, IMMEDIATE, CONTRIB_EXPLOSION) |
scripts/fox-sm-flip-check.py |
SM conviction flip detector (multi-strategy) |
scripts/fox-monitor.py |
Watchdog β per-strategy margin buffer + position health |
scripts/fox-opportunity-scan-v6.py |
Opportunity Scanner v6 (BTC macro, hourly trend, disqualifiers) |
scripts/fox-health-check.py |
Per-strategy orphan DSL / state validation |
scripts/fox-market-regime.py |
Market regime detector |
State Files Reference
| File | Purpose |
|---|---|
fox-strategies.json |
Strategy registry (wallets, budgets, DSL config) |
fox-trade-counter.json |
Daily trade counter with tiered margin |
fox-emerging-movers-history.json |
Emerging movers scan history |
market-regime-last.json |
Latest market regime (shared, read-only) |
max-leverage.json |
Per-asset max leverage (shared) |
dsl/{strategyId}/{ASSET}.json |
DSL v5.3.1 per-position state |
history/scan-history.json |
Cross-scan momentum tracking |
history/fox-scanner-config.json |
Scanner threshold overrides |
See references/state-schema.md for complete schemas.
Known Limitations
- Watchdog blind spot for isolated positions: Can't see isolated liquidation distances for every market consistently. Positions rely on DSL.
- Health check coverage can differ by market view: total equity vs per-position collateral may differ from raw wallet balances.
- Scanner needs history for momentum: Cross-scan momentum requires at least 2 scans. First scan produces scored results without momentum data.
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.