Perpetual futures: Percolator haircut risk model and an insurance fund - #179
Merged
Merged
Conversation
added 2 commits
October 1, 2026 20:37
…e with a trading band Initial margin: max_leverage becomes initial_margin_bps, which must exceed maintenance_margin_bps (InitialMarginNotAboveMaintenance). open_position requires net collateral of at least initial_margin_bps of the size (InitialMarginNotMet); the separate maintenance check at open is implied. Price band: the pool keeps a time-weighted average of the oracle price (average_price, last_oracle_price, average_price_timestamp, ten-minute window). Each fold credits the elapsed time to the price seen at the previous read, so one manipulated read after an idle spell moves nothing. open_position, close_position, add_liquidity and remove_liquidity refuse an oracle price more than max_price_deviation_bps from the average (PriceOutsideBand). liquidate_position and the new permissionless update_price_average() fold without the check, so liquidations run through a genuine move and the average can catch up with it. All three implementations; anchor 37, anchor-v1 37, quasar 28 tests. Claude-Session: https://claude.ai/code/session_019G9tytYrS3Qp42fZ1hBnDu (cherry picked from commit 1f1a46e)
Replaces reserve-and-cap with the risk model from Anatoly Yakovenko's Percolator: trader collateral is senior and profit is junior, paid only as far as the pool can back it. - Nothing is reserved when a position opens, and profit has no cap. reserved_liquidity is removed. - close_position pays a winner profit x h, with h = min(1, (liquidity + insurance_fund) / max(net unrealized profit, the closer's own profit)), computed before the position leaves the accumulators. Winners are paid the same fraction and are never refused for lack of backing; profit is paid from liquidity, then insurance. - Profit warm-up: profit can be taken only profit_warmup_slots after entry_slot (ProfitNotMatured); losses and liquidations are not delayed. - insurance_fee_bps of every fee goes to insurance_fund, which pays a bankrupt position's deficit before the liquidity providers. A liquidation fee the position's equity cannot cover is forgiven. - remove_liquidity is capped at liquidity (InsufficientLiquidity). Reimplements #83 on current code, in all three implementations. Anchor v2 47, Anchor v1 47, Quasar 39 tests. Claude-Session: https://claude.ai/code/session_019G9tytYrS3Qp42fZ1hBnDu
This was referenced Oct 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces the reserve-and-cap risk model with the one from Anatoly Yakovenko's Percolator: trader collateral is senior, profit is junior and paid only as far as the pool can back it. All three implementations (Anchor v2, Anchor v1, Quasar).
Replaces #83, which was built on pre-rewrite history; this reimplements its design on current code.
Builds on #176 (initial margin and price band). Merge #176 first; this PR's own change is the top commit.
Changes
reserved_liquidityand the open-time backing check are removed; positions open regardless of pool size.close_positionpays a winnerprofit × h,h = min(1, (liquidity + insurance_fund) / max(net unrealized profit, the closer's own profit)), floored atHAIRCUT_PRECISION, computed before the closer leaves the accumulators. Every winner is paid the same fraction, and no winning close is refused for lack of backing (a winner closing while open losers still offset them is paid at most the pool's backing). Profit is paid fromliquidity, theninsurance_fund.profit_warmup_slotsafterPosition.entry_slot(ProfitNotMatured); losses and liquidations are not delayed.insurance_fee_bpsof every open and close fee goes toinsurance_fund(the rest toprogram_fees), which pays a bankrupt position's deficit before the liquidity providers.liquidity(InsufficientLiquidity).Tests
Anchor v2: 47, Anchor v1: 47, Quasar: 39, all passing;
cargo fmtandclippy -D warningsclean. New tests includetest_haircut_scales_profit_when_pool_stressed,test_winner_offset_by_open_loser_is_paid_not_refused,test_profit_blocked_before_maturation,test_insurance_fund_funded_by_feesandtest_liquidation_of_bankrupt_position_charges_insurance_before_liquidity.🤖 Generated with Claude Code
https://claude.ai/code/session_019G9tytYrS3Qp42fZ1hBnDu
Generated by Claude Code