> ## Documentation Index
> Fetch the complete documentation index at: https://docs.novosky.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration

> All configuration lives in three files: .env (secrets), config.json (runtime behavior), and ml_config.json (ML pipeline). This page covers all key fields.

## File overview

| File             | Purpose                                              | Tracked in git |
| ---------------- | ---------------------------------------------------- | -------------- |
| `.env`           | Secrets and external service credentials             | No             |
| `config.json`    | Runtime trading behavior, risk profile, filters      | Yes            |
| `ml_config.json` | Feature list, model paths, labeling, training params | Yes            |

The onboarding wizard and `weekly_optimize.py` manage `config.json` automatically. You should rarely need to edit it by hand.

***

## config.json

<Tabs>
  <Tab title="Risk & position sizing">
    The most important section. Controls how much capital is risked per trade.

    ```json theme={null}
    "dynamic_position_sizing": {
      "enabled": true,
      "risk_percent": 3.0,
      "min_lot": 0.01,
      "max_lot": 100,
      "max_risk_per_trade_pct": 10.0
    }
    ```

    | Field                    | Description                                                             |
    | ------------------------ | ----------------------------------------------------------------------- |
    | `risk_percent`           | Base capital at risk per trade, as % of equity                          |
    | `min_lot`                | Floor lot size (never goes below this)                                  |
    | `max_risk_per_trade_pct` | Hard cap — actual risk never exceeds this % of equity regardless of lot |

    The equity-aware SL cap formula:

    ```
    hard_cap_pips = (max_risk_per_trade_pct × equity) / (lot × pip_value)
    pip_value = trade_tick_value / trade_tick_size  (auto-read from broker symbol info)
    ```

    At equity 500 USC: cap = 50 pts. At 2418 USC: cap = 242 pts. Scales automatically as your account grows.
  </Tab>

  <Tab title="Risk profile">
    Set once during onboarding. Controls drawdown limits and hard halt.

    ```json theme={null}
    "risk_profile": {
      "id": 3,
      "name": "Balanced",
      "max_total_dd_pct": 45.0,
      "max_weekly_dd_pct": 20.0,
      "hard_halt_pct": 45.0,
      "starting_balance_usd": 500
    }
    ```

    | # | Name          | Risk/trade | Total halt | Best for               |
    | - | ------------- | ---------- | ---------- | ---------------------- |
    | 1 | Steady Income | 0.5–1.0%   | 20%        | Capital preservation   |
    | 2 | Conservative  | 1.0–1.5%   | 30%        | Low-stress compounding |
    | 3 | **Balanced**  | 1.5–2.0%   | 45%        | Most users             |
    | 4 | Growth        | 2.0–3.0%   | 55%        | Active traders         |
    | 5 | Aggressive    | 3.0–4.0%   | 65%        | High risk tolerance    |

    When the hard halt fires, `trading.py` calls `sys.exit(99)` and stops. It does not restart automatically. Update `starting_balance_usd` before restarting.

    <Warning>
      At 1:500 leverage, even Profile 5 halts well before the broker's margin call threshold. At $500 starting balance on RoboForex, Profile 5 halts at ~$175 USD loss — far above the broker's margin call point.
    </Warning>
  </Tab>

  <Tab title="Circuit breakers">
    Safety gates that pause or stop trading.

    ```json theme={null}
    "max_consecutive_losses": 7,
    "max_weekly_drawdown_pct": 25.0,
    "max_total_drawdown_pct": 55.0,
    "loss_cooldown_minutes": 0,
    "min_atr": 15
    ```

    | Field                     | Default | Effect                                                        |
    | ------------------------- | ------- | ------------------------------------------------------------- |
    | `max_consecutive_losses`  | 7       | Pauses trading after N losses in a row                        |
    | `max_weekly_drawdown_pct` | 25.0    | Pauses trading for the week when weekly loss exceeds this     |
    | `max_total_drawdown_pct`  | 55.0    | Fires hard halt — `sys.exit(99)`                              |
    | `min_atr`                 | 15      | Skips trades when ATR is below this (low volatility filter)   |
    | `loss_cooldown_minutes`   | 0       | Minutes to wait after a loss before next trade (0 = disabled) |
  </Tab>

  <Tab title="SL/TP">
    The dynamic SL/TP model overrides fixed pips at runtime. These values are fallbacks.

    ```json theme={null}
    "sl_pips": 200,
    "tp_pips": 300,
    "atr_period": 14,
    "atr_multiplier": 1.5
    ```

    The `sltp_model` section in `config.json` controls the LightGBM regression parameters:

    ```json theme={null}
    "sltp_model": {
      "enabled": true,
      "sl_multiplier": 1.0,
      "tp_multiplier": 1.2,
      "sl_min_atr": 0.5,
      "tp_min_atr": 0.8
    }
    ```

    `tp_multiplier` must be greater than `sl_multiplier`. The Phase 16 fix corrected a bug where `tp < sl` caused inverted risk/reward.
  </Tab>

  <Tab title="Confidence threshold">
    The signal model outputs a confidence score per bar. Only trades above the threshold pass through.

    ```json theme={null}
    "confidence_threshold": 0.633
    ```

    Higher threshold = fewer trades, higher precision. The optimizer sweeps this parameter during the weekly run. Typical range: 0.55–0.70 depending on risk profile.
  </Tab>
</Tabs>

***

## ml\_config.json

<Accordion title="Feature list (62 features)">
  The feature list is the contract between training and inference. **Never reorder, add, or remove features without retraining all 4 models and updating this file.**

  Key groups:

  * **Bollinger Bands:** `bb_upper`, `bb_lower`, `bb_width`, `range_position`
  * **Trend:** `price_vs_ema200`, `trend_strength`, `macd_signal`, `ema_crossover`
  * **Momentum:** `rsi_14`, `rsi_divergence`, `momentum_5`, `momentum_10`
  * **Volatility:** `atr_14`, `volatility_20`, `high_low_ratio`
  * **Volume:** `volume_ratio`, `volume_trend`
  * **Session/news (protected):** `is_london_session`, `is_ny_session`, `is_asian_session`, `is_news_near`, `news_minutes_away`, `news_count_today`, `is_news_risk_window`
  * **Account state:** `drawdown_pct`, `equity_ratio`, `win_rate_recent`, `consecutive_losses`
  * **Market regime (Phase 17.2):** `volatility_regime`, `w1_ema_bias`, `w1_rsi_norm`

  Protected features (never drop based on SHAP):

  ```
  is_news_near, news_minutes_away, news_count_today, is_news_risk_window
  is_london_session, is_ny_session, is_asian_session
  ```
</Accordion>

<Accordion title="Model paths">
  ```json theme={null}
  "ensemble_paths": {
    "rf": "models/rf_signal.pkl",
    "xgb": "models/xgb_signal.pkl",
    "lgb": "models/lgb_signal.pkl",
    "rf_onnx": "models/rf_signal.onnx",
    "xgb_onnx": "models/xgb_signal.onnx",
    "lgb_onnx": "models/lgb_signal.onnx"
  }
  ```

  Inference priority: **calibrated pkl → ONNX → raw pkl**. After every retrain, `calibrate_models()` runs automatically and writes `*_calibrated.pkl` files.
</Accordion>

<Accordion title="Risk model features">
  The risk model uses 7 equity-state features:

  ```json theme={null}
  "risk_model": {
    "features": [
      "drawdown_pct", "equity_ratio", "win_rate_recent",
      "consecutive_losses", "volatility_20", "atr_14", "hour"
    ]
  }
  ```

  These must stay in sync across `ml/risk_trainer.py`, `ml/risk_predictor.py`, and `ml_config.json`. The model outputs a multiplier in `[0.10, 1.25]` applied to the base risk percentage.
</Accordion>

***

## Broker configuration

The active trading pair is set in `config.json`:

```json theme={null}
{
  "symbol": "BTCUSD",
  "symbols": ["BTCUSD"],
  "symbol_configs": {
    "BTCUSD": { "lot": 0.01, "sl_pips": 200, "tp_pips": 300 }
  }
}
```

All broker-specific parameters (`pip_value`, `contract_size`, `tick_size`, `digits`, etc.) are read automatically from the MT5 API at startup via `GET /symbols/{symbol}`. The system adapts to any broker or suffix — `BTCUSD`, `BTCUSDc`, `BTCUSDm`, etc.

`account_type` (`cent` / `standard`) is also auto-detected from `account.currency` at startup: `USC` → cent, anything else → standard. No manual config needed.

<Note>
  To switch brokers: update `symbol` in `config.json` and `API_URL` in `.env`. The broker UTC offset is detected automatically from the live tick timestamp. Use `GET /time` on the MT5 API to inspect the detected offset.
</Note>
