# How the CloddsBot Unified Risk Engine Detects Volatility Regimes and Triggers Circuit Breakers

> Discover how the CloddsBot unified risk engine detects volatility regimes and triggers circuit breakers by monitoring P&L and market conditions to protect your trading.

- Repository: [AL/CloddsBot](https://github.com/alsk1992/CloddsBot)
- Tags: deep-dive
- Published: 2026-09-14

---

**The unified risk engine monitors rolling P&L standard deviations to classify markets into low, normal, high, or extreme volatility regimes, then halts trading or applies size multipliers based on these classifications, while a separate circuit breaker subsystem monitors configurable trip conditions including volatility thresholds to prevent further trades when markets become unstable.**

The CloddsBot trading bot implements a unified risk engine that combines real-time volatility detection with a configurable circuit breaker mechanism to protect capital during turbulent market conditions. This system analyzes trade P&L percentages through a rolling window approach to identify regime changes, automatically reducing position sizes or stopping trading entirely when volatility exceeds safe thresholds. According to the source code in `alsk1992/CloddsBot`, the engine coordinates between the **Volatility Detector** ([`src/risk/volatility.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/risk/volatility.ts)) and the **Circuit Breaker** ([`src/risk/circuit-breaker.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/risk/circuit-breaker.ts)) to enforce risk limits before every trade execution.

## Detecting Volatility Regimes via Rolling Window Analysis

The **Volatility Detector** subsystem maintains a rolling window of trade P&L percentages to calculate dynamic standard deviations and classify market conditions into distinct regimes.

### Feeding P&L Observations to the Detector

Every completed trade propagates its profit-and-loss percentage to the volatility subsystem through the engine’s `recordPnL` method. In [`src/risk/engine.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/risk/engine.ts), this function forwards the observation to the detector’s internal buffer:

```typescript
// src/risk/engine.ts → recordPnL
function recordPnL(record: PnLRecord): void {
  varCalc.addObservation(record);
  volDetector.addObservation(record.pnlPct);   // ← feed to volatility detector
}

```

The `addObservation` method in [`src/risk/volatility.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/risk/volatility.ts) maintains the rolling window by pushing new values and evicting old ones once the buffer reaches the configured `lookbackWindow` size (defaulting to 30 observations).

### Computing Baseline Volatility

Before the detector can classify regimes, it must establish a **baseline standard deviation**. The `createVolatilityDetector` function initializes an empty `window` array and computes `baselineStdDev` only after the window fills completely:

```typescript
// src/risk/volatility.ts → addObservation
window.push(pnlPct);
while (window.length > cfg.lookbackWindow) window.shift();
if (baselineStdDev === null && window.length >= cfg.lookbackWindow) {
  baselineStdDev = computeStdDev(window);
}

```

This baseline serves as the reference point for determining whether current market volatility represents a departure from historical norms.

### Classifying Market Regimes

On each call to `detect()`, the engine calculates fresh mean, standard deviation (σ), and ATR metrics. The `classifyRegime` function compares current σ against the baseline (or absolute thresholds when no baseline exists) to categorize conditions into four states:

```typescript
// src/risk/volatility.ts → classifyRegime
const ratio = threshold > 1e-12 ? stdDev / threshold : (stdDev > 0 ? cfg.extremeThreshold + 1 : 0);
if (ratio <= cfg.lowThreshold) return 'low';
if (ratio <= cfg.highThreshold) return 'normal';
if (ratio <= cfg.extremeThreshold) return 'high';
return 'extreme';

```

The regimes—**low**, **normal**, **high**, and **extreme**—trigger different risk responses based on configurable threshold ratios.

### Applying Size Multipliers and Trading Halts

Each regime maps to a configurable **size multiplier** that adjusts position sizing (for example, 1.2× for low volatility, 0.5× for high, and 0.25× for extreme). Additionally, the detector returns a `shouldHalt` boolean when the regime is classified as extreme and the `haltOnExtreme` configuration is enabled:

```typescript
// src/risk/volatility.ts → detect()
return {
  regime,
  sizeMultiplier: cfg.regimeMultipliers[regime],
  shouldHalt: regime === 'extreme' && cfg.haltOnExtreme,
  …
};

```

During trade validation, the engine retrieves these values via `volDetector.detect()` and either rejects the trade with an "Extreme volatility" message or applies the multiplier to reduce position size.

## Triggering Circuit Breakers on Volatility Conditions

While the volatility detector manages regime-based sizing, the **Circuit Breaker** ([`src/risk/circuit-breaker.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/risk/circuit-breaker.ts)) provides hard stops when specific risk thresholds are breached.

### Pre-Trade Gate Checks

Early in the `validateTrade` execution sequence, the engine queries the circuit breaker’s state before proceeding with other validations:

```typescript
// src/risk/engine.ts → validateTrade
if (deps.circuitBreaker) {
  const canTrade = deps.circuitBreaker.canTrade();
  const cbState = deps.circuitBreaker.getState();
  checks.push({
    name: 'circuit_breaker',
    passed: canTrade,
    message: canTrade
      ? 'Circuit breaker OK'
      : `Circuit breaker tripped: ${cbState.tripReason}`,
  });
  if (!canTrade) { … return rejected … }
}

```

If `canTrade()` returns false, the trade aborts immediately with the specific trip reason logged for audit purposes.

### Evaluating Volatility Trip Conditions

The circuit breaker evaluates multiple condition types, including volatility-based trips. The `checkVolatilityCondition` function fetches the current market volatility percentage from the feature engineering service ([`src/services/feature-engineering/index.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/services/feature-engineering/index.ts)) and compares it against the configured `maxVolatilityPct`:

```typescript
// src/risk/circuit-breaker.ts → checkVolatilityCondition
const volatilityPct = getVolatilityPct(features);
if (volatilityPct !== null && volatilityPct > condition.maxVolatilityPct) {
  return { tripped: true, details: { volatilityPct, … } };
}

```

This allows the breaker to trip based on external market data independently of the internal P&L-based regime detector.

### Trip Execution and Event Emission

When any condition returns `tripped: true`, the breaker invokes `tripBreaker(event)`, which atomically sets the state, timestamps the incident, logs the warning, and emits a notification:

```typescript
// src/risk/circuit-breaker.ts → tripBreaker
state.tripped = true;
state.trippedAt = event.timestamp;
state.tripEvent = event;
logger.warn({ condition: event.condition.type, details: event.details }, 'Circuit breaker tripped');
emitter.emit('tripped', event);

```

This creates an immutable record of why trading halted, accessible via the dashboard and programmatic interfaces.

### Automatic Reset and Cooldown

For transient volatility events, the circuit breaker supports automatic recovery. When `autoReset` is enabled and `cooldownMs` is greater than zero, `tripBreaker` schedules a timer to call `resetBreaker(false)` after the cooldown period expires:

```typescript
// src/risk/circuit-breaker.ts → tripBreaker (auto‑reset)
if (cfg.autoReset && cfg.cooldownMs > 0) {
  state.resetAt = new Date(Date.now() + cfg.cooldownMs);
  setTimeout(() => { if (state.tripped && cfg.autoReset) resetBreaker(false); }, cfg.cooldownMs);
}

```

This mechanism prevents indefinite trading halts while ensuring sufficient cooling-off time after volatile market moves.

## Engine Integration and Trade Validation Flow

The unified risk engine orchestrates seven sequential safety checks during `validateTrade`. The volatility regime and circuit breaker evaluations occupy specific positions in this pipeline:

1. **Kill-switch** validation (safety manager)
2. **Circuit-breaker check** (`deps.circuitBreaker.canTrade()`)
3. Order-size and exposure limits
4. Daily loss, drawdown, and concentration validations
5. **Value-at-Risk (VaR)** limit verification
6. **Volatility regime check** (`volDetector.detect()`) with potential abort or size adjustment
7. **Kelly sizing** final adjustment

If the volatility detector’s `shouldHalt` flag is true or the circuit breaker has tripped, the engine returns a rejected decision with detailed diagnostic messages. Otherwise, it applies the `sizeMultiplier` from the current regime—retrieved via `volDetector.getRegime()`—to the Kelly-adjusted or raw position size before approving the trade.

## Configuration and Implementation Examples

### Creating a Risk Engine with Custom Volatility Thresholds

```typescript
import { createRiskEngine } from './risk/engine';
import { createVolatilityDetector } from './risk/volatility';

// Example config – more conservative regime thresholds
const riskEngine = createRiskEngine(
  {
    varLimit: 100_000,
    volatilityConfig: {
      lowThreshold: 0.4,
      highThreshold: 1.2,
      extremeThreshold: 2.5,
      regimeMultipliers: { low: 1.3, normal: 1.0, high: 0.6, extreme: 0.25 },
      haltOnExtreme: true,
    },
  },
  {
    riskContext,
    safetyManager,
    circuitBreaker: createCircuitBreaker(CONSERVATIVE_CONFIG),
    kellyCalculator,
    getPositions,
    getPositionValues,
  }
);

```

### Simulating Extreme Volatility Conditions

```typescript
// Simulate extreme volatility by feeding high‑variance P&L observations
for (let i = 0; i < 40; i++) {
  riskEngine.recordPnL({ pnlPct: Math.random() * 5 - 2.5 }); // large swings
}

// Now request a trade – the engine will reject it due to the halted regime
const decision = riskEngine.validateTrade({
  userId: 'u123',
  platform: 'polymarket',
  marketId: '0xabc',
  side: 'buy',
  size: 500,
  price: 0.6,
  outcome: 'YES',
});
console.log(decision.approved); // false
console.log(decision.reason);   // "Extreme volatility — trading halted"

```

### Accessing Circuit Breaker State via Dashboard

```typescript
const dashboard = riskEngine.getDashboard();
console.log(dashboard.circuitBreakerState?.tripped); // true/false
if (dashboard.circuitBreakerState?.tripped) {
  console.log('Trip reason:', dashboard.circuitBreakerState.tripEvent?.condition.type);
}

```

## Summary

- The **Volatility Detector** in [`src/risk/volatility.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/risk/volatility.ts) maintains a 30-period rolling window of P&L percentages to calculate standard deviations and classify markets into low, normal, high, or extreme regimes.
- Each volatility regime applies a configurable **size multiplier** to reduce position exposure during turbulent periods, with an optional **trading halt** when `haltOnExtreme` is enabled for extreme regimes.
- The **Circuit Breaker** in [`src/risk/circuit-breaker.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/risk/circuit-breaker.ts) provides independent trip conditions based on external volatility metrics (`maxVolatilityPct`) and other risk factors, creating a hard stop via `tripBreaker()`.
- Both subsystems integrate in [`src/risk/engine.ts`](https://github.com/alsk1992/CloddsBot/blob/main/src/risk/engine.ts) during the `validateTrade` sequence, with the circuit breaker acting as a pre-trade gate and the volatility detector applying dynamic sizing adjustments.
- **Automatic reset** functionality allows the circuit breaker to resume trading after a configurable `cooldownMs` period, while the dashboard provides real-time visibility into both the current regime and breaker state.

## Frequently Asked Questions

### How does the volatility detector calculate the baseline standard deviation?

The detector stores P&L observations in a rolling `window` array capped at the `lookbackWindow` configuration (default 30). Once the window fills completely, it computes the initial `baselineStdDev` using the `computeStdDev` function on the full window. Subsequent regime classifications compare current volatility against this baseline to determine if market conditions have shifted relative to recent history.

### What happens when the circuit breaker trips due to volatility?

When the `checkVolatilityCondition` function detects that `getVolatilityPct` exceeds the configured `maxVolatilityPct`, it returns a trip signal. The `tripBreaker` method then sets `state.tripped` to true, records the timestamp and trip reason, logs a warning with the volatility percentage, and emits a `tripped` event. All subsequent calls to `canTrade()` return false, rejecting new orders until the breaker resets.

### Can the circuit breaker automatically resume trading after a volatility spike?

Yes. If the configuration specifies `autoReset: true` and a positive `cooldownMs` value, the breaker automatically schedules a timer upon tripping. When the cooldown duration elapses, `resetBreaker(false)` clears the tripped state without manual intervention, allowing the `canTrade()` check to pass again for new trade requests.

### How does the engine combine volatility regime adjustments with Kelly sizing?

During `validateTrade`, the engine first calculates the Kelly-optimal position size (if enabled), then applies the `sizeMultiplier` from the current volatility regime as a final scaling factor. For example, a 1,000 unit Kelly-optimal position in a high-volatility regime (0.5× multiplier) would execute as 500 units. This sequential application ensures that market turbulence reduces exposure even when the underlying edge calculation suggests a larger position.