Complete derivation of all formulas used by the Flash Risk Engine. Every formula references its source in the Flash Trade reference implementation or SDK.
All arithmetic is performed with arbitrary-precision integers (BN.js). No intermediate computation uses floating-point. The following scales are used throughout:
| Scale Name | Value | Usage |
|---|---|---|
| USD_DECIMALS | 10^6 | All USD amounts: size, collateral, margin, PnL, fees |
| ORACLE_PRICE_SCALE | 10^9 | All oracle prices (entry, exit, liquidation, current) |
| BPS_DECIMALS | 10^4 | Leverage ratios, fee ratios, max_leverage thresholds |
| RATE_DECIMALS | 10^9 | Borrow rates, utilization, lock fee accumulators |
| Trade spread | 10^4 * 100 = 10^6 | Spread values are in 100ths of a basis point |
usd = token_amount * oracle_price / 10^(token_decimals + 3)
The +3 derives from: |ORACLE_EXPONENT_SCALE| - USD_DECIMALS = 9 - 6 = 3.
Source: flash-sdk-rust/programs/flash-read/src/states.rs -> impl OraclePrice -> fn get_asset_amount_usd
The protocol uses a dual-oracle model. The Custom Oracle account provides spot price, EMA price, and a confidence interval. The price range is determined by comparing spot-EMA divergence against a custody-specific threshold:
divergence = |spot_price - ema_price| * BPS_POWER / ema_price
if divergence <= max_divergence_bps:
min_price = spot_price
max_price = spot_price
else:
min_price = max(0, spot_price - confidence)
max_price = spot_price + confidence
When divergence is low, the engine uses a single price (no confidence spread). When divergence is high, the confidence band widens the price range.
Source: flash-sdk-rust/programs/flash-read/src/states.rs -> impl OraclePrice -> fn fetch_from_oracle
The exit price is the worst-case price for the trader, adjusted by the trade spread:
spread = trade_spread_min + (trade_spread_max - trade_spread_min)
* size_usd / max_position_size_usd
scale = BPS_POWER * 100 // = 1,000,000
Long exit: exit_price = min_price * (scale - spread) / scale
Short exit: exit_price = max_price * (scale + spread) / scale
Long positions exit at the lower price minus spread. Short positions exit at the higher price plus spread.
Source: flash-perpetuals/programs/perpetuals/src/state/pool.rs -> fn get_exit_price
Long:
price_diff_profit = max(0, exit_price - entry_price)
price_diff_loss = max(0, entry_price - exit_price)
Short:
price_diff_profit = max(0, entry_price - exit_price)
price_diff_loss = max(0, exit_price - entry_price)
profit_usd = size_usd * price_diff_profit / entry_price
loss_usd = size_usd * price_diff_loss / entry_price
The total loss includes borrow interest and close fee:
total_loss = price_loss + interest_usd + close_fee_usd
total_profit = min(price_profit, locked_value_usd) // profit cap
Profit is capped at the position's locked collateral value in USD.
if total_profit > total_loss:
final_profit = total_profit - total_loss
final_loss = 0
else:
final_profit = 0
final_loss = total_loss - total_profit
Source: flash-perpetuals/programs/perpetuals/src/state/pool.rs -> fn get_pnl_usd
current_margin_usd = collateral_usd + final_profit - final_loss
margin = max(0, current_margin_usd) // clamp to zero
Source: flash-perpetuals/programs/perpetuals/src/state/pool.rs -> fn get_leverage
if margin == 0:
leverage = MAX_SAFE_INTEGER // sentinel for infinite leverage
else:
leverage = size_usd * BPS_POWER / margin
Result is in BPS. 10x leverage = 100,000 BPS.
Source: flash-perpetuals/programs/perpetuals/src/state/pool.rs -> fn get_leverage
A position is liquidatable when:
current_leverage > max_leverage
Where max_leverage is custody.pricing.maxLeverage (in BPS, e.g., 1,000,000 = 100x).
Source: flash-perpetuals/programs/perpetuals/src/state/pool.rs -> fn check_leverage
Starting from the liquidation condition and solving for price:
leverage > max_leverage
size_usd * BPS_POWER / margin > max_leverage
margin < size_usd * BPS_POWER / max_leverage
Let min_margin = size_usd * BPS_POWER / max_leverage
At the liquidation price, margin equals min_margin. Substituting the margin formula:
collateral + PnL(liq_price) - fees = min_margin
For a long position, PnL at liquidation price P_liq:
PnL = size_usd * (P_liq - entry_price) / entry_price
Solving for P_liq:
buffer = collateral - fees - min_margin
Long: P_liq = entry_price * (size_usd - buffer) / size_usd
Short: P_liq = entry_price * (size_usd + buffer) / size_usd
Where fees = interest_usd + close_fee_usd.
For longs, the liquidation price is below entry (price must drop). For shorts, it is above entry (price must rise).
The borrow rate is a function of utilization, with a kink at the optimal utilization point:
utilization_rate = locked * RATE_POWER / owned // in RATE_DECIMALS
if utilization_rate < optimal_utilization:
// Below kink: linear interpolation from base_rate to base_rate + slope1
variable_rate = utilization_rate * slope1 / optimal_utilization
rate = base_rate + variable_rate
else:
// Above kink: steep increase from base_rate + slope1 toward base_rate + slope1 + slope2
excess = utilization_rate - optimal_utilization
remaining = RATE_POWER - optimal_utilization
variable_rate = slope1 + excess * slope2 / remaining
rate = base_rate + variable_rate
Maximum rate (at 100% utilization): base_rate + slope1 + slope2.
Source: flash-perpetuals/programs/perpetuals/src/state/custody.rs -> fn update_borrow_rate
The cumulative lock fee is updated using ceiling division:
time_delta = current_time - last_update_time // seconds
accrual = time_delta * current_rate
increment = ceil(accrual / 3600) // ceiling division
cumulative_lock_fee += increment
Ceiling division formula: ceil(a / b) = (a + b - 1) / b
This ensures borrowers are never undercharged due to integer truncation.
Source: flash-perpetuals/programs/perpetuals/src/state/custody.rs -> fn get_cumulative_interest
delta = cumulative_lock_fee_now - position_cumulative_snapshot
interest_usd = delta * locked_usd / RATE_POWER
When locked_usd == 0, interest is zero (multiplication by zero).
Source: flash-perpetuals/programs/perpetuals/src/state/custody.rs -> fn get_interest_amount_usd
close_fee_usd = size_usd * close_position_fee / RATE_POWER
Where close_position_fee is in RATE_DECIMALS (10^9).
Consider a long SOL position with the following parameters:
entry_price = 150,000,000,000 (= $150.00 at exponent -9)
current_price = 148,000,000,000 (= $148.00)
size_usd = 10,000,000,000 (= $10,000.00 at 10^6)
collateral_usd = 1,000,000,000 (= $1,000.00)
interest_usd = 5,000,000 (= $5.00)
close_fee = 1,000,000 (= $1.00)
max_leverage = 1,000,000 (= 100x in BPS)
Step 1: PnL
Exit price approximately equals current price (assuming no spread for simplicity):
price_diff_loss = 150,000,000,000 - 148,000,000,000 = 2,000,000,000
loss_usd = 10,000,000,000 * 2,000,000,000 / 150,000,000,000 = 133,333,333
Loss = $133.33
Step 2: Total loss including fees
total_loss = 133,333,333 + 5,000,000 + 1,000,000 = 139,333,333
Total loss = $139.33
Step 3: Margin
margin = 1,000,000,000 + 0 - 139,333,333 = 860,666,667
Margin = $860.67
Step 4: Leverage
leverage = 10,000,000,000 * 10,000 / 860,666,667 = 116,187
Leverage = 11.62x (116,187 BPS)
Step 5: Liquidation check
116,187 < 1,000,000 → NOT liquidatable
The position is safe (11.62x < 100x).
Step 6: Liquidation price
min_margin = 10,000,000,000 * 10,000 / 1,000,000 = 100,000,000
fees = 5,000,000 + 1,000,000 = 6,000,000
buffer = 1,000,000,000 - 6,000,000 - 100,000,000 = 894,000,000
liq_price = 150,000,000,000 * (10,000,000,000 - 894,000,000) / 10,000,000,000
= 150,000,000,000 * 9,106,000,000 / 10,000,000,000
= 136,590,000,000
Liquidation price = $136.59
Step 7: Distance to liquidation
distance = |148,000,000,000 - 136,590,000,000| / 148,000,000,000 * 100
= 11,410,000,000 / 148,000,000,000 * 100
= 7.71%
The price must drop 7.71% from $148.00 to reach liquidation at $136.59.