Risk Management

The risk management layer is the final gate before a signal is stored or alerted. It enforces hard limits on daily volume, AI acceptance, reward-to-risk, and stop distance — then suggests position size for manual review.

Risk management runs after AI scoring and before a signal is saved. It does not place trades or move capital — it decides whether a setup is within your configured bounds and attaches a sizing suggestion.

4 approval checksEvaluated in order; the first failure rejects the signal.
Sizing on every signalApproved and rejected signals both include position_size when equity and stop distance are valid.
Approved signals tracked laterOnly approved signals move on to outcome tracking.

Approval Checks

Each check is evaluated in sequence. A signal must pass all four to receive approved status. Related settings live under Risk And Scheduling.

Daily Signal Cap

Limits how many signals can be approved in a single UTC calendar day. This prevents alert fatigue and caps exposure when many markets fire at once.

  • The counter uses signals already approved today (status approved, created_at date in UTC).
  • Rejected signals do not count toward the cap.
  • When the cap is reached, every remaining signal in that scan is rejected with a daily-limit reason — even if it would otherwise pass.
  • Controlled by MAX_DAILY_SIGNALS (default 100).

Example rejection reason: Daily signal limit reached: 100/100

AI Rejection

If the AI or rule engine rejects the setup, the risk mangement layer rejects the signal regardless of price levels.

  • This runs after scoring — the risk manager does not re-score the signal, it enforces the AI decision.
  • A signal can show a strong setup in the strategy reasons but still be rejected here if the scorer flagged conflicts or a low score.
  • With an OpenAI key configured, the model decides pass or reject. Without one, the built-in rule engine applies the same threshold.

Example rejection reason: AI evaluation rejected the signal

Minimum Reward-to-Risk

Every signal must target at least as much profit as it risks. Signals below 1.0 R:R are rejected.

  • R:R is set by the strategy from entry, stop loss, and take profit distances.
  • Example: entry 100, stop 98, target 104 → reward 4, risk 2 → R:R 2.0 (passes).
  • Example: entry 100, stop 95, target 102 → reward 2, risk 5 → R:R 0.4 (rejected).
  • The minimum threshold is fixed at 1.0 — it is not configurable.

Example rejection reason: Risk/reward below minimum: 0.50 < 1.00

Stop Distance Limit

Rejects signals whose stop is too far from entry relative to your configured risk budget.

  • Stop distance % = |entry − stop loss| ÷ entry.
  • Maximum allowed = MAX_RISK_PER_TRADE × 5.
  • With the default 2% risk per trade, stops wider than 10% of entry are rejected.
  • This filters setups where the strategy placed a stop so wide that sizing would imply an outsized position or unrealistic risk profile.

Example rejection reason: Stop distance too wide: 12.00% > 10.00%

Approved vs Rejected

Both results are stored in the database and can appear in scan output and alerts. Rejected signals are not hidden — they include risk_reasons explaining which check failed.

  • Approved — passed all checks; counts toward the daily cap; eligible for outcome tracking.
  • Rejected — failed one or more checks; still saved with full AI evaluation and sizing; does not count toward the daily cap.

A signal can show AI pass in an alert but still be REJECTED if a risk check fails — for example, R:R below 1.0 or the daily cap already reached. Always read the Risk section at the bottom of an alert alongside the header status.

Position Sizing

Sizing answers one question: if you risk a fixed fraction of your account and the stop is hit, how large should the position be? The result is a suggestion only — adjust it to your own account, leverage, and risk rules.

Formula

risk amount      = account equity × max risk per trade
stop distance    = |entry − stop loss|
suggested qty    = risk amount ÷ stop distance
suggested notional = suggested qty × entry

Worked Example

With ACCOUNT_EQUITY=10000 and MAX_RISK_PER_TRADE=0.02, a long at entry 64250 and stop 63810.5:

risk amount        = 10,000 × 0.02 = 200.00
stop distance      = |64250 − 63810.5| = 439.5
suggested quantity = 200 ÷ 439.5 ≈ 0.455 units
suggested notional = 0.455 × 64250 ≈ 29,237.84

If price reaches the stop, you lose roughly $200 — 2% of the configured equity. The notional can exceed equity because spot sizing is based on stop distance, not full collateral requirements.

position_size is omitted when account equity, risk percentage, entry, or stop distance are zero or invalid.

FieldDescription
account_equityAccount size from ACCOUNT_EQUITY — the notional baseline for sizing math.
risk_pctRisk fraction from MAX_RISK_PER_TRADE (e.g. 0.02 = 2% of equity at risk if the stop is hit).
risk_amountDollar amount at risk: account_equity × risk_pct. This is what you stand to lose if price reaches the stop.
stop_distanceAbsolute price gap between entry and stop loss in quote-currency terms.
stop_pctStop distance as a fraction of entry — used by the stop-distance approval check.
suggested_quantityBase-asset units sized so a move to the stop loses risk_amount: risk_amount ÷ stop_distance.
suggested_notionalTotal position value in quote currency: suggested_quantity × entry.

Signal Strength

strength on the final signal is set directly from the AI or rule-engine score (0–1). It reflects setup quality from the scorer, not a separate risk calculation. A high-strength signal can still be rejected if a hard risk limit fails.