Sprint 2 — Rate limiting, leverage safety, Redis kill-switch#17
Sprint 2 — Rate limiting, leverage safety, Redis kill-switch#17cluster2600 merged 9 commits intomainfrom
Conversation
…N env var Hardcoded token 'trading-bot-token' exposed Vault credentials to anyone with repo read access. Now raises EnvironmentError if VAULT_TOKEN is not set, forcing operators to explicitly provide the secret at runtime.
…_PASSWORD env var Hardcoded password 'elvis_password' exposed database credentials in source. POSTGRES_CONFIG now reads all connection params from environment variables: DB_HOST, DB_PORT, DB_USER, DB_PASSWORD (required), DB_NAME Raises EnvironmentError at startup if DB_PASSWORD is not set.
The Flask API on 0.0.0.0:5050 had no authentication — any host on the network could read trade data. Added a before_request hook that validates the X-API-Key header against the API_KEY environment variable on every request except /health (exempted for load-balancer/Docker health checks). Returns HTTP 401 on invalid key, HTTP 503 if API_KEY is not configured.
Added three new endpoints to trading/utils/trade_history_api.py: POST /emergency_stop — Activate kill-switch (halts trading) DELETE /emergency_stop — Clear kill-switch (allow resume) GET /emergency_stop/status — Read current kill-switch state All endpoints are protected by the X-API-Key auth added in #13. The global KILL_SWITCH_ACTIVE flag must be polled by the trading engine in main.py to honour the stop signal. Activation is logged at CRITICAL level with timestamp and remote IP.
Adds hyphen-variant routes (/emergency-stop, /emergency-stop/status)
as aliases to the existing underscore endpoints, matching the sprint spec.
Adds public is_trading_halted() helper so trading loops can import it:
from trading.utils.trade_history_api import is_trading_halted
if is_trading_halted():
break # stop all order placement
All routes are protected by the X-API-Key auth from ISSUE #13.
POST returns 200 with JSON confirmation; 409 if already stopped.
DELETE clears the flag; GET /emergency-stop/status is read-only.
…ntial backoff
- Add utils/binance_rate_limiter.py:
* binance_retry decorator (tenacity): 5 attempts, exponential back-off 1-60 s
* check_rate_limit_headers(): reads X-MBX-USED-WEIGHT-1M and X-MBX-ORDER-COUNT-1S
headers; logs utilisation at DEBUG and auto-pauses (5 s default) when >80%
of the 1200 req/min weight limit is consumed; honours Retry-After header
* rate_limited_call() convenience wrapper for raw requests.Response callers
* All thresholds configurable via env vars (BINANCE_RATE_LIMIT_WARN_FRACTION,
BINANCE_WEIGHT_LIMIT_PER_MIN, BINANCE_RETRY_MULTIPLIER, etc.)
- Wrap get_balance(), get_position(), get_current_price(), set_leverage() in
binance_executor.py with @binance_retry inner functions
- Add tenacity to requirements.txt
…idation
- config/config.py:
* DEFAULT_LEVERAGE changed from hardcoded 100 to 3 (read from DEFAULT_LEVERAGE
env var, defaulting to 3 if unset)
* Add validate_leverage_config(leverage) function:
- leverage > 5x → WARNING log on every startup
- leverage > 10x → EnvironmentError unless OVERRIDE_HIGH_LEVERAGE=true
- leverage <= 0 → ValueError immediately
- trading/execution/binance_executor.py:
* __init__ default_leverage parameter changed from int=100 to int=None
(resolves from TRADING_CONFIG['DEFAULT_LEVERAGE'] when not explicitly passed)
* Calls validate_leverage_config() on startup — bot refuses to run with
leverage > 10x unless operator has set OVERRIDE_HIGH_LEVERAGE=true
- .env.example: document DEFAULT_LEVERAGE, OVERRIDE_HIGH_LEVERAGE, and
all new Issue #12 / #15 env vars
…starts)
- Replace purely in-memory kill-switch state with Redis-backed persistence:
* On ACTIVATE → redis.set('ELVIS_KILL_SWITCH', '1')
* On DEACTIVATE → redis.delete('ELVIS_KILL_SWITCH')
* On STARTUP → reads Redis; if key is present, sets KILL_SWITCH_ACTIVE=True
and logs a CRITICAL warning — trading stays halted until
a human explicitly clears it via DELETE /emergency_stop
- Add helper functions:
* _get_redis_client() — connects using REDIS_HOST/PORT/DB/PASSWORD env vars
* _redis_set_kill_switch() — writes/deletes key; returns bool (success)
* _redis_get_kill_switch() — reads key; falls back to in-memory if Redis down
- GET /emergency_stop/status now reads from Redis on every call and syncs the
in-memory flag (response includes source: 'redis')
- POST /emergency_stop and DELETE /emergency_stop responses now include
redis_persisted / redis_updated fields to confirm persistence
- Graceful fallback: if Redis is unavailable, behaviour is identical to Sprint 1
(in-memory only) and a WARNING is logged — bot does not crash
- All Redis connection params controlled by env vars (no hardcoding)
|
Claude finished @cluster2600's task —— View job Code Review Complete ✅I've completed a comprehensive review of Sprint 2's reliability and risk management features. Here's my detailed feedback: 🎯 Overall Assessment: STRONGThis PR successfully implements three critical reliability features with solid engineering practices. The code demonstrates defensive programming, proper error handling, and good documentation. ✅ StrengthsIssue #12: Rate Limiting Implementation
Issue #14: Leverage Safety Validation
Issue #15: Redis Kill-Switch Persistence
|
Sprint 2 — Rate limiting, leverage safety, Redis kill-switch
Sprint 2: Reliability & Risk Management
Issues fixed
OVERRIDE_HIGH_LEVERAGE=trueCommits
ff745bf9[SECURITY] S4 — No Binance API rate limiting (IP ban risk) #12 Rate limiting + retry2f329480[RISK] R1 — Default leverage 100x will cause instant liquidation #14 Leverage safety9ff9ddad[RISK] R4 — No kill-switch / emergency stop mechanism #15 Kill-switch Redis persistenceTesting
.env.exampleNext: Sprint 3