Automated emergency risk management for multi-exchange crypto trading.
Stop blindly closing everything when drawdown hits. Killswitch identifies which side caused the loss, ranks positions by risk, and closes surgically — escalating to more aggressive stages only if the situation worsens.
Version: 6.0 — stage-based PnL attribution engine
Status: Requires dry-run validation before live use (see QUICKSTART.md)
Standard kill-switches close all positions on drawdown. The problem: if you're running a hedged book (longs + shorts), closing everything converts a temporary drawdown into a realized loss on both sides. Worse, if only shorts are bleeding, closing longs removes your hedge.
Killswitch v6.0 solves this with a three-stage architecture:
| Stage | Mode | What it does |
|---|---|---|
| 1 | CLOSE_TOP_RISK_CONTRIBUTORS |
Closes top-N positions on the losing side only, 50% partial by default |
| 2 | CLOSE_DOMINANT_LOSS_DIRECTION |
Closes all positions on the dominant losing direction |
| 3 | CLOSE_ALL_POSITIONS |
Full stop: cancels entries, closes everything, cancels orphan orders |
Stages escalate upward only. Stage 3 requires manual reset.
git clone https://github.com/zabor-crypto/killswitch-crypto
cd killswitch-crypto
pip install -r requirements.txt
cp .env.example .env && nano .env # fill in your API keys
python3 killswitch.py --test-mock # must pass before anything elseSee QUICKSTART.md for the full dry-run → live deployment flow.
Equity Drawdown Detected
↓
Position Snapshots Fetched + Stored in SQLite
↓
PnL Delta Attribution
(delta = current_pnl − reference_pnl; a declining profitable position counts)
→ source: SHORT | LONG | MIXED | UNKNOWN
↓
Risk Score per Position
= 0.40 × |pnl_delta| + 0.25 × |current_loss| + 0.20 × liq_proximity
+ 0.10 × notional_share + 0.05 × margin_loss_pct
↓
Stage Selection — highest triggered stage wins (3 > 2 > 1)
↓
Stage Machine Check
- Escalation (new > current): always allowed
- Same stage: allowed only after cooldown expires
- De-escalation (new < current): always blocked
↓
Execute Stage Action → Write trading lock file → Record state in SQLite
Default trigger thresholds (futures):
| Stage | 15m trigger | 1h trigger | Confirmations | Cooldown |
|---|---|---|---|---|
| 1 | −4.5% | −7.0% | 3 consecutive | 30 min |
| 2 | −7.0% | −10.0% | 2 consecutive | 90 min |
| 3 | −10.0% | −14.0% | 1 | 360 min |
All thresholds, cooldowns, and exchange settings are in config.yaml. The file is heavily commented. API keys are loaded from environment variables — never hardcoded.
See config_bitget_futures_only.yaml for a single-exchange example, and config_stage_based_example.yaml for a full multi-exchange template.
When any stage fires, killswitch_trading_lock.json is written. Your bots must check it before placing orders:
import json, os, time
lock_path = "./killswitch_trading_lock.json"
if os.path.exists(lock_path):
lock = json.load(open(lock_path))
if lock.get("locked") and lock.get("expires_ts", 0) > time.time():
raise RuntimeError(f"Trading locked: {lock['reason']}")# Stage state
sqlite3 killswitch.sqlite "SELECT * FROM scope_stage_state"
# Recent actions
sqlite3 killswitch.sqlite "SELECT * FROM actions ORDER BY ts DESC LIMIT 10"
# Trading lock status
cat killswitch_trading_lock.jsonpython3 -m pytest tests/ -v94+ tests across 5 suites. The system is not ready for live use until all tests pass in your environment.
| Suite | Coverage |
|---|---|
test_logic.py |
Drawdown calculation, window parsing, config loading |
test_actions.py |
Action execution paths |
test_attribution.py |
PnL attribution (SHORT/LONG/MIXED), risk ranking, liq proximity |
test_stage_machine.py |
Stage transitions, cooldown, escalation, de-escalation block |
test_integration.py |
End-to-end: losing shorts, losing longs, mixed, escalation, stale orders |
killswitch.py # Main script
risk_attribution.py # PnL attribution + risk ranking engine
stage_machine.py # Stage state machine (SQLite-backed)
order_safety.py # CloseInstruction dataclass
trading_lock.py # File-based trading lock
position_store.py # Position snapshot persistence
logger.py # Structured logging
config.yaml # Full multi-exchange config (dry_run: true by default)
config_bitget_futures_only.yaml # Single-exchange example
config_stage_based_example.yaml # Annotated multi-exchange template
requirements.txt
tests/ # 94+ tests
tools/ # dryrun_smoke.py, e2e_demo_runner.py
This is the complete production codebase. Nothing has been removed from the risk management logic. The only differences from a private deployment are:
- No actual API keys (you supply your own via
.env) - Example spot blacklists use generic tokens; replace with your own
See KNOWN_LIMITATIONS.md.
Binance, Bybit, Bitget — futures and spot. Uses ccxt for exchange connectivity.
This software is provided for educational and research purposes. Automated trading carries substantial risk of financial loss. Test thoroughly in dry-run mode before deploying with real funds. The authors are not responsible for any trading losses.
MIT — see LICENSE.