T09 · Insecure Skill Coding Practices
Error
- Location
- lib/risk_manager.py:43
- Finding
- Trading risk controls are incomplete, use stale state, and reset when the process restarts<![CDATA[ ## Vulnerability Details **File Location**: `lib/risk_manager.py:43-51, 93-102, 109-145`; `lib/engine.py:143-168` **Vulnerability Type**: Incomplete and non-persistent financial safety controls **Risk Level**: High ### Vulnerable Code ```python # lib/risk_manager.py:43-51 class RiskManager: def __init__(self, config: RiskConfig): self.config = config self.daily_spent = 0.0 self.daily_reset_date = _today() self.consecutive_losses = 0 self.initial_balance: Optional[float] = None self.circuit_breaker_active = False ``` ```python # lib/risk_manager.py:93-102 # Max total exposure total_exposure = sum( float(p.get("size", 0)) * float(p.get("avgPrice", 0)) for p in state.positions ) if state.balance > 0 and total_exposure / state.balance > self.config.max_total_exposure: return False, f"Total exposure {total_exposure / state.balance:.1%} > {self.config.max_total_exposure:.1%}" if len(state.positions) >= self.config.max_open_positions: return False, f"Max open positions ({self.config.max_open_positions}) reached" ``` ```python # lib/risk_manager.py:109-145 def validate_signal(self, signal: Signal, state: TradingState) -> tuple: """ Validate a specific trade signal. Returns (allowed: bool, reason: str). """ if signal.amount > self.config.max_position_size: return False, f"Amount ${signal.amount:.2f} > max position ${self.config.max_position_size:.2f}" if self.daily_spent + signal.amount > self.config.max_daily_spend: return False, f"Would exceed daily limit: ${self.daily_spent:.2f} + ${signal.amount:.2f} > ${self.config.max_daily_spend:.2f}" if signal.amount > state.balance: return False, f"Amount ${signal.amount:.2f} > balance ${state.balance:.2f}" if signal.order_type == "LIMIT" and signal.price is None: return False, "LIMIT order requires price" if signal.price is not None and (signal.price < 0.01 or signal.price > ...[truncated 5131 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Persist risk state in a transaction-safe local database or retrieve authoritative counters from the trading API. At minimum, persist: - Daily committed spend and its date. - Circuit-breaker state. - Consecutive realized losses. - Initial or high-water-mark balance used for drawdown calculations. 2. Reconcile local state with authoritative balances, positions, open orders, fills, cancellations, and realized profit/loss before every cycle. 3. Treat accepted but unfilled orders as reserved exposure and reserved spending. 4. After each successful submission, immediately update local reserved balance, exposure, open-order count, and projected position count before validating the next signal. 5. Validate an entire signal batch against aggregate limits before submitting any order. Use locking or transactional reservations if concurrent workers are possible. 6. Connect fill and settlement events to `record_loss()` and `record_win()`, or derive consecutive outcomes directly from authoritative trade history. 7. Implement the configured `stop_loss_pct` using monitored position prices and authenticated exit orders, or remove the option and documentation until it is actually supported. 8. Require an explicit, authenticated administrative action to reset a circuit breaker. A process restart must not reset safety state. 9. Add tests covering: - Multiple signals in one cycle. - Pending orders and partial fills. - Restart during an active breaker. - Daily-spend continuity across restarts. - Position limits reached during a batch. - Drawdown and consecutive-loss activation. ]]>
