Risk Management

Every strategy in MangroveAI has an execution_config that controls risk management, position sizing, and trade lifecycle rules. This guide explains each field, how they interact, and how to tune them for your trading style.
Deprecation notice: The legacy cooldown keys cooldown_bars, daily_momentum_limit, weekly_momentum_limit, and the top-level max_hold_time_hours are deprecated as of the v2 cooldown redesign. They continue to work during the 90-day grace period but will be removed in a future major version. Migrate to the cooldown_config block described below.Migration summary:
Legacy keyReplacement
cooldown_barscooldown_config[tf].short_window_bars (also drives cooldown duration)
daily_momentum_limitcooldown_config[tf].short_loss_limit
weekly_momentum_limitcooldown_config[tf].long_loss_limit
max_hold_time_hours (top-level)time_based_exits.max_hold_bars (max hold is now in execution_config.max_hold_bars)

How risk management works

When a strategy generates an entry signal, the execution engine does not just open a position — it calculates the appropriate position size based on your risk budget, sets a dynamic stop-loss using ATR, and enforces limits on how many positions you can hold, how long you can hold them, and how often you can trade. All of this is configured in the execution_config object, which is stored with each strategy.

The execution_config object

Here is the complete execution_config with default values. Each field is explained in the sections below.
{
  "max_risk_per_trade": 0.01,
  "reward_factor": 2,
  "stop_loss_calculation": "dynamic_atr",
  "atr_period": 14,
  "atr_volatility_factor": 2.0,
  "min_balance_threshold": 0.1,
  "min_trade_amount": 25,
  "max_open_positions": 10,
  "max_trades_per_day": 50,
  "volatility_window": 24,
  "target_volatility": 0.02,
  "volatility_mode": "stddev",
  "enable_volatility_adjustment": false,
  "max_hold_bars": 50,
  "exit_on_loss_after_bars": 50,
  "exit_on_profit_after_bars": 60,
  "profit_threshold_pct": 0.05,
  "cooldown_config": {
    "5m":  {"short_loss_limit": 4, "long_loss_limit": 6, "short_window_bars": 180, "long_window_bars": 480},
    "15m": {"short_loss_limit": 4, "long_loss_limit": 6, "short_window_bars": 120, "long_window_bars": 320},
    "1h":  {"short_loss_limit": 4, "long_loss_limit": 6, "short_window_bars": 48,  "long_window_bars": 144},
    "1d":  {"short_loss_limit": 4, "long_loss_limit": 6, "short_window_bars": 20,  "long_window_bars": 60}
  }
}
If you do not provide execution_config when creating a strategy, the API populates it with defaults from trading_defaults.json. You only need to include fields you want to override.

Stop-loss calculation

The stop-loss determines where a losing trade is automatically closed. MangroveAI uses dynamic ATR-based stops by default.

How dynamic ATR stops work

ATR (Average True Range) measures recent price volatility. A dynamic ATR stop-loss places the stop at a distance proportional to recent volatility:
stop_loss_price = entry_price - (ATR * atr_volatility_factor)
For example, if you enter BTC at 80,000,the14periodATRis80,000, the 14-period ATR is 2,000, and your atr_volatility_factor is 2.0:
stop_loss_price = $80,000 - ($2,000 * 2.0) = $76,000
The stop is $4,000 below entry, representing 2x the current volatility.

Relevant fields

FieldDefaultDescription
stop_loss_calculation"dynamic_atr"Stop-loss method. Use "dynamic_atr" for volatility-adjusted stops.
atr_period14Number of bars to calculate ATR. Longer periods smooth out volatility spikes.
atr_volatility_factor2.0Multiplier for ATR distance. Higher = wider stop = more room to breathe.

Tuning the stop-loss

Scenarioatr_volatility_factorEffect
Tight risk control1.0 - 1.5Stops are close to entry. More trades get stopped out, but losses are small.
Standard2.0Balanced tradeoff. The default for most strategies.
Wide stops2.5 - 3.5Stops give the trade room to fluctuate. Fewer stop-outs, but larger individual losses.
ATR parameters (atr_period, atr_volatility_factor) belong in execution_config, not in strategy_json. They are injected at runtime by the backtesting and execution engines.

Take-profit calculation

Take-profit is calculated from the stop-loss distance and the reward_factor:
take_profit_distance = stop_loss_distance * reward_factor
take_profit_price = entry_price + take_profit_distance
Using the example above with reward_factor: 2:
take_profit_price = $80,000 + ($4,000 * 2) = $88,000
FieldDefaultDescription
reward_factor2Risk/reward ratio. A value of 2 means the take-profit target is 2x the stop-loss distance.
A reward_factor of 2 means you need to win only 34% of trades to break even (excluding fees). Combined with a reasonable win rate, this creates a positive expected value.

Position sizing

Position sizing determines how much capital to allocate per trade.

Risk-based sizing

The max_risk_per_trade field sets the maximum percentage of your account you are willing to lose on a single trade:
risk_amount = account_value * max_risk_per_trade
position_size = risk_amount / stop_loss_distance
Example with a 10,000account,110,000 account, 1% risk, and 4,000 stop distance on BTC:
risk_amount = $10,000 * 0.01 = $100
position_size = $100 / $4,000 = 0.025 BTC
FieldDefaultDescription
max_risk_per_trade0.01Max risk per trade as a decimal (0.01 = 1% of account).
min_trade_amount25Minimum trade size in dollars. Trades below this are skipped.
min_balance_threshold0.1Minimum account balance as a fraction. Trading pauses if balance drops below this.

Volatility-adjusted sizing

Optionally, position sizes can be scaled based on current volatility:
FieldDefaultDescription
enable_volatility_adjustmentfalseWhen true, position size is scaled inversely to volatility.
volatility_window24Number of bars for volatility calculation.
target_volatility0.02Target portfolio volatility level.
volatility_mode"stddev"Volatility measure: "stddev" (standard deviation) or "atr".
When enabled, positions are smaller during high-volatility periods and larger during low-volatility periods, targeting consistent portfolio risk.

Position limits

These fields prevent over-concentration and overtrading:
FieldDefaultDescription
max_open_positions10Maximum concurrent open positions. New entries are blocked once this limit is reached.
max_trades_per_day50Daily trade cap. Prevents runaway trading in volatile markets.

Tuning for your style

Trading stylemax_open_positionsmax_trades_per_day
Conservative1 - 35 - 10
Moderate5 - 1020 - 50
Active10 - 2050 - 100

Cooldown system

The cooldown system prevents a strategy from entering new positions after a losing streak. It uses a two-tier design — a short tier that catches acute streaks, and a long tier that catches sustained underperformance. Both tiers operate independently per asset.

Two-tier design

TierPurposeTrips when…
ShortDetects an acute losing streakshort_loss_limit losses occur within the last short_window_bars bars
LongDetects sustained underperformancelong_loss_limit losses occur within the last long_window_bars bars
When a tier trips, the strategy enters a cooldown. The cooldown duration equals the window itself: tripping the short tier blocks entries for short_window_bars bars; tripping the long tier blocks entries for long_window_bars bars. This means the window bar count serves double duty — it is both the rolling lookback and the cooldown duration. If a short-tier trip occurs while a long cooldown is already active, the longer cooldown is preserved. Cooldowns never shorten an already-active block.

The cooldown_config block

cooldown_config is a dict keyed by primary timeframe. Each timeframe has four values:
KeyDescription
short_loss_limitNumber of losses in the short window that trips the short-tier cooldown
long_loss_limitNumber of losses in the long window that trips the long-tier cooldown
short_window_barsRolling lookback for the short tier AND the cooldown duration when short trips
long_window_barsRolling lookback for the long tier AND the cooldown duration when long trips
Max hold time is controlled separately via time_based_exits.max_hold_bars in execution_config.
"cooldown_config": {
  "5m":  {"short_loss_limit": 4, "long_loss_limit": 6, "short_window_bars": 180, "long_window_bars": 480},
  "15m": {"short_loss_limit": 4, "long_loss_limit": 6, "short_window_bars": 120, "long_window_bars": 320},
  "1h":  {"short_loss_limit": 4, "long_loss_limit": 6, "short_window_bars": 48,  "long_window_bars": 144},
  "1d":  {"short_loss_limit": 4, "long_loss_limit": 6, "short_window_bars": 20,  "long_window_bars": 60}
}

Reading the defaults

For a 1h strategy with the defaults above:
  • Short tier: 4 losses in the last 48 bars (2 days) trips a 48-bar cooldown
  • Long tier: 6 losses in the last 144 bars (6 days) trips a 144-bar cooldown
  • Max hold is set separately via execution_config.max_hold_bars (see Time-based exits below)
For a 5m strategy:
  • Short tier: 4 losses in the last 180 bars (15 hours) trips a 180-bar cooldown
  • Long tier: 6 losses in the last 480 bars (40 hours) trips a 480-bar cooldown
  • Max hold is set separately via execution_config.max_hold_bars (see Time-based exits below)

Tuning guidance

The defaults are calibrated for standard multi-timeframe use. Adjust based on your strategy’s expected win rate and trade frequency:
  • Higher short_loss_limit: more tolerance for short losing streaks before cooling off
  • Lower short_window_bars: narrower lookback — only very recent losses count
  • Larger long_window_bars: spread the long-tier assessment over a wider history
  • Smaller max_hold_bars (in execution_config): force position turnover faster regardless of timeframe

Fallback (legacy keys)

If cooldown_config is absent from execution_config, or if the strategy’s primary timeframe is not a key in cooldown_config, the engine falls back to the legacy flat keys and emits a structured deprecation warning in the logs. See the deprecation notice at the top of this page for the migration mapping.

Time-based exits

These fields force positions to close after a certain number of bars, regardless of signals:
FieldDefaultDescription
max_hold_bars50Maximum bars to hold any position. Forces exit after this limit.
exit_on_loss_after_bars50Force-exit losing positions after this many bars.
exit_on_profit_after_bars60Force-exit winning positions after this many bars.
profit_threshold_pct0.05What counts as “winning” for exit_on_profit_after_bars. 0.05 = 5% unrealized profit.
Time-based exits are a safety net. They prevent positions from lingering indefinitely in a stagnant market.

Example scenario

With defaults on a 1h timeframe:
  • A losing trade is forced closed after 50 bars (about 2 days)
  • A winning trade (5%+ profit) is forced closed after 60 bars (about 2.5 days)
  • Any position, regardless of P&L, closes after 50 bars (max_hold_bars)

Putting it all together

Here is a complete example that creates a strategy with custom risk parameters — tighter stops, smaller position sizes, fewer concurrent positions, and explicit cooldown config for a 1h timeframe:
curl -s -X POST "https://api.mangrovedeveloper.ai/api/v1/strategies" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Conservative BTC Strategy",
    "asset": "BTC",
    "entry": [
      {
        "name": "sma_cross_up",
        "signal_type": "TRIGGER",
        "timeframe": "1h",
        "params": {"window_fast": 10, "window_slow": 50}
      },
      {
        "name": "adx_strong_trend",
        "signal_type": "FILTER",
        "timeframe": "1h",
        "params": {"window": 14, "threshold": 25}
      }
    ],
    "exit": [],
    "reward_factor": 2.5,
    "execution_config": {
      "max_risk_per_trade": 0.005,
      "reward_factor": 2.5,
      "stop_loss_calculation": "dynamic_atr",
      "atr_period": 14,
      "atr_volatility_factor": 1.5,
      "min_balance_threshold": 0.1,
      "min_trade_amount": 50,
      "max_open_positions": 3,
      "max_trades_per_day": 10,
      "volatility_window": 24,
      "target_volatility": 0.02,
      "volatility_mode": "stddev",
      "enable_volatility_adjustment": false,
      "max_hold_bars": 30,
      "exit_on_loss_after_bars": 24,
      "exit_on_profit_after_bars": 36,
      "profit_threshold_pct": 0.03,
      "cooldown_config": {
        "1h": {"short_loss_limit": 3, "long_loss_limit": 5, "short_window_bars": 48, "long_window_bars": 144}
      }
    }
  }' | python3 -m json.tool
Key differences from defaults:
  • 0.5% risk per trade instead of 1% — halves position sizes
  • 1.5x ATR stops instead of 2x — tighter exit on losses
  • 2.5x reward factor — requires more upside before taking profit
  • 3 max positions instead of 10 — less capital at risk simultaneously
  • Explicit 1h cooldown_config — 3-loss short trip, 5-loss long trip, both tuned for conservative operation

Quick reference: all fields

FieldTypeDefaultDescription
max_risk_per_tradenumber0.01Max risk per trade (0.01 = 1%)
reward_factornumber2Risk/reward ratio for take-profit
stop_loss_calculationstring"dynamic_atr"Stop method ("dynamic_atr" or "fixed")
atr_periodint14ATR lookback period (bars)
atr_volatility_factornumber2.0ATR multiplier for stop distance
min_balance_thresholdnumber0.1Min balance fraction before trading pauses
min_trade_amountnumber25Min trade size in dollars
max_open_positionsint10Max concurrent positions
max_trades_per_dayint50Daily trade limit
volatility_windowint24Bars for volatility calc
target_volatilitynumber0.02Target portfolio volatility
volatility_modestring"stddev"Volatility method ("stddev" or "atr")
enable_volatility_adjustmentboolfalseScale position sizes by volatility
max_hold_barsint50Max bars for any position
exit_on_loss_after_barsint50Force-exit losers after N bars
exit_on_profit_after_barsint60Force-exit winners after N bars
profit_threshold_pctnumber0.05Threshold for “winning” (0.05 = 5%)
cooldown_configobjectsee defaultsPer-timeframe cooldown settings (see above)
cooldown_barsint24Deprecated. Use cooldown_config[tf].short_window_bars.
daily_momentum_limitnumber3Deprecated. Use cooldown_config[tf].short_loss_limit.
weekly_momentum_limitnumber3Deprecated. Use cooldown_config[tf].long_loss_limit.
max_hold_time_hoursnumber/nullnullDeprecated. Use execution_config.max_hold_bars (bars) instead of hours.

Next steps