T09 · Insecure Skill Coding Practices
Error
- Location
- trader.py:362
- Finding
- Declared Live-Trading Risk Controls Are Not Fully Enforced<![CDATA[ ## Vulnerability Details **File Location**: `trader.py:32-35`, `trader.py:240-264`, and `trader.py:362-391`; related declarations in `SKILL.md:81-84` **Vulnerability Type**: Missing enforcement of financial risk controls **Risk Level**: High The documentation describes `SIMMER_MIN_VOLUME` as a minimum market-volume filter and `SIMMER_MAX_POSITIONS` as the maximum number of concurrent open positions. The implementation loads these settings, but `MIN_VOLUME` is never applied before an order, while `MAX_POSITIONS` only counts successful orders during the current process invocation. The pattern-trading path also does not enforce the configured `MIN_DAYS` resolution constraint. ### Vulnerable Code ```python MIN_VOLUME = float(os.environ.get("SIMMER_MIN_VOLUME", "3000")) MIN_DAYS = int(os.environ.get( "SIMMER_MIN_DAYS", "0")) MAX_POSITIONS = int(os.environ.get( "SIMMER_MAX_POSITIONS", "8")) ``` The pattern signal checks spread but not market volume or days until resolution: ```python def compute_pattern_signal(pattern: str, target: IntervalMarket) -> tuple[str | None, float, str]: """ Continuation signal after Three White Soldiers or Three Black Crows. Soldiers (UP trend) + next interval < 0.55 -> buy YES (hasn't caught up). Crows (DOWN trend) + next interval > 0.45 -> buy NO (hasn't caught down). """ m = target.market p = target.p q = m.question # Spread gate if m.spread_cents is not None and m.spread_cents / 100 > MAX_SPREAD: return None, 0, f"Spread {m.spread_cents/100:.1%} > {MAX_SPREAD:.1%}" if pattern == "soldiers": lag = 0.55 - p conviction = min(1.0, lag / 0.55) if lag > 0 else 0.05 size = max(MIN_TRADE, round(conviction * MAX_POSITION, 2)) return "yes", size, ( f"3-SOLDIERS -> YES {p:.0%} lag={lag:.0%} size=${size} -- {q[:60]}" ) elif pattern == "crows": lag = p - 0.45 conviction = min(1.0, lag / ...[truncated 3886 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Enforce minimum volume immediately before order placement using a reliable market-volume field: ```python volume = float(getattr(m, "volume", 0) or 0) if volume < MIN_VOLUME: return None, 0, f"Volume ${volume:,.2f} below ${MIN_VOLUME:,.2f}" ``` 2. Apply the `MIN_DAYS` check inside a shared validation function used by every signal path. Fail closed when a live market has a missing or malformed resolution timestamp. 3. Query current open positions from the venue before processing opportunities: ```python open_positions = client.get_positions(status="open") remaining = max(0, MAX_POSITIONS - len(open_positions)) ``` Stop trading when `remaining` reaches zero. 4. Count unique portfolio positions rather than orders. If several orders for one market can exist, define whether the control limits markets, positions, or orders and enforce that definition consistently. 5. Revalidate volume, spread, resolution time, and portfolio capacity immediately before each `client.trade()` call to reduce time-of-check/time-of-use issues. 6. Add automated tests proving that low-volume markets, near-resolution markets, and portfolios already at the position limit cannot place orders in either simulated or live mode. ]]>
