This guide covers stale-price protection and the new paper-trade viability engine: graded viability scoring, manual override modes, minimum meaningful edge, adaptive context-aware edge, configurable volatility floor, and how to use these settings safely in production versus testing.
_get_current_price() reads cached price and timestamp.[STALE PRICE].if age > 2.0:
logger.warning(f"[STALE PRICE] {symbol} → {age:.2f}s old")
return price
The paper gate is now a graded decision engine. It combines cost realism, per-share efficiency, minimum meaningful edge, volatility-aware adaptive filtering, and safe manual override behavior.
per_share_profit = expected_move
per_share_cost = est_cost / qty
viability_score = per_share_profit / per_share_cost
dynamic_min_edge = volatility_unit * multiplier * qty
final_min_edge = max(min_expected_profit, dynamic_min_edge)
if expected_gross < final_min_edge:
tier = "REJECT_ABSOLUTE"
| Tier | Rule | Meaning | Default Behavior |
|---|---|---|---|
| STRONG | score ≥ 2.0 | Clear edge after cost | Allow |
| ACCEPTABLE | score ≥ 1.2 | Tradable but not elite | Allow |
| WEAK | score ≥ 0.8 | Marginal edge | STRICT uses configurable REJECT/WARN |
| REJECT | score < 0.8 | Poor cost efficiency | Reject unless override or mode allows |
| REJECT_ABSOLUTE | expected_gross < final_min_edge | Too small to matter | Hard reject |
dynamic_min_edge = volatility_unit * 0.5 * qty
final_min_edge = max(min_expected_profit, dynamic_min_edge)
volatility_unit = abs(entry - stop_loss)min_volatility_unit = entry * min_volatility_floor_pct.0.001 = 0.1%, configurable in the UI.0.0001 through 0.02 for safety.min_volatility_floor_pct = max(0.0001, min(user_value, 0.02))
min_volatility_unit = entry * min_volatility_floor_pct
volatility_unit = max(volatility_unit, min_volatility_unit)
Important: manual override is not a trading edge. It is a testing convenience. Do not rely on override to justify poor signals.
Real objective: fewer but better trades. A drop in trade count is usually healthy if net expectancy, realized Sharpe, and cost efficiency improve.
expected_gross=84.00
est_cost=62.30
per_share_profit=0.4200
per_share_cost=0.3115
min_expected_profit=100.00
dynamic_min_edge=73.50
final_min_edge=100.00
min_volatility_floor_pct=0.0010
effective_volatility_unit=0.3500
tier=REJECT_ABSOLUTE
score=1.348
score tells you per-unit edge quality.tier tells you decision class.final_min_edge shows whether static or adaptive rule dominated.effective_volatility_unit shows whether the floor likely kicked in.| Question | Answer |
|---|---|
| Does stale price protection block trades? | No. It logs stale price age and still returns the price. |
| What is viability score based on? | Per-share profit divided by per-share cost. |
| What blocks tiny but efficient trades? | REJECT_ABSOLUTE via final_min_edge. |
| What is adaptive min edge? | max(min_expected_profit, volatility_unit × multiplier × qty) |
| How is volatility estimated? | Stop distance when available, otherwise expected move divided by RR. |
| What stops ultra-tight SL abuse? | A configurable minimum volatility floor percentage. |
| Is the floor safe against bad configs? | Yes. It is clamped between 0.01% and 2% in the engine. |
| Should manual override be used in production? | No. Keep it for testing, debugging, and isolated operator workflows. |
Scope: these presets are for Indian-market workflows only: NSE cash, NFO index or stock derivatives, and MCX commodities. No crypto assumptions are used here.