From ce97ac03243673bc6c066d749ca59ffe46819fb1 Mon Sep 17 00:00:00 2001 From: Gordsport <83510148+gordsport@users.noreply.github.com> Date: Thu, 22 Feb 2024 11:12:03 +0000 Subject: [PATCH] feat(palazzo): Merge palazzo into master (#2183) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: remove redundant AC 013 * feat: address comments * feat: remove duplicated ACs * feat: remove duplicated ACs * feat: rename network parameter FeeDiscountDecay * feat: add governance proposal change market name * chore: typo * feat: address comments * feat: typo * chore: add ACs to features file for overnance proposal market name * Insurance pools of closed / settled markets to go to on - chain treasury (#2065) * feat: insurance pool redistribution * feat: add more updates * chore: markdown * chore: remove old AC * feat: address comments Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * feat: address comments Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * feat: address comments * feat: update spec 0015 * chore: markdown * chore: typo --------- Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * feat: add more ACs to isolated margin * chore: markdown * chore: markdown * feat: add more ACs * chore: markdown * chore: markdown * chore: typo * feat: address comments * chore: typo * Remove a duplicate AC (#2073) * fix: Removed duplicate AC * fix: Removed duplicate AC * fix: formula in ac * fix: diff twap if auct was included * feat: tidy up and add more ACS * chore: reorder * chore: format * fix: MD and spelling checks * feat: add ACs for switch margin modes * feat: update closeout * feat: update closeout * chore: remove mystery AC * feat: address commments from Tom * chore: typo * fix: AC 0053-PERP-030 now flexes the boundary * fix: formatting * Update protocol/0053-PERP-product_builtin_perpetual_future.md Co-authored-by: Gordsport <83510148+gordsport@users.noreply.github.com> * fix: another formatting issue * feat: A couple more ACs, API clarifications and Removing excessive margin check (#2076) * feat: add alias to public keys * feat: add allow list to teams * feat: align spec with core change adding JoinTeam * chore: assign ac codes * chore: remove mentions of alias * feat: stop order position linked (#2043) * feat: Stop order changes * fix: correct expected behaviour * chore: duplicate ac * fix: address comments * feat: update for pegged order in isolated margin * feat: update json * chore: markdown * feat: more margin pegged order acs * fix: typo marging > margin * fix: duplicate ac code * fix: more marging typo * refactor: remove two acs * chore: add 165 back in * chore: position of ac corrected for diff * feat: update AC * feat: add back pegged order AC * chore: markdown * chore: typo * feat: update market state duing name changing * feat: address comment * chore: typo * feat: mark price updates ACs * fix: replace withdrawal limit > maximumLifetimeDeposit * fix: spellcheck * feat: update meaning of closed and allow_list team fields (#2090) * feat: change meaning of closed and allow_list * chore: reassign AC codes * fix: update wordlist * feat: add two system-test acs * feat: clarify market has no pm params * fix: update ac numbers * fix: still dup ac numbers * feat: update AC and add more AC * chore: tidy up * chore: markdown * feat: address comments * chore: markdown * chore: add timing * chore: typo * chore: Add AC code to the featurtes.json Also sneaking in a categories tidy up * fix: duplicated AC codes in features.json * feat: address comment * feat: EthL2s * fix: better name * fix: clarity on what is meant by L2 * chore: add initial AC codes and fix linting * fix: codeblock formatting * fix: milestone name * chore: Add ACs from the Eth spec to the L2 spec * fix: review comment * fix: MD linting * fix: approbation linting * fix: clarity on what is meant by L2 * feat: few acs for stop orders in auction * fix: updated ac numbers * fix: still dup acs * fix: removed AC codes and add L2 clarification * feat: added one more ac * feat: add acs for network profit and loss * fix: update for markdownlint * fix: update for spellcheck * fix: clarity on what is meant by L2 * fix: MD linting and add AC code to file * fix: MD linting and spellings * fix: spelling linting * chore: add AC code to existing AC (#2102) Adds an AC code to an AC that had one missing. * feat: add more acs * feat: add ac for first mark price * chore: markdown * feat: add more ac * feat: update AC 001 * chore: address comments Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * chore: address comment * chore: typo * feat: add more AC for validation * chore: typo * chore: markdown * chore: address comment * feat: add validation ac for mark price * fix: post-research call edits * refactor: scale funding payment down (auction) * refactor: apply suggestions from code review Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * feat: add liquidation acs * fix: correct typo in features * chore: update AC codes for rebase * fix: markdown * feat: add AC 126 * chore: Add more perps funding ACs (#2110) * chore: Add more perps funding ACs Adds more perps funding ACs from the AC review meeting Note the ACs mentioned for: - https://github.com/vegaprotocol/vega/issues/10254 - https://github.com/vegaprotocol/vega/issues/10255 Have NOT yet been added as the core side work has not been scheduled. A note has been added to the core issue * chore: Add AC code to the features.json file * fix: spelling * fix: spelling * fix: more spelling * fix: AC wording Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> --------- Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * fix: add missing AC codes to the featurews.json file Adds in the missing margin, markprice and perp AC codes * chore: Add ACs from the LP fee setting AC review meeting (#2115) * chore: Add ACs from the LP fee setting AC review meeting This PR adds new ACs from the LP fee setting AC review meeting. * fix: add codes to features.json * fix: add AC clarification * fix: wording from review comment Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * fix: fill in value --------- Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * fix: liq fee setting method market update * chore: comment out the cosmic-carryover (#2120) * chore: comment out the cosmic-carryover This comments out the cosmic carry over ACs so that the approbation stats reflect just the palazzo ACs * fix: syntax error * feat: more validation acs * fix: correct the AC requirement to match the correct behaviour (#2111) * fix: correct the AC requirement to match the correct behaviour * feat: corrected ac and added 2 more * fix: duplicated AC codes * fix: json syntax error * fix: Extra AC codes --------- Co-authored-by: Sohill-Patel <85170301+Sohill-Patel@users.noreply.github.com> * chore: update features.json (#2123) In order to have the correct reporting on palazzo AC coverage I have remove some ACs that will NOT be done: insurance pool re-dist:

 0073-LIMN-115 0073-LIMN-116 0073-LIMN-117 perps/sucessor markets: 0015-INSR-005 0081-SUCM-036 We do not support sucessor and perps and we are not supporting LNL * chore: split AC code 056 into 3 (#2126) Splits the AC code 056 into 3 parts and adds one new AC. All to be done as system tests * chore: adds AC from the isolated margin AC review (#2125) * chore: adds AC from the isolated margin AC review Adds in protocol upgrade AC coverage for isolated margin as per the isolated margin AC review meeting * chore: Update AC to be more clear Co-authored-by: Sohill-Patel <85170301+Sohill-Patel@users.noreply.github.com> --------- Co-authored-by: Sohill-Patel <85170301+Sohill-Patel@users.noreply.github.com> * chore Update features.json to add missing AC code (#2127) * chore Update features.json to add missing AC code Adds the following missing AC to the features file: "0087-EVMD-043" * fix: remove "0053-PERP-036", as its a nebula AC * chore: add more position stop order AC codes (#2109) * chore: add more position stop order AC codes Adds in ACs from the position stop orders feature AC review * fix: linting checks * fix: review comment * fix: reduce ethereum rpc data source flexibility * fix: reduce ethereum rpc data source flexibility * fix: AC code approbation check fail * feat: add AC 003 for mark price * chore: update json file * feat: update ac 027 * chore: grammar * feat: update initial margin level in isolated margin * chore: grammar Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * chore: typo Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * feat: change tau scaling bound * feat: update bound * feat: update probability of trading in LP score * chore: address comment Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> * chore: markdown * chore: descope AC 0053-PERP-035 from Palazzo (#2135) based on a research / protocol design discussion this PR descopes AC 0053-PERP-035 from Palazzo * fix: Updating AC to cover wrong side stop orders being rejected * feat: add party profile feature * feat: add anti alias squatting feature * Revert "feat: add anti alias squatting feature" This reverts commit 6c3f548c773cd1344d55d76e987e1330ae70d03f. * chore: correct restriction typos * feat: add validation and defaults for new network parameters * feat: add ac for price via data node * feat: update json file * chore: remove ACs from palazzo (#2140) 0009-MRKP-133 and 0053-PERP-043 will be moved to Nebula in a new PR * refactor: update liquidation price estimate * chore: add feature file for 0012-NP-LIPE ACs * refactor: add spec for position estimate * chore: Update features.json * chore: renove dupe AC from features file (#2151) * chore: add deatails about the max proposals in a batch This updates the spec to details the max number of proposals that can be submitted in a single batch proposal * chore: add teams spam protection (#2157) Copies the changes made to the Colosseo branch here: - https://github.com/vegaprotocol/specs/pull/2156 * feat: add ac to 0019 * chore: add features.json entries * fix: dupe ACs * chore: add update team spam ACs (#2159) * chore: add update team spam ACs Adds in ACs for also updating referral sets / teams and spam protection * fix: spelling * chore: Update protocol/0062-SPAM-spam_protection.md Co-authored-by: Jiajia-Cui <92106936+Jiajia-Cui@users.noreply.github.com> * chore: Update protocol/features.json Co-authored-by: Jiajia-Cui <92106936+Jiajia-Cui@users.noreply.github.com> * fix: net param name --------- Co-authored-by: Jiajia-Cui <92106936+Jiajia-Cui@users.noreply.github.com> * feat: add more ACs * chore: typo * feat: market update ACs for mark price and perps price (#2171) * feat: market update ACs for mark price and perps price * feat: market update ACs for mark price and perps price * feat: market update ACs for mark price and perps price * feat: market update ACs for mark price and perps price * feat: market update ACs for mark price and perps price * feat: market update ACs for mark price and perps price * chore: add ACs to feature.json file --------- Co-authored-by: gordsport * refactor: AC clarifications * refactor: amend 0012-NP-LIPE-005 * refactor: clarify and extend liquidation ACs * refactor: additional clarifications * feat: Add extra validator ranking reward metric AC (#2174) * feat: Add extra validator ranking reward metric AC Adds an extra validator ranking reward metric AC: For reward metrics relating to trading, an individual must meet the staking requirement AND notional time-weighted average position requirement) set in the recurring transfer. If they do not then their reward metric is set to 0. Note, these requirements do not apply to the validator ranking metric or the market creation reward metric. e.g. For a party that is a consensus or standby validator, the staking requirement and notional time-weighted average position requirement do not apply to their validator ranking metric * chore: split AC into 3 * fix: remove old AC * refactor: amend a few perp ACs * chore: remove dup ac * refactor: modify AC * feat: clarify and add ac for gov suspend and resume * fix: duplicate ac code * fix: dup ac codes * fix: conflicts and spell / md lint checks * fix: missed dupe ACs * chore: remove spot --------- Co-authored-by: Jiajia-Cui Co-authored-by: Jiajia-Cui <92106936+Jiajia-Cui@users.noreply.github.com> Co-authored-by: David Siska <62546419+davidsiska-vega@users.noreply.github.com> Co-authored-by: Pete Barrow <62435083+peterbarrow@users.noreply.github.com> Co-authored-by: Sohill-Patel <85170301+Sohill-Patel@users.noreply.github.com> Co-authored-by: Witold Co-authored-by: wwestgarth Co-authored-by: wwestgarth Co-authored-by: Tom Co-authored-by: Charlie Co-authored-by: Charlie <99198652+cdummett@users.noreply.github.com> Co-authored-by: Witold Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> Co-authored-by: Pete Barrow Co-authored-by: Jeremy Letang --- .github/workflows/quality_check.yml | 3 +- ...007-NP-SNTP-sweetwater_signed_netparams.md | 6 +- ...0012-NP-LIPE-liquidation-price-estimate.md | 21 +- .../0013-NP-POSE-position-estimate.md | 58 ++ protocol/0001-MKTF-market_framework.md | 1 + protocol/0002-STTL-settlement.md | 12 +- .../0003-MTMK-mark_to_market_settlement.md | 4 + protocol/0004-AMND-amends.md | 35 +- protocol/0009-MRKP-mark_price.md | 145 ++- protocol/0010-MARG-margin_orchestration.md | 11 +- .../0011-MARA-check_order_allocate_margin.md | 17 - protocol/0012-POSR-position_resolution.md | 224 ++-- protocol/0013-ACCT-accounts.md | 10 +- protocol/0014-ORDT-order_types.md | 45 +- ...5-INSR-market_insurance_pool_collateral.md | 4 +- protocol/0019-MCAL-margin_calculator.md | 614 ++++++++++- protocol/0020-APIS-core_api.md | 2 +- protocol/0025-OCRE-order_submission.md | 1 - protocol/0026-AUCT-auctions.md | 11 +- protocol/0027-ASSP-asset_proposal.md | 1 + protocol/0028-GOVE-governance.md | 72 +- protocol/0031-ETHB-ethereum_bridge_spec.md | 2 +- protocol/0032-PRIM-price_monitoring.md | 4 +- ...PROB-prob_weighted_liquidity_measure.ipynb | 4 +- protocol/0035-LIQM-liquidity_monitoring.md | 76 -- protocol/0041-TSTK-target_stake.md | 6 +- ...042-LIQF-setting_fees_and_rewarding_lps.md | 58 +- protocol/0043-MKTL-market_lifecycle.md | 65 +- protocol/0044-LIME-lp_mechanics.md | 9 +- protocol/0051-PROD-product.md | 4 +- ...3-PERP-product_builtin_perpetual_future.md | 224 +++- protocol/0056-REWA-rewards_overview.md | 13 +- protocol/0057-TRAN-transfers.md | 89 +- ...0059-STKG-simple_staking_and_delegating.md | 2 +- protocol/0062-SPAM-spam_protection.md | 90 +- .../0065-FTCO-floating_point_consensus.md | 1 - protocol/0068-MATC-matching_engine.md | 2 +- protocol/0072-SPPW-spam-protection-PoW.md | 19 +- protocol/0073-LIMN-limited_network_life.md | 39 +- .../0074-BTCH-batch-market-instructions.md | 7 +- .../0079-TGAP-transaction_gas_and_priority.md | 1 - protocol/0081-SUCM-successor_markets.md | 13 +- protocol/0082-ETHD-ethereum-data-source.md | 1 + .../0083-RFPR-on_chain_referral_program.md | 79 +- protocol/0085-RVST-rewards_vesting.md | 3 +- .../0087-EVMD-eth-rpc-and-evm-data-source.md | 146 +++ protocol/0088-PPRF-party_profile.md | 37 + protocol/README.md | 1 - protocol/categories.json | 7 +- protocol/cosmic-features.json | 943 +++++++++++++++++ protocol/features.json | 986 ++++++++---------- wordlist.txt | 68 +- 52 files changed, 3118 insertions(+), 1178 deletions(-) create mode 100644 non-protocol-specs/0013-NP-POSE-position-estimate.md delete mode 100644 protocol/0035-LIQM-liquidity_monitoring.md create mode 100644 protocol/0087-EVMD-eth-rpc-and-evm-data-source.md create mode 100644 protocol/0088-PPRF-party_profile.md create mode 100644 protocol/cosmic-features.json diff --git a/.github/workflows/quality_check.yml b/.github/workflows/quality_check.yml index f649a0c47..fea81880c 100644 --- a/.github/workflows/quality_check.yml +++ b/.github/workflows/quality_check.yml @@ -8,7 +8,8 @@ name: "Quality checks" branches: - master - cosmicelevator - - palazzomistero + - palazzo + - colosseo env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/non-protocol-specs/0007-NP-SNTP-sweetwater_signed_netparams.md b/non-protocol-specs/0007-NP-SNTP-sweetwater_signed_netparams.md index 5ccba9522..77da509b5 100644 --- a/non-protocol-specs/0007-NP-SNTP-sweetwater_signed_netparams.md +++ b/non-protocol-specs/0007-NP-SNTP-sweetwater_signed_netparams.md @@ -3,7 +3,7 @@ There are parameters within Vega that influence the behaviour of the system: - some are set in genesis block but fixed once network is running, -- while others are changeable by on-chain [governance](../protocol/0028-GOVE-governance.md) but initialised to genesis values during network launch. For more info see [network paramters](../protocol/0054-NETP-network_parameters.md) +- while others are changeable by on-chain [governance](../protocol/0028-GOVE-governance.md) but initialised to genesis values during network launch. For more info see [network parameters](../protocol/0054-NETP-network_parameters.md) On [Sweetwater (Restricted Mainnet) Release](https://github.com/orgs/vegaprotocol/projects/114) Vega Team wishes to control how certain parameters are initialised while letting validators change others as they see fit. As the process of decentralisation progresses Vega Team the number of such parameters will be reduced. @@ -22,8 +22,8 @@ The values to be specified as a PR against ??? repo. | Name | Comment | Suggested value (optional) | |-------------------------------------------------------------|:------------------------------------------------------------------:| :-------------------------:| -| `min number of validators` (not in sweetwater) | Not in [network paramters](../protocol/0054-NETP-network_parameters.md) | | -| `validator min balance` | Not in [network paramters](../protocol/0054-NETP-network_parameters.md) | 3000 VEGA | +| `min number of validators` (not in sweetwater) | Not in [network parameters](../protocol/0054-NETP-network_parameters.md) | | +| `validator min balance` | Not in [network parameters](../protocol/0054-NETP-network_parameters.md) | 3000 VEGA | | `governance.proposal.updateNetParam.requiredMajority` | So that what is set in genesis cannot be changed too easily | 0.5 | | `governance.proposal.updateNetParam.requiredParticipation` | So that what is set in genesis cannot be changed too easily | 0.5 | | `validators.epoch.length` | Rewards currently make an assumption on epoch length, best fix it. | 1 day | diff --git a/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md b/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md index b1a0e973f..6f47894f5 100644 --- a/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md +++ b/non-protocol-specs/0012-NP-LIPE-liquidation-price-estimate.md @@ -8,7 +8,7 @@ Provide an estimate of the price range at which the liquidation of a specified p Provide an estimated liquidation price range, where the lower bound assumes no slippage in the [margin level calculation](../protocol/0019-MCAL-margin_calculator.md) and the upper bound assumes that the slippage cap is applied. -This amounts to carrying out the same computation twice, once with both linear and quadratic slippage factor set to `0` and once with the actual values used by the market for which the specified position is being considered. +This amounts to carrying out the same computation twice, once with slippage factor set to `0` and once with the actual value used by the market for which the specified position is being considered. The system carries out [position resolution](../protocol/0012-POSR-position_resolution.md) when the available collateral (amount in margin account for the market along with amount in the general account denominated in the same asset) is less than the maintenance margin level for the position. The first step is to cancel any open orders that a distressed party may have. After that the margin requirement is re-evaluated to see if the position is still distressed. Therefore we provide three sets of estimates of a liquidation price range: current open volume only, current open volume with active buy orders, current open volume with active sell orders. @@ -27,13 +27,13 @@ where $V$ is the open volume (negative for a short position) and $S^\text{curren We assume margin is calculated as per continuous trading formula (as there are no closeouts in auctions) and that the slippage cap always applies, therefore we get: $$ -\text{collateral available} + V(S^{\text{liquidation}}-S^\text{current}) = S^{\text{liquidation}} (V \cdot \text{linear slippage factor}+V^2 \cdot \text{quadratic slippage factor}+V \cdot \text{risk factor}) + V \cdot \text{constant}, +\text{collateral available} + V(S^{\text{liquidation}}-S^\text{current}) = S^{\text{liquidation}} (\abs{V} \cdot \text{linear slippage factor}+\abs{V} \cdot \text{risk factor}) + V \cdot \text{constant}, $$ where $\text{risk factor}$ is the long risk factor when $V>0$ and the short risk factor otherwise. The $\text{constant}$ is an optional arbitrary constant scaling with open volume added to the maintenance margin, e.g. the funding payment portion of the margin for [perpetual futures](../protocol/0053-PERP-product_builtin_perpetual_future.md#5-margin-considerations). Solving for $S^{\text{liquidation}}$ we get: $$ -S^{\text{liquidation}} = \frac{\text{collateral available}-V \cdot S^\text{current} - V \cdot \text{constant}}{V \cdot \text{linear slippage factor}+V^2 \cdot \text{quadratic slippage factor}+V \cdot \text{risk factor}-V} +S^{\text{liquidation}} = \frac{\text{collateral available}-V \cdot S^\text{current} - V \cdot \text{constant}}{\abs{V} \cdot \text{linear slippage factor}+\abs{V} \cdot \text{risk factor}-V} $$ if the denominator in the above expression evaluates to $0$ the liquidation price is undefined and we return an error, otherwise we return the result floored at $0$ (as the negative price is not attainable for any of the currently supported products). @@ -46,15 +46,18 @@ When including orders we sort the orders in the order they will get filled in (d - For each limit order: - if the order price ($S^{\text{order}}$) is above (buy side) / below (sell side) the liquidation price ($S^{\text{liquidation}}$): - recalculate $V$ to include the order's remaining volume (assumes order gets filled as soon as its price level is filled), - - update $\text{collateral available}$ to include the MTM gains/losses: $V(S^{\text{order}}-S^{\text{current}})$, + - update $\text{collateral available}$ to include the MTM gains/losses: $V(S^{\text{order}}-S^{\text{current}})$, - update $S^{\text{current}}$ to equal $S^{\text{order}}$, - otherwise return last calculated $S^{\text{liquidation}}$ (assumes other orders will get cancelled and the remaining position will be liquidated). ### Acceptance criteria 1. An estimate is obtained for a long position with no open orders, mark price keeps going down in small increments and the actual liquidation takes place within the estimated range. (0012-NP-LIPE-001) -1. An estimate is obtained for a short position with no open orders, mark price keeps going up in small increments and the actual liquidation takes place within the estimated range. (0012-NP-LIPE-002) -1. An estimate is obtained for a position with no open volume and a single limit buy order, after the order fills the mark price keeps going down in small increments and the actual liquidation takes place within the obtained estimated range. (0012-NP-LIPE-003) -1. An estimate is obtained for a long position with multiple limit sell order with the absolute value of the total remaining size of the orders less than the open volume. The estimated liquidation price with sell orders is lower than that for the open volume only. As the limit orders get filled the estimated liquidation price for the (updated) open volume converges to the estimate originally obtained with open sell orders. (0012-NP-LIPE-004) -1. An estimate is obtained for a short position with multiple limit sell order with the absolute value of the total remaining size of the orders less than the open volume. The estimated liquidation price with sell orders is lower than that for the open volume only. As the limit orders get filled the estimated liquidation price for the (updated) open volume converges to the estimate originally obtained with open sell orders. As the price keeps moving in small increments the liquidation happens within the originally estimated range (with sell orders) (0012-NP-LIPE-005) -1. There's no difference in the estimate for an open volume and that with `0` open volume and market order of the same size. (0012-NP-LIPE-006) +1. An estimate is obtained for a short position with no open orders, mark price keeps going up in small increments and the actual liquidation takes place within the estimated range. (0012-NP-LIPE-002) +1. An estimate is obtained for a position with no open volume and a single limit buy order, after the order fills the mark price keeps going down in small increments and the actual liquidation takes place within the obtained estimated range. (0012-NP-LIPE-003) +1. An estimate for cross-margin mode with `include_collateral_increase_in_available_collateral` set to `true` is obtained for a long position with multiple limit sell orders with the absolute value of the total remaining size of the orders less than the open volume. The general account balance should be set to `0` in the query and the margin account balance should be set to the maintenance margin for the chosen position. Orders should be chosen so that the liquidation price estimate with sell orders is non-zero (otherwise the party is collateralised up to a point that it will never get liquidated if these orders fill). The estimated liquidation price with sell orders is lower than that for the open volume only. As the limit orders get filled the estimated liquidation price for the (updated) open volume converges to the estimate originally obtained with open sell orders. (0012-NP-LIPE-004) +1. An estimate for cross-margin mode with `include_collateral_increase_in_available_collateral` set to `true` is obtained for a short position with multiple limit buy orders with the absolute value of the total remaining size of the orders less than the open volume. The general account balance should be set to `0` in the query and the margin account balance should be set to the maintenance margin for the chosen position. The estimated liquidation price with buy orders is higher than that for the open volume only. As the limit orders get filled the estimated liquidation price for the (updated) open volume converges to the estimate originally obtained with open buy orders. As the price keeps moving in small increments the liquidation happens within the originally estimated range (with buy orders) (0012-NP-LIPE-005) +1. There's no difference in the estimate for an open volume (use `open_volume_only` field) and that with `0` open volume and market order of the same size (use `including_buy_orders` or `including_sell_orders` depending on the order side). (0012-NP-LIPE-006) +1. When margining mode gets successfully changed to isolated margin mode and party has non-zero general account balance afterwards than its liquidation price estimate for all cases (position only, with buy orders, with sell orders) moves closer to the current mark price (compared to cross-margin figure). (0012-NP-LIPE-007) +1. An estimate for isolated margin mode with `include_collateral_increase_in_available_collateral` set to `true` is obtained for a short position with open sell orders. As the price keeps moving in small increments the liquidation happens within the originally estimated range (`open_volume_only` estimate). The sell orders and order margin account balance remain unchanged. (0012-NP-LIPE-008) +1. An estimate for isolated margin mode with `include_collateral_increase_in_available_collateral` set to `true` is obtained for a long position with open buy orders. As the price keeps moving in small increments the liquidation happens within the originally estimated range (`open_volume_only` estimate). The buy orders and order margin account balance remain unchanged. (0012-NP-LIPE-009) diff --git a/non-protocol-specs/0013-NP-POSE-position-estimate.md b/non-protocol-specs/0013-NP-POSE-position-estimate.md new file mode 100644 index 000000000..b2c83f0b7 --- /dev/null +++ b/non-protocol-specs/0013-NP-POSE-position-estimate.md @@ -0,0 +1,58 @@ +# Position estimate + +## Summary + +Protocol provides an API endpoint which can estimate the following aspects of a theoretical position (open volume and open orders): + +- margin levels, +- expected collateral increase required to support the position, +- liquidation price. + +Each estimate is a range between a relevant figure obtained with the assumption of no slippage and maximum slippage configured for the market. + +## Details + +The endpoint does not access information related to any of the existing positions, it's based entirely on the information specified in the request. +The endpoint only distinguishes between market orders (these are assumed to fill instantly and in full at a current mark price) and limit orders (these are assumed to fill in full once mark price reaches their limit price). + +### Margin level + +Margin level estimate contains the levels specified in [0019-MCAL-margin_calculator](./../protocol/0019-MCAL-margin_calculator.md#reference-level-explanation) spec as well margin mode and margin factor (0 in cross margin mode). + +### Collateral increase estimate + +Collateral increase estimate provides an approximate difference between the current collateral and the resulting collateral for the specified theoretical position. + +In isolated margin mode it's the difference between collateral required to support the specified position and orders with the margin factor provided and the balance of margin and order margin accounts specified in the request. + +In cross-margin mode: + +- if the collateral required for the specified position is higher than the combined margin and order margin account balances then it's the difference between the initial margin level for the specified position and the sum of those account balances. +- if the collateral required for the specified position is lower than the combined margin and order margin account balances then: + - if the combined account balances are above the margin release level for the specified position: it's the difference between the initial margin level for the specified position and the sum of those account balances, + - otherwise it's `0`. + +### Liquidation price estimate + +Liquidation price estimate as specified in [0012-NP-LIPE-liquidation-price-estimate](./0012-NP-LIPE-liquidation-price-estimate.md). + +Depending on the [margining mode](../protocol/0019-MCAL-margin_calculator.md#margining-modes) selected by the party for the market on which its position is being considered the $\text{collateral available}$ will differ. + +Cross-margin mode: $\text{collateral available} = \text{margin account balance} + \text{general account balance} + \text{order margin account balance}$. + +Isolated margin mode: $\text{collateral available} = \text{margin account balance}$. + +The position estimate request has an additional `include_collateral_increase_in_available_collateral` argument. It's relevant for the isolated margin mode: when set to `false` the collateral available used in liquidation price estimate will be the margin account balance only. When set to `true` the portion of the collateral increase estimated for the specified position only (not for the additional orders) will also be included in the available collateral. + +The endpoint request contains additional optional argument `scale_liquidation_price_to_market_decimals`. When set to `false` the liquidation price estimates are scaled to asset decimal places, when set to `true` these estimates are scaled to market decimal places. + +## Acceptance criteria + +1. In isolated margin mode the request with `0` open volume and one or more limit orders specified results in a non-zero order margin in the margin level estimate and margin mode correctly representing isolated margin mode. (0013-NP-POSE-001) +1. When account balances are set to `0` and market has slippage factors set to `0`, the collateral increase figure per specified theoretical position correctly approximates (absolute relative difference of less than $10^{-6}$) the actual margin and order margin account balances for a party which opens such a position. (0013-NP-POSE-002) +1. For a market with slippage cap factor set to `0`, when the response for a given request contains figure `x` as the collateral increase (best and worst case should be the same) then resubmitting the request with margin account balance increased by `x` should result in `0` collateral increase estimate. When increasing the margin account balance in the request further the collateral increase should get negative by the amount of the increase when in isolated margin mode. In cross margin mode it should remain at `0` until the combined margin and order balances are above the margin release level for the theoretical position. Then the collateral increase amount should be negative and equal to: `initial margin level for the specified position - margin account balance - order account balance`. (0013-NP-POSE-009) +1. In isolated margin mode increasing general account balance specified in the request has no impact on the collateral increase estimate and the liquidation price estimate. (0013-NP-POSE-004) +1. In isolated margin mode the liquidation price estimate for a position with non-zero additional margin requirement with `include_collateral_increase_in_available_collateral` argument set to `true` results in liquidation price which is closer to the current mark price than the result obtained with argument set to `false`. (0013-NP-POSE-005) +1. When market is set with different number of decimal places then its settlement asset then setting `scale_liquidation_price_to_market_decimals` to `false` results in liquidation price estimates scaled to asset decimal places, when set to `true` these estimates get scaled to market decimal places. (0013-NP-POSE-006) +1. The estimates for open volume of `1` and no orders are the same as those for open volume of `0` and a single buy market order of size `1`. (0013-NP-POSE-007) +1. The estimates for open volume of `-1` and no orders are the same as those for open volume of `0` and a single sell market order of size `1`. (0013-NP-POSE-008) diff --git a/protocol/0001-MKTF-market_framework.md b/protocol/0001-MKTF-market_framework.md index 1d0599a5f..1f4bc30d5 100644 --- a/protocol/0001-MKTF-market_framework.md +++ b/protocol/0001-MKTF-market_framework.md @@ -32,6 +32,7 @@ Data: - **Position Decimal Places**: number of decimal places for orders and positions, i.e. if this is 2 then the smallest increment that can be traded is 0.01, for example 0.01 BTC in a `BTSUSD` market. - If this is negative e.g. -3 this means that the smallest order and position is of size 1000. - Accepted values are `-6,...,-1,0,1,2,...,6`. +- **Liquidation strategy**: A field specifying the liquidation strategy for the market. Please refer to [0012-POSR-position_resolution](./0012-POSR-position_resolution.md#managing-networks-position) for supported strategies. Note: it is agreed that initially the integer representation of the full precision of both order and positions can be required to fit into an int64, so this means that the largest position/order size possible reduces by a factor of ten for every extra decimal place used. This also means that, for instance, it would not be possible to create a `BTCUSD` market that allows order/position sizes equivalent to 1 sat. diff --git a/protocol/0002-STTL-settlement.md b/protocol/0002-STTL-settlement.md index fc6458f63..5ee9b2962 100644 --- a/protocol/0002-STTL-settlement.md +++ b/protocol/0002-STTL-settlement.md @@ -68,7 +68,7 @@ The [market lifecycle spec](./0043-MKTL-market_lifecycle.md) provides detail on - With a market configured to take an oracle termination time and settlement price and put into continuous trading mode. When there are traders with open positions on the market and the termination trigger from oracle is sent so the market is terminated. Send market settlement price and assert that it is no longer possible to trade on this market. -### Example 1 - A typical path of a cash settled futures market nearing expiry when market is trading in continuous session (0002-STTL-002) +### Example 1 - A typical path of a cash settled futures market nearing expiry when market is trading in continuous session (0002-STTL-011) 1. Market has a status of ACTIVE and is trading in default trading mode 1. The product's [trading terminated trigger is hit](./0016-PFUT-product_builtin_future.md#41-termination-of-trading) @@ -79,12 +79,12 @@ The [market lifecycle spec](./0043-MKTL-market_lifecycle.md) provides detail on 1. Any remaining balances in parties' margin and LP bond accounts are moved to their general account. 1. The margin accounts and LP bond accounts for these markets are no longer required. 1. Positions can be left as open, or set to zero (this isn't important for the protocol but should be made clear on the API either way). -1. The market's insurance pool is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. +1. The market's [insurance pool balance is transferred](./0015-INSR-market_insurance_pool_collateral.md) into the global insurance pool using the same settlement asset. 1. Market status is now set to [SETTLED](./0043-MKTL-market_lifecycle.md). 1. Now the market can be deleted. 1. This mechanism does not incur fees to traders that have open positions that are settled at expiry. (0002-STTL-003) -### Example 2 - A less typical path of such a futures market nearing expiry when market is suspended (0002-STTL-004) +### Example 2 - A less typical path of such a futures market nearing expiry when market is suspended (0002-STTL-012) 1. Market has a status of SUSPENDED and in a protective auction 1. The product's [trading terminated trigger is hit](./0016-PFUT-product_builtin_future.md#41-termination-of-trading) @@ -95,7 +95,7 @@ The [market lifecycle spec](./0043-MKTL-market_lifecycle.md) provides detail on 1. Any remaining balances in parties' margin and LP bond accounts are moved to their general account. 1. The margin accounts and LP bond accounts for these markets are no longer required. 1. Positions can be left as open, or set to zero (this isn't important for the protocol but should be made clear on the API either way). -1. The market's insurance pool is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. +1. The [market's insurance pool](./0015-INSR-market_insurance_pool_collateral.md) balance is transferred into the global insurance pool using the same settlement asset. 1. Market status is now set to [SETTLED](./0043-MKTL-market_lifecycle.md). 1. Now the market can be deleted. 1. This mechanism does not incur fees to traders that have open positions that are settled at expiry. (0002-STTL-005) @@ -107,7 +107,7 @@ The [market lifecycle spec](./0043-MKTL-market_lifecycle.md) provides detail on 1. If margin and general account of trader are insufficient to cover collateral transfers, then collateral is attempted to be taken from market's insurance pool. (0002-STTL-008) 1. If the full required amount for collateral cannot be collected from individual or combination of these accounts, then as much as possible in the above sequence of accounts is collected and loss socialisation occurs. (0002-STTL-009) -### Example 3 - Settlement data to cash settled future is submitted before trading is terminated (0002-STTL-010) +### Example 3 - Settlement data to cash settled future is submitted before trading is terminated (0002-STTL-013) 1. A [cash settled futures](0016-PFUT-product_builtin_future.md) market has a status of ACTIVE and is trading in default trading mode (continuous trading) 1. An [oracle event occurs](./0045-DSRC-data_sourcing.md) that is eligible to settle the market, as defined on the [Product](./0001-MKTF-market_framework.md) (see also [cash settled futures spec](./0016-PFUT-product_builtin_future.md)). In other words the settlement price is submitted to the market before trading is terminated. @@ -124,6 +124,6 @@ All of that happens while processing the trading terminated transaction. 1. Any remaining balances in parties' margin and LP bond accounts are moved to their general account. 1. The margin accounts and LP bond accounts for these markets are no longer required. 1. Positions can be left as open, or set to zero (this isn't important for the protocol but should be made clear on the API either way). -1. The market's insurance pool is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. +1. The [market's insurance pool](./0015-INSR-market_insurance_pool_collateral.md) balance is transferred into the global insurance pool using the same settlement asset. 1. Market status is now set to [SETTLED](./0043-MKTL-market_lifecycle.md). 1. Now the market can be deleted. diff --git a/protocol/0003-MTMK-mark_to_market_settlement.md b/protocol/0003-MTMK-mark_to_market_settlement.md index 7b7355f2b..08ce35ec5 100644 --- a/protocol/0003-MTMK-mark_to_market_settlement.md +++ b/protocol/0003-MTMK-mark_to_market_settlement.md @@ -22,6 +22,10 @@ 1. If the mark price hasn't changed: 1. A trader with no change in open position size has no transfers in or out of their margin account (0003-MTMK-012) 1. An aggressive order to buy 2 units at 1010 which matches with two passive orders each of size one resting on the book with prices of 1000 and 1010 results in a transfer of 10 flowing from the party with order priced at 1000 to the aggressive party during the next MTM settlement 0003-MTMK-013) +1. If the network party has a non-zero position and the mark-price moves in favour of the network's position gains from mark-to-market settlement should be paid into the market insurance pool. (0003-MTMK-015) +1. If the network party has a non-zero position and the mark-price moves against the network's position, losses from mark-to-market settlement should be paid from the market insurance pool. (0003-MTMK-016) +1. If the network does not have enough funds in the market insurance pool to cover its mark-to-market losses, loss-socialisation occurs. (0003-MTMK-017) + ## Market with position decimal places > 0 scenario diff --git a/protocol/0004-AMND-amends.md b/protocol/0004-AMND-amends.md index 6259abe41..f383e0b50 100644 --- a/protocol/0004-AMND-amends.md +++ b/protocol/0004-AMND-amends.md @@ -4,9 +4,13 @@ - Only LIMIT or PEGGED orders can be amended. Any attempt to amend a MARKET order is rejected (0004-AMND-001). - Price change amends remove the order from the book and insert the order at the back of the queue at the new price level (0004-AMND-002). -- Reducing the quantity leaves the order in its current spot but reduces the remaining amount accordingly (0004-AMND-003). -- Quantity after amendment must be a multiple of the smallest increment possible given the `Position Decimal Places` (PDP) specified in the [Market Framework](./0001-MKTF-market_framework.md), i.e. is PDP = 2 then quantity must be a whole multiple of 0.01. (0004-AMND-004). -- Increasing the quantity causes the order to be removed from the book and inserted at the back of the price level queue with the updated quantity (0004-AMND-005). +- Reducing the quantity by specifying a `sizeDelta` leaves the order in its current spot but reduces the remaining amount accordingly (0004-AMND-003). +- Quantity after amendment using a `sizeDelta` must be a multiple of the smallest increment possible given the `Position Decimal Places` (PDP) specified in the [Market Framework](./0001-MKTF-market_framework.md), i.e. is PDP = 2 then quantity must be a whole multiple of 0.01. (0004-AMND-004). +- Increasing the quantity by specifying a `sizeDelta` causes the order to be removed from the book and inserted at the back of the price level queue with the updated quantity (0004-AMND-005). +- Size change amends specifying a `size` greater than the current size remove and reinsert the order at the back of the price level and increase the remaining amount accordingly (0004-AMND-056). +- Size change amends specifying a `size` lower than the current size leave the order in its current spot and reduce the remaining amount of the order accordingly (0004-AMND-057). +- Size change amends which would result in the remaining part of the order being reduced below zero should instead cancel the order (0004-AMND-058). +- A transaction specifying both a `sizeDelta` and `size` field should be rejected as invalid (0004-AMND-059). - Changing the `TIF` can only occur between `GTC` and `GTT`. Any attempt to amend to another `TIF` flag is rejected. A `GTT` must have an `expiresAt` value but a `GTC` must not have one. (0004-AMND-006). - Any attempt to amend to or from the `TIF` values `GFA` and `GFN` will result in a rejected amend. (0004-AMND-007). - All updates to an existing order update the `UpdatedAt` time stamp field in the order (0004-AMND-008). @@ -40,7 +44,7 @@ The amend order can alter the quantity, price and expiry time/`TIF` type. For pe ## Guide-level explanation -The amend order message is a custom message containing the `orderID` of the original order and optional fields that can be altered. Prices can be changed with a new absolute value, quantity can be reduced or increased from their current remaining size. Expiry time can be set to a new value and the `TIF` type can be toggled between `GTC` and `GTT`. Changing the `TIF` field will impact the value in the `ExpiryTime` field as it will either be blanked or set to a new valid value. +The amend order message is a custom message containing the `orderID` of the original order and optional fields that can be altered. Prices can be changed with a new absolute value, sizes can be changed by either specifying a size delta or a new absolute value. If a user attempts to amend the size of an order by specifying both a size delta and anew absolute value the order should be rejected. Expiry time can be set to a new value and the `TIF` type can be toggled between `GTC` and `GTT`. Changing the `TIF` field will impact the value in the `ExpiryTime` field as it will either be blanked or set to a new valid value. Some examples: A LIMIT order sitting on the bid side of the order book: @@ -56,6 +60,15 @@ amendOrder{ } ``` +or + +```json +amendOrder{ + orderID: "V0000000001-0000000001" + size: 300 +} +``` + This will be the resulting order book: `Bids: 300@1000 GTC (OrderID V0000000001-0000000001)` @@ -84,7 +97,9 @@ The fields which can be altered are: - `Price` - Amending the price causes the order to be removed from the book and re-inserted at the new price level. This can result in the order being filled if the price is moved to a level that would cross. - `SizeDelta` - - A size change is specified as a delta to the current amount. This will be applied to both the `Size` and `Remaining` part of the order. In the case that the remaining amount it reduced to zero or less, the order is cancelled. This must be a multiple of the smallest value allowed by the `Position Decimal Places` (PDP) specified in the [Market Framework](./0001-MKTF-market_framework.md), i.e. is PDP = 2 then `SizeDelta` must be a whole multiple of 0.01. (NB: `SizeDelta` may use an int64 where the int value 1 is the smallest multiple allowable given the configured dp). In case PDP is negative this again applies e.g. if PDP = -1 then `SizeDelta` must be a whole multiple of 10. + - Amending the size by specifying a `sizeDelta` will be applied to both the `Size` and `Remaining` part of the order. In the case that the remaining amount it reduced to zero or less, the order is cancelled. This must be a multiple of the smallest value allowed by the `Position Decimal Places` (PDP) specified in the [Market Framework](./0001-MKTF-market_framework.md), i.e. is PDP = 2 then `SizeDelta` must be a whole multiple of 0.01. (NB: `SizeDelta` may use an int64 where the int value 1 is the smallest multiple allowable given the configured dp). In case PDP is negative this again applies e.g. if PDP = -1 then `SizeDelta` must be a whole multiple of 10. +- `Size` + - Amending the size by specifying a new `size` causes the the `Size` and `Remaining` part of the order to be amended by the difference between the original and amended size. In the case that the remaining amount it reduced to zero or less, the order is cancelled. This must be a multiple of the smallest value allowed by the `Position Decimal Places` (PDP) specified in the [Market Framework](./0001-MKTF-market_framework.md), i.e. is PDP = 2 then `Size` must be a whole multiple of 0.01. (NB: `Size` may use an int64 where the int value 1 is the smallest multiple allowable given the configured dp). In case PDP is negative this again applies e.g. if PDP = -1 then `Size` must be a whole multiple of 10. - `TimeInForce` - The `TIF` enumeration can only be toggled between `GTT` and `GTC`. Amending to `GTT` requires an `expiryTime` value to be set. Amending to `GTC` removes the `expiryTime` value. - `ExpiryTime` @@ -108,10 +123,11 @@ message amendOrder { enum TIF 4; int64 expiryTime 5; PeggedOrder *peggedOrder 6; + int64 size 7; } ``` -An example of using a negative size is shown below: +An example of reducing the size is shown below: `Bids: 100@1000 GTC (OrderID V0000000001-0000000001)` @@ -122,6 +138,13 @@ amendOrder{ orderID:"V0000000001-0000000001", sizeDelta: -50 } ``` +or + +```json +amendOrder{ orderID:"V0000000001-0000000001", + size: 50 } +``` + The resulting order book will be: `Bids: 50@1000 GTC (OrderID V0000000001-0000000001)` diff --git a/protocol/0009-MRKP-mark_price.md b/protocol/0009-MRKP-mark_price.md index 353389907..ac34a06b8 100644 --- a/protocol/0009-MRKP-mark_price.md +++ b/protocol/0009-MRKP-mark_price.md @@ -6,70 +6,153 @@ A *Mark Price* is a concept derived from traditional markets. It is a calculate Introduce a network parameter `network.markPriceUpdateMaximumFrequency` with minimum allowable value of `0s` maximum allowable value of `1h` and a default of `5s`. +The *Mark Price* represents the "current" market value for an instrument that is being traded on a market on Vega. It is a calculated value primarily used to value trader's open portfolios against the prices they executed their trades at. Specifically, it is used to calculate the cash flows for [mark-to-market settlement](./0003-MTMK-mark_to_market_settlement.md). + +The mark price is instantiated when a market opens via the [opening auction](./0026-AUCT-auctions.md). + +It will subsequently be calculated according to a methodology selected from a suite of algorithms. The selection of the algorithm for calculating the *Mark Price* is specified at the "market" level as a market parameter. + +## Algorithms for calculating the *Mark Price* + +### 1. Last Traded Price of a Sequence with same time stamp with maximum frequency set by `network.markPriceUpdateMaximumFrequency` + +The mark price for the instrument is set to the last trade price in the market following processing of each transaction (i.e. submit/amend/delete order) from a sequence of transactions with the same time stamp, provided that at least `network.markPriceUpdateMaximumFrequency` has elapsed since the last mark price update. + +>*Example:* Assume `network.markPriceUpdateMaximumFrequency = 10s`. + +Consider the situation where the mark price was last updated to $900 and this was 12s ago. There is a market sell order for -20 and a market buy order for +100 with the same time stamp. The sell order results in two trades: 15 @ 920 and 5 @ 910. The buy order results in 3 trades; 50 @ $1000, 25 @ $1100 and 25 @ $1200, the mark price changes **once** to a new value of $1200. + +Now 8s has elapsed since the last update and there is a market sell order for volume 3 which executes against book volume as 1 @ 1190 and 2 @ 1100. +The mark price isn't updated because `network.markPriceUpdateMaximumFrequency = 10s` has not elapsed yet. + +Now 10.1s has elapsed since the last update and there is a market buy order for volume 5 which executes against book volume as 1 @ 1220, 2 @ 1250 and 2 @ 1500. The mark price is updated to 1500. + +### 2. Flexible mark price methodology + +The calculations are specified in [markprice methodology research note](https://github.com/vegaprotocol/research/blob/markprice-updates/papers/markprice-methodology/markprice-methodology.tex). +Here, we only write the acceptance criteria. +Note that for calculating the median with an even number of entries we sort, pick out the two values that are in the middle of the list and average those. So in particular with two values a median is the same as the average for our purposes. + ## Acceptance criteria - The mark price must be set when the market leaves opening auction. (0009-MRKP-002) -- Each time the mark price changes the market data event containing the new mark price should be emitted. Specifically, the mark price set after leaving each auction, every interim mark price as well as the mark price based on last trade used at market termination and the one based on oracle data used for final settlement should all be observable from market data events. (0009-MRKP-009) +- Each time the mark price changes the market data event containing the new mark price should be emitted.Specifically, the mark price set after leaving each auction, every interim mark price as well as the mark price based on last trade used at market termination and the one based on oracle data used for final settlement should all be observable from market data events. (0009-MRKP-009) +- If a market mark price is configured in such a way that the mark price methodology hasn't provided a price at the point of uncrossing the opening auction, then the auction uncrossing price is set as the first mark price, regardless of what the mark price methodology says. (0009-MRKP-001) and a perps market (0009-MRKP-003) -Algorithm 1: +### Algorithm 1 (last trade price, excluding network trades) -- If `network.markPriceUpdateMaximumFrequency=0s` then any transaction that results in one or more trades causes the mark price to change to the value of the last trade and only the last trade. (0009-MRKP-003) - If `network.markPriceUpdateMaximumFrequency>0` then out of a sequence of transactions with the same time-stamp the last transaction that results in one or more trades causes the mark price to change to the value of the last trade and only the last trade but only provided that at least `network.markPriceUpdateMaximumFrequency` has elapsed since the last update. (0009-MRKP-007) - A transaction that doesn't result in a trade does not cause the mark price to change. (0009-MRKP-004) - A transaction out of a sequence of transactions with the same time stamp which isn't the last trade-causing transaction will *not* result in a mark price change. (0009-MRKP-008) - The mark price must be using market decimal place setting. (0009-MRKP-006) +- It is possible to configure a cash settled futures market to use Algorithm 1 (ie last trade price) (0009-MRKP-010) and a perps market (0009-MRKP-011). -## Guide-level explanation +### Flexible mark price methodology, no combinations yet -The *Mark Price* represents the "current" market value for an instrument that is being traded on a market on Vega. It is a calculated value primarily used to value trader's open portfolios against the prices they executed their trades at. Specifically, it is used to calculate the cash flows for [mark-to-market settlement](./0003-MTMK-mark_to_market_settlement.md). +#### Trade-size-weighted average price -Note that a mark price may not be a true "price" in the financial sense of the word, i.e. if the `price` changes by `X` then the value of a position does not necessarily change by `X * position size`, instead, the product's quote-to-value function must be used to ascertain the change, i.e. `change_in_value == product.value(new_mark_price) - product.value(old_mark_price)`. +- It is possible to configure a cash settled futures market to use weighted average of trades over `network.markPriceUpdateMaximumFrequency` with decay weight `1` and power `1` (linear decay) (0009-MRKP-012) and a perps market (0009-MRKP-013). +- when choosing price type `weight` and set weight on `trade-size-weight price` only, set decay weight to `0` and decay power to `1`, and set up trade at price `15920`, `15940`, `15960`, `15990` at various size, and check if the weighted average mark price is corrected calculated (0009-MRKP-110) and on perp market (0009-MRKP-111) +-when choosing price type `weight` and set weight on `trade-size-weight price` only, set decay weight to `0` and decay power to `1`, and set up trade at price `15940`, `15960`, `15990` at various size, and check if the weighted average mark price is corrected calculated (0009-MRKP-112) and on perp market (0009-MRKP-113) +- when choosing price type `weight` and set weight on `trade-size-weight price` only, set decay weight to `1` and decay power to `1`, and set up trade at price `15920`, `15940`, `15960`, `15990` at various size, and check if the weighted average mark price is corrected calculated (0009-MRKP-114) and on perp market (0009-MRKP-115) +- when choosing price type `weight` and set weight on `trade-size-weight price` only, set decay weight to `1` and decay power to `0`, and set up trade at price `15920`, `15940`, `15960`, `15990` at various size, and check if the weighted average mark price is corrected calculated (0009-MRKP-116) and on perp market (0009-MRKP-117) +- when choosing price type `weight` and set weight on `trade-size-weight price` only, set decay weight to `0.5` and decay power to `1`, and set up trade at price `15920`, `15940`, `15960`, `15990` at various size, and check if the weighted average mark price is corrected calculated (0009-MRKP-118) and on perp market (0009-MRKP-119) -## Reference-level explanation +#### Leverage-notional book price -The mark price is instantiated when a market opens via the [opening auction](./0026-AUCT-auctions.md). +- It is possible to configure a cash settled futures market to use impact of leveraged notional on the order book with the value of USDT `100` for mark price (0009-MRKP-014) and a perps market (0009-MRKP-015). +- when choosing price type `weight` and set weight on `leverage-notional book price` only, set cash amount to `100` (which should make the notional volume at sell and buy round to `0`), and place a few orders on the book with best bid `15900` and best ask `16000` and the leverage-notional book price should be the mid-price (0009-MRKP-120) and on perp market (0009-MRKP-121) +- when choosing price type `weight` and set weight on `leverage-notional book price` only, set cash amount to `100,000` and place a few orders on the book with best bid `15900` and best ask `16000` and check if leverage-notional book price is corrected calculated (0009-MRKP-122) and on perp market (0009-MRKP-123) +- when choosing price type `weight` and set weight on `leverage-notional book price` only, set cash amount to `5,000,000` (which should make the notional volume too big for the book) and place a few orders on the book with best bid `15900` and best ask `16000` and the leverage-notional book price should be the mid-price (0009-MRKP-124) and on perp market (0009-MRKP-125) +- when choosing price type `weight` and set weight on `leverage-notional book price` only, set cash amount to `100,000` and place a few orders on the book with best bid `15900` and best ask `16000` and check leverage-notional book price, then change the order book by placing a trade, and check if the leverage-notional book price is updated when the time indicated by the mark price frequency is crossed(0009-MRKP-126) and on perp market (0009-MRKP-127) -It will subsequently be calculated according to a methodology selected from a suite of algorithms. The selection of the algorithm for calculating the *Mark Price* is specified at the "market" level as a market parameter. +#### Oracle source price -Mark price can also be adjusted by [product lifecycle events](./0051-PROD-product.md), in which case the mark price set by the product remains the mark price until it is next recalculated (e.g. if the methodology is last traded price, until there is a new trade). +- It is possible to configure a cash settled futures market to use an oracle source for the mark price (0009-MRKP-016) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-017). -## Usage of the *Mark Price* +- when choosing price type `weight` and set weight on `oracle source price` only, set up 2 oracle prices at different time, and check if the oracle mark price is correctly calculated (0009-MRKP-130) -The most recently calculated *Mark Price* is used in the [mark-to-market settlement](./0003-MTMK-mark_to_market_settlement.md). A change in the *Mark Price* is one of the triggers for the mark-to-market settlement to run. +- when choosing price type `weight` and set weight on `oracle source price` only, set up oracle prices at different time, and check if the oracle mark price updated according to is correctly calculated according to `network.markPriceUpdateMaximumFrequency` (0009-MRKP-131) -## Algorithms for calculating the *Mark Price* +- when choosing price type `weight` and set weight on `oracle source price` only, set up 3 oracle prices at different time with 1 of them becomes stale, and check if the oracle mark price updated according to is correctly calculated by using the right price (0009-MRKP-132) -### 1. Last Traded Price of a Sequence with same time stamp with maximum frequency set by `network.markPriceUpdateMaximumFrequency` +- It is possible to obtain a time series for the market mark price from the data node from the time of the market proposal enactment onwards (subject to data node retention policies).(0009-MRKP-133) + + +### Flexible mark price methodology, combinations + +- It is possible to configure a cash settled futures market to use a weighted average of 1. weighted average of trades over `network.markPriceUpdateMaximumFrequency` and 2. impact of leveraged notional on the order book with the value of USDT `100` and 3. an oracle source and if last trade is last updated more than 1 minute ago then it is removed and the remaining re-weighted and if the oracle is last updated more than 5 minutes ago then it is removed and the remaining re-weighted (0009-MRKP-018) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-019). + +- It is possible to configure a cash settled futures market to use a weighted average of 1. weighted average of trades over `network.markPriceUpdateMaximumFrequency` and 2. impact of leveraged notional on the order book with the value of USDT `100` and if last trade is last updated more than 1 minute ago then it is removed and the remaining re-weighted and if the oracle is last updated more than 5 minutes ago then it is removed and the remaining re-weighted and if both sources are stale than the mark price stops updating (0009-MRKP-020) and a perps market (0009-MRKP-021). + +- It is possible to configure a cash settled futures market to use a median of 1. weighted average of trades over `network.markPriceUpdateMaximumFrequency` and 2. impact of leveraged notional on the order book with the value of USDT `100` and 3. an oracle source and if last trade is last updated more than 1 minute ago then it is removed and if the oracle is last updated more than 5 minutes ago then it is removed (0009-MRKP-022) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-023). + +- When market is leaving auction (including opening auction and monitoring auction), mark price should be recalculated (0009-MRKP-024) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-025). + +- When market is at monitoring auction, book price should be indicative uncrossing price, mark price should only be recalculated when the auction exits, starting from only the last period indicated by `network.markPriceUpdateMaximumFrequency` (0009-MRKP-026) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-027). + +- It is possible to configure a cash settled futures market to use a weighted average of 1. weighted average of trades over `network.markPriceUpdateMaximumFrequency` and 2. impact of leveraged notional on the order book with the value of USDT `100` and when the book does not have enough volume, then the book price should not be included (0009-MRKP-028) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-029). + +- It is possible to configure a cash settled futures market to use a weighted average of 1. weighted average of trades over `network.markPriceUpdateMaximumFrequency` and 2. impact of leveraged notional on the order book with the value of USDT `0` and the book price should be mid price (0009-MRKP-030) and a perps market (with the oracle source different to that used for the external price in the perps market) (0009-MRKP-031). +- Set price type to "median", only have data source available from "Trade-size-weighted average price" and "Leverage-notional book price" and 1 trade at 15920, check the mark price is correctly calculated (0009-MRKP-032) and a perps market (0009-MRKP-033) + +- Set price type to "median", only have data source available from "Trade-size-weighted average price" and "Leverage-notional book price" and 1 trade at 15920, and 1 trade at 15940, move time, and check stale price should not be included (0009-MRKP-034) and a perps market (0009-MRKP-035) + +- A market can be configured with `markPriceConfiguration: price type` is `Weighted` without oracles (0009-MRKP-060) + +- A market can be configured with `markPriceConfiguration: price type` is `Median` without oracles (0009-MRKP-061) + +### Validation + +- Boundary values are respected for the market parameters + + - `markPriceConfiguration: decayWeight` valid values: `[0,1]`(0009-MRKP-050) + + - `markPriceConfiguration: decayPower` valid values: `{1,2,3}`(0009-MRKP-051) + + - `markPriceConfiguration: cashAmount` valid values: `>=0`(0009-MRKP-052) - The mark price for the instrument is set to the last trade price in the market following processing of each transaction (i.e. submit/amend/delete order) from a sequence of transactions with the same time stamp, provided that at least `network.markPriceUpdateMaximumFrequency` has elapsed since the last mark price update. + - `markPriceConfiguration: source weight` valid values: `>=0`(0009-MRKP-053) - >*Example:* Assume `network.markPriceUpdateMaximumFrequency = 10s`. + - `markPriceConfiguration: source staleness tolerance` valid values: `valid duration string, e.g. "5s", "24h"`(0009-MRKP-054) - Consider the situation where the mark price was last updated to $900 and this was 12s ago. There is a market sell order for -20 and a market buy order for +100 with the same time stamp. The sell order results in two trades: 15 @ 920 and 5 @ 910. The buy order results in 3 trades; 50 @ $1000, 25 @ $1100 and 25 @ $1200, the mark price changes **once** to a new value of $1200. + - `markPriceConfiguration: source weight` and `markPriceConfiguration: source staleness tolerance` should have the same length(0009-MRKP-055) - Now 8s has elapsed since the last update and there is a market sell order for volume 3 which executes against book volume as 1 @ 1190 and 2 @ 1100. - The mark price isn't updated because `network.markPriceUpdateMaximumFrequency = 10s` has not elapsed yet. + - Mark price configuration `source_weight` length should be 3 plus number of oracle data sources if the price type is weighted (0009-MRKP-056). - Now 10.1s has elapsed since the last update and there is a market buy order for volume 5 which executes against book volume as 1 @ 1220, 2 @ 1250 and 2 @ 1500. The mark price is updated to 1500. + - If the mark price type is not weighted the source weight must be empty (0009-MRKP-062). -### 2. Last Traded Price + Order Book + - Mark price configuration `source staleness tolerance` length must be 3 plus number of oracle data sources if price type is weighted or median (0009-MRKP-063). -The mark price is set to the higher / lower of the last traded price, bid/offer. + - If the mark price type is weighted, there must be at least one non zero weight (0009-MRKP-064). ->*Example a):* consider the last traded price was $1000 and the current best bid in the market is $1,100. The bid price is higher than the last traded price so the new Mark Price is $1,100. ->*Example b):* consider the last traded price was $1000 and the current best bid in the market is $999. The last traded price is higher than the bid price so the new Mark Price is $1,000. + - When `markPriceConfiguration: price type` is **not** `Last Trade Price`, the `markPriceConfiguration: source staleness tolerance`, `markPriceConfiguration: source weight`, `markPriceConfiguration: decayPower` and `markPriceConfiguration: cashAmount` must be provided + - new market (0009-MRKP-057) + - update market (0009-MRKP-058) -### 3. Oracle + - When `markPriceConfiguration: source weight` is provided then it must not be all `0` (0009-MRKP-059) - An oracle source external to the market provides updates to the Mark Price. See the [data sourcing spec](./0045-DSRC-data_sourcing.md). + - Launch a cash settled futures market which uses the "Last Traded Price" mark price method. Submit a market update proposal to change this to a composite price with some chosen configuration. Observe that the new methodology is applied correctly from enactment onwards. (0009-MRKP-134). -### 4. Model +- Launch a perpetual futures market which uses the "Last Traded Price" mark price method. Submit a market update proposal to change this to a composite price with some chosen configuration. Observe that the new methodology is applied correctly from enactment onwards. (0009-MRKP-135). - The *Mark Price* may be calculated using a built in model. - >*Example:* An option price will theoretically decay with time even if the market's order book or trade history does not reflect this. +### Example 1 - A typical path of a cash settled futures market from end of opening auction till expiry (use Algorithm 2 (ie median price))(0009-MRKP-040) -### 5. Defined as part of the product +1. Market is in opening auction, no mark price. +2. Order uncrossed, ends of opening auction, market is in active state. New event is emitted for new mark price. +3. New trade triggers new traded price, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +4. New Oracle price comes, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +5. Another Oracle price comes, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +6. Traded price at step 2 is stale, and Oracle price at step 4 is stale, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +7. market's status is set to trading terminated, An [oracle event occurs] that is eligible to settle the market, new event is emitted for new mark price. - The *Mark Price* may be calculated using an algorithm defined by the product -- and 'selected' by a market parameter. +### Example 2 - A typical path of a cash settled perps market from end of opening auction (use Algorithm 2 (ie median price))(0009-MRKP-041) +1. Market is in opening auction, no mark price. +2. Order uncrossed, ends of opening auction, market is in active state. New event is emitted for new mark price. +3. New trade triggers new traded price, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +4. New Oracle price comes, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +5. Another Oracle price comes, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +6. Oracle price comes for funding payments, mark price recalculated when the time indicated by the mark price frequency is crossed, new event is emitted for new mark price. +7. Traded price at step 2 is stale, and Oracle price at step 4 is stale, mark price recalculated, new event is emitted for new mark price. diff --git a/protocol/0010-MARG-margin_orchestration.md b/protocol/0010-MARG-margin_orchestration.md index e2129a8eb..fad70db62 100644 --- a/protocol/0010-MARG-margin_orchestration.md +++ b/protocol/0010-MARG-margin_orchestration.md @@ -34,9 +34,10 @@ This specification outlines: ### **Background - how margin levels are calculated** -The [margin calculator](./0019-MCAL-margin_calculator.md) will calculate the margin levels when instructed to do so. It will return four margin levels for each trader: +The [margin calculator](./0019-MCAL-margin_calculator.md) will calculate the margin levels when instructed to do so. It will return five margin levels for each trader: 1. maintenance margin +1. order margin 1. collateral search level 1. initial margin 1. collateral release level @@ -71,11 +72,11 @@ The [margin calculator](./0019-MCAL-margin_calculator.md) returns four margin le The protocol compares these levels to the balance in the trader's margin account for a market. -| Traders Collateral | Protocol Action | Whitepaper Description +| Traders Collateral | Protocol Action | Whitepaper Description | | ------------- |:-------------:| -----:| -| less than _collateral search level_ | Collateral search, possibly close outs | Collateral search, Close out zone -| greater than _collateral release level_ | Collateral release | Collateral release -| greater than _collateral search level_ and less than _initial margin_ | no action | No financial risk to network +| less than _collateral search level_ | Collateral search, possibly close outs | Collateral search, Close out zone | +| greater than _collateral release level_ | Collateral release | Collateral release | +| greater than _collateral search level_ and less than _initial margin_ | no action | No financial risk to network | When posting a new order the initial margin for the overall position including the new order is calculated and order is only allowed to go ahead if party has enough funds to bring their margin account balance to that figure. An exception to this is when a party has an open position and tries to reduce it, but cannot afford the new margin. Then orders from the opposite side (short orders for a long position, long orders for a short position) are accepted as follows: diff --git a/protocol/0011-MARA-check_order_allocate_margin.md b/protocol/0011-MARA-check_order_allocate_margin.md index a4cb68573..722afe1f6 100644 --- a/protocol/0011-MARA-check_order_allocate_margin.md +++ b/protocol/0011-MARA-check_order_allocate_margin.md @@ -9,14 +9,10 @@ Orders should be rejected if we can’t allocate sufficient margin. ## Acceptance criteria 1. If an order is amended such that margin requirement is increased and user has sufficient balance in the general account to top up their margin account then the amendment is executed successfully. (0011-MARA-001) -1. In Spot market, if an order is amended such that holding requirement is increased and user has sufficient balance in the general account to top up their holding account then the amendment is executed successfully. (0011-MARA-018) 1. If an order is amended such that margin requirement is increased and user doesn't have sufficient balance in the general account to top up their margin account then their amend is not executed but the unamended order stays on the book. (0011-MARA-002) -1. In Spot market, if an order is amended such that holding requirement is increased and user doesn't have sufficient balance in the general account to top up their holding account then their amend is not executed but the unamended order stays on the book. (0011-MARA-019) 1. Cancelling an order releases the margin amount back to user's general account, provided the user has no other orders or positions (0011-MARA-003) -In Spot market, cancelling an order releases the holding amount back to user's general account. (0011-MARA-020) 1. If an order is amended such that margin requirement is decreased then the amendment is executed successfully. (0011-MARA-004) 1. If an order is partially filled then the margin requirements are recalculated reflecting the reduced order size and new position size. (0011-MARA-005) -In Spot market, if an order is partially filled then the holding requirements are recalculated reflecting the reduced order size. (0011-MARA-021) 1. If an order is partially filled and if this leads to a reduced position and reduced riskiest long / short then the margin requirements are seen to be reduced and if margin balance is above release level then the excess amount is transferred to the general account. (0011-MARA-006) 1. Margin is correctly calculated for [all order types](./0014-ORDT-order_types.md) in continuous trading: 1. Limit GTT (0011-MARA-007) @@ -31,19 +27,6 @@ In Spot market, if an order is partially filled then the holding requirements ar 1.Limit GFA (0011-MARA-015) 1.Pegged GTT (parked in auction \*) (0011-MARA-016) 1.Pegged GTC (parked in auction \* ) (0011-MARA-017) -1. In Spot market, holding in holding account is correctly calculated for [all order types](./0014-ORDT-order_types.md) in continuous trading: - 1. Limit GTT (0011-MARA-022) - 1. Limit GTC (0011-MARA-023) - 1. Limit GFN (0011-MARA-024) - 1. Pegged GTT (0011-MARA-025) - 1. Pegged GTC (0011-MARA-026) - 1. Pegged GFN (0011-MARA-027) -1. In Spot market, holding in holding account is correctly calculated for [all order types](./0014-ORDT-order_types.md) in auction mode: - 1.Limit GTT (0011-MARA-028) - 1.Limit GTC (0011-MARA-029) - 1.Limit GFA (0011-MARA-030) - 1.Pegged GTT (parked in auction \*) (0011-MARA-031) - 1.Pegged GTC (parked in auction \* ) (0011-MARA-032) ## Pseudocode diff --git a/protocol/0012-POSR-position_resolution.md b/protocol/0012-POSR-position_resolution.md index 6c69b452c..a5e050644 100644 --- a/protocol/0012-POSR-position_resolution.md +++ b/protocol/0012-POSR-position_resolution.md @@ -2,167 +2,131 @@ ## Acceptance Criteria -- All orders of "distressed traders" are cancelled (0012-POSR-001) -- Open positions of distressed traders are closed (0012-POSR-002) -- One market order is submitted for the net liability (0012-POSR-003) +- All orders of "distressed traders" in cross-margin mode are cancelled (0012-POSR-001) +- Open positions of distressed traders are closed immediately (0012-POSR-002) - Mark Price is never updated during position resolution (0012-POSR-004) -- Non-distressed traders who trade with the network because their open orders are hit during the close out trade have their positions settled correctly. (0012-POSR-005) +- Non-distressed traders who trade with the network because their open orders are hit during the networks' trade have their positions settled correctly. (0012-POSR-005) - When a distressed party has a [staking account](./0013-ACCT-accounts.md) with the same currency as the settlement currency of the market where it's distressed the staking account is NOT used in margin search and liquidation. (0012-POSR-006) -- When a party is distressed at the point of leaving an auction it should get closed out as soon as the market returns to continuous trading mode and all the parked orders (pegged and LP) get added back to the order book. (0012-POSR-008) +- When a party is distressed and gets closed out the network's position gets modified to reflect that it's now the network party that holds that volume. (0012-POSR-009) -## Summary +- When the network party holds a non-zero position and there are not enough funds in market's insurance pool to meet the mark-to-market payment the network's position is unaffected and loss socialisation is applied. (0012-POSR-010) -Position resolution is the mechanism which deals with closing out distressed positions on a given market. It is instigated when one or more participant's collateral balance is insufficient to fulfil their settlement or margin liabilities. +- When the network party holds a non-zero position and the market's insurance pool balance is below the network party's maintenance margin for that market the network's position in that market remains unaffected. (0012-POSR-011) -## Guide-level explanation +- The liquidation strategy can be updated using the market update transaction (0012-POSR-012) -## Reference-level explanation +- When the market is configured to use: + - `disposal time step` = `10s`, + - `disposal fraction` = `0.5`, + - `full disposal size` = `50`, + - `max fraction of book side within liquidity bounds consumed` = `0.01` -Any trader that has insufficient collateral to cover their margin liability is referred to as a "distressed trader". + and the volume on the buy side of the book within the liquidity bounds is always `10,000` (as volume on the book gets filled new orders get placed) then liquidating a distressed party with an open volume of `280` results in 4 network trades in total spaced `10s` apart with volumes of: `100`, `90`, `45`, `45`. (0012-POSR-013) -### Position resolution algorithm +- It is possible to check the network party's open volume and margin level in any market via the API. (0012-POSR-014) + +- It is possible to check the time of the next liquidation trade attempt in any market via the API. (0012-POSR-015) + +### Network position disposal + +- When calculating the available volume, the full remaining size of iceberg orders should be considered. (0012-POSR-019) +- When calculating the available volume, volume outside price monitoring bounds should be considered. (0012-POSR-020) +- When calculating the available volume, volume outside the liquidity price range should not be considered. (0012-POSR-021) +- Given a highly liquid market, if the network’s position is greater than `full disposal size`. The network must attempt to dispose `position * disposal fraction` at the next disposal step. (0012-POSR-022) +- Given a highly liquid market, if the network’s position is less than or equal to `full disposal size`. The network must attempt to dispose of its full position at the next disposal step. (0012-POSR-023) +- Given a highly liquid market, if the network’s `disposal fraction<1` and `full disposal size`=0, the network must still eventually dispose of its full position. (0012-POSR-024) +- The network must never dispose more than `available volume * max fraction of book side within liquidity bounds consumed` in a single order. (0012-POSR-025) +- A network disposal order which generates trades must not affect the mark price. (0012-POSR-026) +- A network disposal order can not cross with orders outside the liquidity price range. (0012-POSR-027) +- A network disposal order can cross with orders outside price monitoring bounds but must not trigger a price monitoring auction. (0012-POSR-028) +- A network disposal order which crosses multiple orders should generate multiple atomic trades. (0012-POSR-029) + +### Network Profit and Loss + +- Given the network starts with no position and does not dispose any of it's position during the scenario: (0012-POSR-016) -See [Whitepaper](https://vega.xyz/papers/vega-protocol-whitepaper.pdf), Section 5.3 , steps 1 - 3 + - The mark price moves to `100`, the network liquidates a distressed party with a long position of `1`. The network should report a position of `1` and a realised and unrealised pnl of `0` and `0` respectively. + - The mark price moves to `120`, the network liquidates a distressed party with a short position of `-1`. The network should report a position of `-1` and a realised and unrealised pnl of `20` and `0` respectively. + - The mark price moves to `60`. The network should report a position of `-1` and a realised and unrealised pnl of `20` and `60` respectively. -1. A "distressed trader" has all their open orders on that market cancelled. Note, the network must then recalculate their margin requirement on their remaining open position and if they now have sufficient collateral (i.e. aren't in the close out zone) they are no longer considered a distressed trader and not subject to position resolution. The market may at any point in time have multiple distressed traders that require position resolution. They are 'resolved' together in a batch. -1. The batch of distressed open positions that require position resolution may be comprised of a collection of long and short positions. The network calculates the overall net long or short position. This tells the network how much volume (either long or short) needs to be sourced from the order book. For example, if there are 3 distressed traders with +5, -4 and +2 positions respectively. Then the net outstanding liability is +3. If this is a non-zero number, do Step 3. -1. This net outstanding liability is sourced from the market's order book via a single market order (in above example, that would be a market order to sell 3 on the order book) executed by the network as a counterpart. This internal entity is the counterpart of all trades that result from this single market order and now has a position which is comprised of a set of trades that transacted with the non-distressed traders on the order book. Note, the network's order should not incur a margin liability. Also, these new positions (including that incurred by the network) will need to be "MTM settled". This should happen after Step 5 to ensure we don't bankrupt the market's insurance pool before collecting the distressed trader's collateral. This has been included as Step 6. -1. The network then generates a set of trades with all the distressed traders all at the volume weighted average price of the network's (new) open position. These trades should be readily distinguished from the trades executed by the network counterpart in Step 3 (suggest by a flag on the trades) - 1. Note, If there was no market order (i.e step 3 didn't happen) the close-out price is the most recently calculated _Mark Price_. See Scenario 1 below for the list of resulting trades for the above example. The open positions of all the "distressed" traders is now zero and the networks position is also zero. Note, no updates to the _Mark Price_ should happen as a result of any of these trades (as this would result in a new market-wide mark to market settlement at this new price and potentially lead to cascade close outs). -1. All bankrupt trader's remaining collateral in their margin account for this market is confiscated to the market's insurance pool. -1. If an order was executed on the market (in Step 3), the resulting trade volume between the network and passive orders must be mark-to-market settled for all parties involved including the network's internal 'virtual' party. As the network's closeout counterparty doesn't have collateral, any funds it 'owes' will be transferred from the market's insurance fund during this settlement process (as defined in the [settlement spec](./0003-MTMK-mark_to_market_settlement.md).). It's worth noting that the network close-out party must never have margins calculated for it. This also should naturally happen because no margin calculations would happen during the period that the network temporarily (instantaneously) has an open position, as the entire position resolution process must happen atomically. +- Given the network starts with no position and does not dispose any of it's position during the scenario: (0012-POSR-017) -### Note + - The mark price moves to `100`, the network liquidates a distressed party with a long position of `1`. The network should report a position of `1` and a realised and unrealised pnl of `0` and `0` respectively. + - The mark price moves to `90`, the network liquidates a distressed party with a long position of `1` (average entry price now equals `95`). The network should report a position of `2` and a realised and unrealised pnl of `0` and `-10` respectively. + - The mark price moves to `60`. The network should report a position of `2` and a realised and unrealised pnl of `0` and `-70` respectively. -- Entire distressed position should always be liquidated - even if reducing position size, by say 50%, would result in the remaining portion being above the trader's maintenance margin. -- When there's insufficient volume on the order-book to close out a distressed position no action should be taken: the position remains open and any amounts in trader's margin account should stay there. Same principle should apply if upon next margin recalculation the position is still distressed. -- If the party is distressed at a point of leaving auction it should be closedout immediately (provided there's enough volume on the book once all the pegged and liquidity provision orders get redeployed). - -## Examples and Pseudo code +- Given an empty insurance pool and the liquidation strategy `disposal time step = 5`, `disposal fraction = 0.5`, `full disposal size=0` and `max fraction of book side within liquidity bounds consumed = 0.01` during the below scenario: (0012-POSR-018) + - The mark price moves to 100, the network liquidates a distressed party with a long position of 2. The network should report a position of 2 and a realised and unrealised pnl of 0 and 0 respectively. + - Given the order book: + | side | price | size | + |------|-------|------| + | buy | 90 | 1000 | + | sell | 110 | 1000 | + - The time updates to the next disposal time, the network reduces its position by 1. The network should report a position of 1 and a realised and unrealised pnl of -10 and 0 respectively. + - The time updates to the next disposal time, the network reduces its position by 1. The network should report a position of 1 and a realised and unrealised pnl of -20 and 0 respectively. + - Loss socialisation should be applied and the accumulated balance for all accounts should be unchanged. -### _Scenario - All steps_ - -`Trader1 open position: +5` -`Trader1 open orders: 0` -`Trader2 open position: -4` -`Trader2 open orders: 0` -`Trader3 open position: +2` -`Trader3 open orders: 0` - -#### STEP 1 -No traders are removed from the distressed trader list. +## Summary + +Position resolution is the mechanism which deals with closing out distressed positions on a given market. It is instigated when one or more participant's margin account balance falls below their latest maintenance margin level. + +## Guide-level explanation + +## Reference-level explanation + +Any trader that has insufficient collateral to cover their margin liability is referred to as a "distressed trader". + +### Position resolution algorithm -#### STEP 2 +#### Liquidating distressed party's position in a market -`NetOutstandingLiability = 5 - 4 + 2 = 3` +- Position resolution evaluation gets carried out each time the mark price is updated, strictly after the margin levels margin level update and mark-to-market settlement. -#### STEP 3 +- During position resolution evaluation party's margin account balance is compared to it's latest maintenance margin level. If the account balance is below the margin level then the party gets marked as distressed. -```json -LiquiditySourcingOrder: { - type: 'market', - direction: 'sell', - size: 3 -} +- If a party marked as "distressed" has the cross-margin mode selected for the market being considered: -LiquiditySourcingTrade1: { - buyer: Trader4, - seller: Network, - size: 2, - price: 120, - type: 'liquidity-sourcing' -} + - all their open orders on that market get cancelled without releasing any collateral from the margin account, + - their margin level then gets recalculated to account only for the open volume they hold in a market + - if party's margin account balance is now above or equal to their latest maintenance margin level then such party is no longer marked as "distressed". -LiquiditySourcingTrade2: { - buyer: Trader5, - seller: Network, - size: 1, - price: 100, - type: 'liquidity-sourcing' +- If party's is marked as "distressed" at this stage then: -} + - their open volume gets added to network's open volume for that market, + - party's open volume in that market gets set to 0, + - the full amount of party's margin account balance gets transferred to market's insurance pool. -``` +This concludes the position resolution from party's perspective. -#### STEP 4 +#### Managing network's position -Close out trades are generated with the distressed traders +Whilst network has a non-zero position in a given market it's treated as any other party with market's insurance in that market acting as its margin account and with and exception that margin search, margin release or liquidation are never attempted on a network party. -```json -CloseOutTrade1 { - buyer: Network, - seller: Trader1, - size: 5, - price: 113.33, - type: 'safety-provision' -} +Whenever the network party has a non-zero position it attempts to unload it using an [immediate or cancel](./0014-ORDT-order_types.md) limit order. If the the network has a `long` position it will submit a `sell` order. If it has a short position it will submit a buy order. The size of that order is chosen according to the liquidation strategy which forms a part of the market's configuration. The strategy can be updated at any point whilst market is active with a market change [governance vote](./0028-GOVE-governance.md#2-change-market-parameters). -CloseOutTrade2 { - buyer: Trade2, - seller: Network, - size: 4, - price: 113.33, - type: 'safety-provision' -} +Currently only one liquidation strategy is supported and its defined by the following parameters: -CloseOutTrade3 { - buyer: Network, - seller: Trader3, - size: 2, - price: 113.33, - type: 'safety-provision' -} -``` +- `disposal time step` (min: `1s`, max: `1h`): network attempts to unload its position in a given market every time it goes out of auction and then every `disposal time step` seconds as long as market is not in auction mode and while the network's position is not equal to `0`, +- `disposal fraction` (min: `0.01`, max: `1`): fraction of network's current open volume that it will try to reduce in a single disposal attempt, +- `full disposal size` (min: `0`, max: `max int`): once net absolute value of network's open volume is at or below that value, the network will attempt to dispose the remaining amount in one go, +- `max fraction of book side within liquidity bounds consumed` (min: `0`, max: `1`): once the network chooses the size of its order (`s_candidate`) the effective size will be calculated as `s_effective=min(m*N, s_candidate)`, where `N` is the sum of volume (on the side of the book with which the network's order will be matching) that falls within the range implied by the `market.liquidity.priceRange` [parameter](./0044-LIME-lp_mechanics.md#market-parameters) and `m` is the `max fraction of book side within liquidity bounds consumed`. -This results in the open position sizes for all distressed traders and the network entities to be zero. +Assume the price range implied by the `market.liquidity.priceRange` is `[a, b]`. Once the network has worked out a size of its immediate or cancel limit order it sets it's price to `a` if it's a sell order or `b` if it's a buy order, and it submits the order. -`// OpenPosition of Network = -3 +5 -4 +2 = 0` -`// OpenPosition of Trader1 = +5 -5 = 0` -`// OpenPosition of Trader2 = -4 +4 = 0` -`// OpenPosition of Trader3 = +2 - 2 = 0` +Note that setting: -#### STEP 5 +- `disposal time step` = `0s`, +- `disposal fraction` = `1`, +- `full disposal size` = `max int`, +- `max fraction of book side within liquidity bounds consumed` = `1` -The collateral from distressed traders is moved to the market's insurance pool - -```json -// sent by Settlement Engine to the Collateral Engine -TransferRequest1 { - from: [Trader1_MarginAccount], - to: MarketInsuranceAccount, - amount: Trader1_MarginAccount.size, // this needs to be the full amount -} +is closest to reproducing the legacy setup where party would get liquidated immediately (with a difference that closeout now happens immediately even if there's not enough volume on the book to fully absorb it) hence the above values should be used when migrating existing markets to a new version. For all new markets these values should be specified explicitly. -TransferRequest2 { - from: [Trader2_MarginAccount], - to: MarketInsuranceAccount, - amount: Trader2_MarginAccount.size, // this needs to be the full amount -} +Different liquidation strategies with different parameters might be proposed in the future, hence implementation should allow for easy substitution of strategies. -TransferRequest3 { - from: [Trader3_MarginAccount], - to: MarketInsuranceAccount, - amount: Trader3_MarginAccount.size, // this needs to be the full amount -} -``` - -#### STEP 6 +API requirements: -Traders from step 3 need to be settled. - -Prior to STEP 3 trades, assume Trader 4 and Trader 5 had the following open positions. - -`// OpenPosition of Trader4 = -3` -`// OpenPosition of Trader5 = 15` - -Trader 4 has therefore closed out 2 contracts through the `LiquiditySourcingTrade` 1. These need to be settled against the trade price. - -```json -TransferRequest4 { - from: [MarketInsuranceAccount], - to: Trader4_MarginAccount, - amount: (120 - PreviousMarkPrice) * -2, // this is the movement since the last settlement multiplied by the volume of the closed out amount -} - -``` +- create an endpoint to easily identify network's position in any given market, +- create an endpoint to easily identify network's margin levels in any given market, +- create an endpoint to easily check time of next liquidation trade attempt. diff --git a/protocol/0013-ACCT-accounts.md b/protocol/0013-ACCT-accounts.md index 1c4243b41..62c6ee24c 100644 --- a/protocol/0013-ACCT-accounts.md +++ b/protocol/0013-ACCT-accounts.md @@ -4,7 +4,7 @@ A party only has control over balances in the "general" account for each asset. [Parties](./0017-PART-party.md) are identified by Vega public keys. Each party that makes a deposit on one of the asset bridges, currently only [Ethereum ERC20 bridge](./0031-ETHB-ethereum_bridge_spec.md) will have a general account for the relevant [asset](./0040-ASSF-asset_framework.md) created. -In order to submit [orders](./0014-ORDT-order_types.md a non-zero general account balance is needed; Vega will transfer appropriate amount to the [margin account](./0011-MARA-check_order_allocate_margin.md) for the party and the market. +In order to submit [orders](./0014-ORDT-order_types.md) a non-zero general account balance is needed; Vega will transfer appropriate amount to the [margin account](./0011-MARA-check_order_allocate_margin.md) for the party and the market. Any party can submit a withdrawal transaction to withdraw assets from the general account to a specified address on another chain, currently only [Ethereum ERC20 bridge](./0031-ETHB-ethereum_bridge_spec.md). @@ -85,7 +85,7 @@ Every market will have at least one insurance pool account that holds collateral When a [market launches](./0043-MKTL-market_lifecycle.md), an insurance pool account is created for that market for each settlement asset. This account is used by the protocol during the collection of [margin requirements](./0010-MARG-margin_orchestration.md) and the collection of [mark to market settlement](./0003-MTMK-mark_to_market_settlement.md). -When a market is finalised / closed remaining funds are redistributed equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. This occurs using ledger entries to preserve double entry accounting records within the collateral engine. +When a market is finalised / closed remaining funds are transferred into the global insurance pool using the same settlement asset. This occurs using ledger entries to preserve double entry accounting records within the collateral engine. ## General insurance pool @@ -144,10 +144,6 @@ Additional accounts associated with [distribution](./0056-REWA-rewards_overview. - Cannot have a non-zero balance on a margin account where there's no position / position size = 0 and no active orders. (0013-ACCT-009) - Cannot transfer into or out of a margin account where there's no position / position size = 0 and no active orders. (0013-ACCT-010) -### Party holding accounts in Spot market - -- Every party that submits an order on a Spot market will have a holding account created for the relevant market asset pair. (0013-ACCT-030) -- Each party should only have two holding accounts per market: one for the the base_asset and one for the quote_asset. (0013-ACCT-031) ### Liquidity Provider bond accounts @@ -159,7 +155,7 @@ Additional accounts associated with [distribution](./0056-REWA-rewards_overview. - When a market opens for trading, there is an insurance account that is able to be used by that market for every settlement asset of that market. (0013-ACCT-020) - Only protocol-initiated aka internal transfer requests move money in or out of the insurance account. User initiated transfer requests cannot be used to move funds in or out of insurance pool. (0013-ACCT-021) -- When all markets of a risk universe expire and/or are closed, the insurance pool account has its outstanding funds redistributed to the global insurance pool account for the appropriate asset (if it doesn't exist create it) and other insurance pools using the same asset. (0013-ACCT-032) +- When a market terminates and settles (the final settlement cashflow happens), the insurance pool account has its outstanding balance transferred to the global insurance pool account for the appropriate asset (if it doesn't exist create it), other insurance pools using the same asset will not get the outstanding funds. (0013-ACCT-033) ### Special case: Staking accounts diff --git a/protocol/0014-ORDT-order_types.md b/protocol/0014-ORDT-order_types.md index b9190402a..5881d2f53 100644 --- a/protocol/0014-ORDT-order_types.md +++ b/protocol/0014-ORDT-order_types.md @@ -51,12 +51,11 @@ Notes on scope of current version of this spec: ### Stop orders -In addition to normal immediately executing order, Vega should accept the submission of stop orders. +In addition to normal immediately executing orders, Vega should accept the submission of stop orders. These differ from normal orders in that they sit off the order book until triggered, when they are entered as normal. These are generally used to exit positions under pre-defined conditions, either as a "stop loss" order that controls the maximum losses a position may take, a "take profit" order that closes a position once a defined level of profit has been made, or both. - -A stop order submission can be made (stop loss or take profit are probably both just called a stop order internally). +A stop order submission can be made (stop loss or take profit are both just called a stop order internally). - Stop order submissions must include either a trigger price OR trailing stop distance as a % move from the reference price in addition to a normal order submission. @@ -71,10 +70,16 @@ If it has an expiry then it can be set either to cancel on expiry (i.e. it is de An OCO contains TWO stop order submissions, and must include one in each trigger direction. OCOs work exactly like two separate stop orders except that if one of the pair is triggered, cancelled, deleted, or rejected, the other one is automatically cancelled. An OCO submission allows a user to have a stop loss and take profit applied to the same amount of their position without the risk of both trading and reducing their position by more than intended. - - An OCO submission cannot be set to execute at expiry. + - An OCO submission can be set to have one of three different behaviours at expiry, either triggering one side, triggering the other, or expire without any action. This is configured through the setting of the expiry behaviour on each leg. Setting each leg to trade at expiration will result in the OCO being rejected. - The stop order submission wraps a normal order submission. +- A stop order submission may have an optional `Size Override`: + - If unset, the size within the contained normal order submission will be used + - If set to `Position`, triggering should override the contained order's size with the trader's entire current position on the market. + - The `Position` override configuration should also include the option `position_fraction` which determines what proportion of the position is closed when the stop order is triggered. At time of triggering the size of the order will be determined by $fraction \cdot position$. + - All `Position` stop orders existing should be cancelled if a trader's position changes from long to short (or vice versa). + - The submission is validated when it is received but does not initially interact with the order book unless it is triggered immediately (see below). - If and when the trigger price is breached in the specified direction the order provided in the stop order submission is created and enters the book or trades as normal, as if it was just submitted. @@ -86,7 +91,9 @@ Therefore the trigger level of a stop order moves with the market allowing the t - The order can't be triggered or trade at all during an auction (even if the current price would normally trigger it immediately on entry). -- A stop order can be entered during an auction, and can then be triggered by the auction uncrossing price if the auction results in a trade, as well as any trades (including auction uncrossing trades) after that. +- A stop order can be entered during an auction (except opening auction), and can then be triggered by the auction uncrossing price if the auction results in a trade, as well as any trades (including auction uncrossing trades) after that. + +- A stop order entered during opening auction will be rejected. - GFA is not a valid TIF for a stop order submission. @@ -261,7 +268,7 @@ Network orders are used during [position resolution](./0012-POSR-position_resolu - Any GTT limit order that [still] resides on the order book at its expiry time is cancelled and removed from the book before any events are processed that rely on its being present on the book, including any calculation that incorporates its volume and/or price level. (0014-ORDT-004). - A GTT order submitted at a time >= its expiry time is rejected. (0014-ORDT-005). - No party can submit a [network order type](#network-orders) (0014-ORDT-006). -- A pegged order (including iceberg pegged orders) never has its price updated during the execution of an incoming aggressive order (even as price levels get consumed so that its reference price changes after the execution). (0014-ORDT-039). +- A pegged order (including iceberg pegged orders) never has its price updated during the execution of an incoming aggressive order (even as price levels get consumed so that its reference price changes after the execution). (0014-ORDT-039) ### Iceberg Orders AC's @@ -272,7 +279,6 @@ Network orders are used during [position resolution](./0012-POSR-position_resolu 3. An iceberg post only order can be submitted (0014-ORDT-009). 4. An iceberg reduce only order is rejected (0014-ORDT-010). 5. For an iceberg order that is submitted with total size x and display size y the margin taken should be identical to a regular order of size `x` rather than one of size `y` (0014-ORDT-011) -In Spot market, for an iceberg order that is submitted with total size x and display size y the holding asset taken should be identical to a regular order of size `x` rather than one of size `y` (0014-ORDT-091) 6. For an iceberg order, the orders are refreshed immediately after producing a trade. Every time volume is taken from the displayed quantity , the order is refreshed if display quantity < minimum peak size (0014-ORDT-012). - If the order is successfully refreshed , then the order loses its time priority and is pushed to the back of the queue 7. For an iceberg order that's submitted when the market is in auction, iceberg orders trade according to their behaviour if they were already on the book (trading first the visible size, then additional if the full visible price level is exhausted in the uncrossing) (0014-ORDT-013). @@ -296,12 +302,10 @@ In Spot market, for an iceberg order that is submitted with total size x and dis 1. Amending an iceberg order to increase size will increase the total and remaining quantities of the order and time priority of the order is not lost (0014-ORDT-023). 2. Amending an iceberg order to decrease size will decrease the total and remaining quantities and time priority of the order is not lost (0014-ORDT-024). 3. Amend an iceberg order to decrease size so that the displayed quantity is decreased. Total, displayed and remaining quantity is decreased, margin is recalculated and released and time priority is not lost (0014-ORDT-025) -4. In Spot market, amend an iceberg order to decrease size so that the displayed quantity is decreased. Total, displayed and remaining quantity is decreased, margin is recalculated and released and time priority is not lost. (0014-ORDT-103) #### Iceberg Order Cancellation 1. Cancelling an iceberg order will cancel the order, remove it from the order book , release margin and update order book to reflect the change (0014-ORDT-026) -1. In Spot market, cancelling an iceberg order will cancel the order, remove it from the order book , release holding asset and update order book to reflect the change (0014-ORDT-104) #### Iceberg Order Execution @@ -346,6 +350,8 @@ In Spot market, for an iceberg order that is submitted with total size x and dis - A stop order with expiration time `T` set to expire at that time will expire at time `T` if reached without being triggered. (0014-ORDT-052) - A stop order with expiration time `T` set to execute at that time will execute at time `T` if reached without being triggered. (0014-ORDT-053) - If the order is triggered before reaching time `T`, the order will have been removed and will *not* trigger at time `T`. (0014-ORDT-054) + - An OCO stop order with expiration time `T` with one side set to execute at that time will execute at time `T` if reached without being triggered, with the specified side triggering and the other side cancelling. This must be tested both sides (fall below and rise above). (0014-ORDT-131) + - An OCO stop order with expiration time `T` with both sides set to execute at that time will be rejected on submission (0014-ORDT-130) - A stop order set to trade volume `x` with a trigger set to `Rises Above` at a given price will trigger at the first trade at or above that price. (0014-ORDT-055) - If a pair of stop orders are specified as OCO, one being triggered also removes the other from the book. (0014-ORDT-056) @@ -361,8 +367,15 @@ In Spot market, for an iceberg order that is submitted with total size x and dis - Be triggered by a fall to `45`. (0014-ORDT-063) - Not be triggered by a fall to `46`. (0014-ORDT-064) -- A stop order placed either prior to or during an auction will not execute during an auction, nor will it participate in the uncrossing. (0014-ORDT-065) -- A stop order placed either prior to or during an auction, where the uncrossing price is within the triggering range, will immediately execute following uncrossing. (0014-ORDT-066) +- A stop order placed during an auction will not execute during an auction, nor will it participate in the uncrossing. (0014-ORDT-065) +- A stop order placed during an auction, where the uncrossing price is within the triggering range, will immediately execute following uncrossing. (0014-ORDT-066) + +- A stop order placed prior to an auction will not execute during an auction, nor will it participate in the uncrossing. (0014-ORDT-134) +- A stop order placed prior to an auction, where the uncrossing price is within the triggering range, will immediately execute following uncrossing. (0014-ORDT-135) +- An order with a stop is placed during continuous trading. The market goes into auction. The market exits auction, the condition for triggering the stop is not met. The stop order is still present. (0014-ORDT-136) + +- A party places a stop order on a market in continuous trading, the market moves to an auction and the party cancels the stop order. When the market exits the auction the party no longer has a stop order. (0014-ORDT-132) +- A stop order placed during the opening auction, will be rejected. (0014-ORDT-133) - If a trader has open stop orders and their position moves to zero whilst they still have open limit orders their stop orders will remain active. (0014-ORDT-067) - If a trader has open stop orders and their position moves to zero with no open limit orders their stop orders are cancelled. (0014-ORDT-068) @@ -370,6 +383,8 @@ In Spot market, for an iceberg order that is submitted with total size x and dis - A Stop order that hasn't been triggered can be cancelled. (0014-ORDT-071) - All stop orders for a specific party can be cancelled by a single stop order cancellation. (0014-ORDT-072) - All stop orders for a specific party for a specific market can be cancelled by a single stop order cancellation. (0014-ORDT-073) +- If a stop order is placed with a position_fraction equal to 0.5 and the position size is 5 then the rounding should be equal to 3 (0014-ORDT-138) + ## Stop Orders - Negative Cases @@ -378,6 +393,8 @@ In Spot market, for an iceberg order that is submitted with total size x and dis - Stop orders submitted with expiry in the past are rejected. (0014-ORDT-076) - GFA Stop orders submitted are rejected. (0014-ORDT-077) - Stop orders once triggered can not be cancelled. (0014-ORDT-078) +- If a stop order is placed with a position_fraction equal to 0 the order should be rejected. (0014-ORDT-139) +- A party with a long position cannot enter a buy stop order, and a party with a short position cannot enter a sell stop order (0014-ORDT-137) ## Stop Orders - Snapshots @@ -387,6 +404,12 @@ In Spot market, for an iceberg order that is submitted with total size x and dis - API end points should be available to query stop orders with all relevant fields. (0014-ORDT-080) +## Stop Orders - Linked + +- A stop order with a size override linked to the position of the trader will use the current position as an override of the triggered order size. (0014-ORDT-127) +- All stop orders with a position size override should be cancelled if the trader's position flips sides (long->short or short->long). (0014-ORDT-128) +- A stop order with a position size override with a position_fraction set to 0.75, for a trader with long position 20, should create a stop order for selling size 15 when triggered (0014-ORDT-129) + ## Perpetuals - All order types should be able to be placed and act in the same way on a perpetual market as on an expiring future market. Specifically this includes: diff --git a/protocol/0015-INSR-market_insurance_pool_collateral.md b/protocol/0015-INSR-market_insurance_pool_collateral.md index be3dee548..34677e201 100644 --- a/protocol/0015-INSR-market_insurance_pool_collateral.md +++ b/protocol/0015-INSR-market_insurance_pool_collateral.md @@ -14,7 +14,7 @@ When a market is finalised / closed remaining funds are distributed equally amon ## Global insurance pool -One global insurance pool per each of the settlement asset in which markets were terminated with remaining market-level insurance pool funds exists. It receives a portion of the funds from market's insurance pool upon its closure (funds get distributed equally between the global insurance pool and remaining active markets using the same settlement asset, e.g. if there are 4 such markets then the global insurance pool receives 1/5 of the funds). +One global insurance pool per each of the settlement asset in which markets were terminated with remaining market-level insurance pool funds exists. It receives all the funds from market's insurance pool upon its closure. Global insurance pool is not included in the margin search process when party is insolvent - only the insurance pool of the market in which the insolvent party's liabilities are considered gets searched, if funds therein are insufficient then the remaining shortfall gets dealt with using loss socialisation. @@ -23,6 +23,6 @@ Funds can only be withdrawn from the global insurance pool via a [governance ini ## Acceptance Criteria - When a market proposal gets accepted and the opening auction commences, there is an insurance account that is available for use by that market for the settlement asset of that market and its balance is zero. (0015-INSR-001) -- After the market enters transitions from "trading terminated state" to "settled" state (see [market lifecyle](0043-MKTL-market_lifecycle.md)), and `market.liquidity.successorLaunchWindowLength` has elapsed from the settlement time, the insurance pool account has its balance[redistributed](./0015-INSR-market_insurance_pool_collateral.md) to the on-chain treasury for the settlement asset of the market and other insurance pools using the same asset. (0015-INSR-002) +- After the market enters transitions from "trading terminated state" to "settled" state (see [market lifecyle](0043-MKTL-market_lifecycle.md)), and `market.liquidity.successorLaunchWindowLength` has elapsed from the settlement time, the insurance pool account has its balance transferred to the on-chain treasury for the settlement asset of the market. For markets that have a named successor market the appropriate fraction of the insurance pool balance is transferred to the insurance pool of the successor market. (0015-INSR-005) - The [insurance pool feature test](https://github.com/vegaprotocol/vega/blob/develop/core/integration/features/verified/0015-INSR-insurance_pool_balance_test.feature) is passing. (0015-INSR-003) - When global insurance pool has positive balance for the settlement asset used by market with insolvent party, and that market's insurance pool has insufficient balance to cover the shortfall, the global insurance pool balance does NOT get used and the remaining balances are dealt with using the loss socialisation mechanism. (0015-INSR-004) diff --git a/protocol/0019-MCAL-margin_calculator.md b/protocol/0019-MCAL-margin_calculator.md index 334d9a286..f307aabd7 100644 --- a/protocol/0019-MCAL-margin_calculator.md +++ b/protocol/0019-MCAL-margin_calculator.md @@ -1,6 +1,6 @@ # Margin Calculator -## Acceptance Criteria +## Acceptance Criteria (Cross margin) - Get four margin levels for one or more parties (0019-MCAL-001) @@ -24,7 +24,7 @@ - A feature test that checks margin in case market PDP < 0 is created and passes. (0019-MCAL-010) -- If a party is short `1` unit and the mark price is `15 900` and `market.maxSlippageFraction[1] = 0.25`, `market.maxSlippageFraction[2] = 0.25` and `RF short = 0.1` and order book is +- If a party is short `1` unit and the mark price is `15 900` and `market.linearSlippageFactor = 0.25` and `RF short = 0.1` and order book is ```book buy 1 @ 15 000 @@ -34,11 +34,405 @@ sell 10 @ 100 100 ``` - then the maintenance margin for the party is `15 900 x (0.25 x 1 + 0.25 x 1 x 1) + 0.1 x 1 x 15 900 = 9 540`. (0019-MCAL-011) + then the maintenance margin for the party is `min(1 x (100000-15900), 15900 x 0.25 x 1) + 0.1 x 1 x 15900 = 5565`. (0019-MCAL-024) -- In the same situation as above, if `market.maxSlippageFraction[1] = 100`, `market.maxSlippageFraction[2] = 100` (i.e. 10 000% for both) instead, then the margin for the party is `84 100 + 0.1 x 1 x 15900 = 85 690`. (0019-MCAL-012) +- In the same situation as above, if `market.linearSlippageFactor = 100`, (i.e. 10 000%) instead, then the margin for the party is `min(1 x (100000-15900), 15900 x 100 x 1) + 0.1 x 1 x 15900 = 85690`. (0019-MCAL-025) -- If the `market.maxSlippageFraction` is updated via governance then it will be used at the next margin evaluation i.e. at the first mark price update following the parameter update. (0019-MCAL-013) +- If the `market.linearSlippageFactor` is updated via governance then it will be used at the next margin evaluation i.e. at the first mark price update following the parameter update. (0019-MCAL-013) + +- For a perpetual future market, the maintenance margin is equal to the maintenance margin on an equivalent dated future market, plus a component related to the expected upcoming margin funding payment. Specifically: + - If a party is long `1` unit and the mark price is `15 900` and `market.linearSlippageFactor = 0.25` and `RF long = 0.1` and order book is + + ```book + buy 1 @ 15 000 + buy 10 @ 14 900 + and + sell 1 @ 100 000 + sell 10 @ 100 100 + ``` + + then the dated future maintenance margin component for the party is `min(1 x (100000-15900), 15900 x 0.25 x 1) + 0.1 x 1 x 15900 = 5565`. The current accrued funding payment for the perpetual component is calculated using + + ```book + delta_t = funding_period_end - max(funding_period_start, internal_data_points[0].t) + funding_payment = f_twap - s_twap + min(clamp_upper_bound*s_twap,max(clamp_lower_bound*s_twap, (1 + delta_t * interest_rate)*s_twap-f_twap)) + ``` + + Where `f_twap` represents the internal mark price TWAP and `s_twap` represents the TWAP from the external oracle feed. When clamp bounds are large we use: + + ```book + funding_payment = f_twap - s_twap + (1 + delta_t * interest_rate)*s_twap-f_twap + = s_twap * delta_t * interest_rate + ``` + + - If `s_twap = 1600`, `f_twap = 1590` `delta_t = 0.002` and `interest_rate = 0.05` then `funding_payment = 1600 * 0.002 * 0.05 = 0.16`. + - Thus, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 556.5 + 0.5 * 0.16 * 1 = 556.58` (0019-MCAL-026) + + - If instead + - `clamp_upper_bound*s_twap < max(clamp_lower_bound*s_twap, (1 + delta_t * interest_rate)*s_twap-f_twap)` + - `funding payment = f_twap - s_twap + clamp_upper_bound*s_twap = f_twap + s_twap * (clamp_upper_bound - 1)`. + - Then with `s_twap = 1600`, `clamp_upper_bound = 0.05` and `f_twap = 1500`, `funding_payment = 1500 + 1600 * (0.05 - 1) = -20` + - Thus, with `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 525 + 0.5 * max(0, -20 * 1) = 525` (0019-MCAL-027) + - However is position is instead `-1`, with the same margin requirement, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 525 + 0.5 * max(0, -20 * -1 = 535)`(0019-MCAL-030) + + - If instead + - `clamp_upper_bound*s_twap > clamp_lower_bound*s_twap > (1 + delta_t * interest_rate)*s_twap-f_twap)` + - `funding payment = f_twap - s_twap + clamp_lower_bound*s_twap = f_twap + s_twap * (clamp_lower_bound - 1)`. + - Then with `s_twap = 1600`, `clamp_lower_bound = -0.05` and `f_twap = 1700`, `funding_payment = 1700 + 1600 * (-0.05 - 1) = 20` + - Thus, with `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 595 + 0.5 * max(0, 20 * 1) = 605` (0019-MCAL-028) + - However is position is instead `-1`, with the same margin requirement, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 595 + 0.5 * max(0, 20 * -1) = 595`(0019-MCAL-029) + +## Acceptance Criteria (Isolated-margin) + +**When party has a newly created short position:** + +- If a party has a newly created short position of `1` and the mark price is `15 900` and `market.linearSlippageFactor = 0.25`, `RF short = 0.1` and `Initial margin factor = 1.5` and order book is + + ```book + buy 1 @ 15 000 + buy 10 @ 14 900 + and + sell 1 @ 100 000 + sell 10 @ 100 100 + ``` + +- When switching to isolated-margin mode and the `margin factor short = 0.11`, the maintenance margin should be updated to `average entry price x current position x new margin factor = 57500 x 1 x 0.11 = 6325`, the switching will be rejected with message "required position margin must be greater than initial margin". (0019-MCAL-032) + +- When switching to isolated-margin mode and the `margin factor short = 0.9`, the maintenance margin should be updated to `average entry price x current position x new margin factor = 15900 x 1 x 0.9 = 14310`, and margin account should be updated to `14310`. (0019-MCAL-033) + +- When decreasing the `margin factor short` from `0.9` to `0.7`, the maintenance margin should be updated to `average entry price x current position x new margin factor = 15900 x 1 x 0.7 = 11130`, and margin account should be updated to `11130`. (0019-MCAL-031) + +- When increasing the `margin factor short` from `0.7` to `0.9`, the maintenance margin should be updated to `average entry price x current position x new margin factor = 15900 x 1 x 0.9 = 14310`, and margin account should be updated to `14310`. (0019-MCAL-059) + +- Switching to isolated margin mode will be rejected if the party does not have enough asset in the general account to top up the margin account to the new required level (0019-MCAL-066) + +**When party has a position and an order which does not offset the position:** + +- When the party places a new short order of `10` with price `15910` which does not offset the existing position, and the market is in continuous trading. +There should be an additional amount `limit price x size x margin factor = 15910 x 10 x 0.9 = 143190` transferred into "order margin" account if the party has enough asset in the general account(0019-MCAL-034) + +- The order will be rejected if the party does not have enough asset in the general account (0019-MCAL-035) + +- The party amends the order size to `5`, and the amount `limit price x size x margin factor = 15912 x 5 x 0.9 = 71604` will be transferred from "order margin" account into general account (0019-MCAL-060) + +- Another trader places a buy order of `3` with price `15912`, party's position changes from `1` to `4`, party's margin account should have additional `15912 x 3 x 0.9 =42962` transferred from general account and order margin should be reduced to `15912x 2 x 0.9 = 28642`since party's order size has been reduced from `5` to `2` after the trade (0019-MCAL-061) + +- Switch margin mode from isolated margin to cross margin when party holds position only, the margin account should be updated to initial margin level in cross margin mode(0019-MCAL-065) + +- Switch from cross margin to isolated margin mode, both margin account and order margin should be updated (0019-MCAL-064) + +- When the party has no orders, their order margin account should be `0` (0019-MCAL-062) + +- When the mark price moves, the margin account should be updated while order margin account should not (0019-MCAL-067) + +- Amend the order (change size) so that new side margin + margin account balance < maintenance margin, the remaining should be stopped (0019-MCAL-068) + +**When a party has a position and an order which offsets the position:** + +- When the party places a new long order of `2` with price `15912` which offsets the existing position, and the market is in continuous trading. The margin account should not change as no additional margin is required (0019-MCAL-038) + +- When the party switches to cross margin mode, the margin accounts will not be updated until the next MTM (0019-MCAL-036) + +- The order will be rejected if the party does not have enough asset in the general account (0019-MCAL-037) + +- When the party place a new long order of `10` with price `145000` and the party has existing short position of `3`, and the market is in continuous trading. The margin account should have additional amount `limit price * size * margin factor = 145000 x (10-3) x 0.9 = 913500` added if the party has enough asset in the general account(0019-MCAL-039) + +- When increasing the `margin factor` and the party does not have enough asset in the general account to cover the new maintenance margin, then the new margin factor will be rejected (0019-MCAL-040) + +**Amending order:** + +- When the party submit a pegged order, it should be rejected(0019-MCAL-049) + +- When the party submit a iceberg pegged order, it should be rejected(0019-MCAL-052) + +- When the party has pegged orders and switches from cross margin mode to isolated margin mode, all the pegged orders will be cancelled. (0019-MCAL-050) + +- When the party has iceberg pegged orders and switches from cross margin mode to isolated margin mode, all the iceberg pegged orders will be cancelled. (0019-MCAL-051) + +- A party with multiple types of orders in cross margin mode switches to isolated margin only their pegged orders are cancelled. (0019-MCAL-057) + +- A market in continuous trading and a party with a partially filled pegged order in cross margin mode switches to isolated margin mode the unfilled portion of the pegged order is cancelled (0019-MCAL-075) + +- A market in continuous trading and a party with a partially filled iceberg pegged order in cross margin mode switches to isolated margin mode the unfilled portion of the pegged order is cancelled (0019-MCAL-078) + +- A market in auction trading and a party with a partially filled pegged order in cross margin mode switches to isolated margin mode the unfilled portion of the pegged order is cancelled (0019-MCAL-147) + +- A market in auction trading and a party with a partially filled iceberg pegged order in cross margin mode switches to isolated margin mode the unfilled portion of the pegged order is cancelled (0019-MCAL-148) + +- A party with a parked pegged order switches from cross margin mode to isolated margin mode, the parked pegged order is cancelled (0019-MCAL-149) + +- A party with a parked iceberg pegged order switches from cross margin mode to isolated margin mode, the parked iceberg pegged order is cancelled (0019-MCAL-144) + +- A market in auction and party with a partially filled pegged order switches from cross margin mode to isolated margin mode the unfilled portion of the pegged order is cancelled (0019-MCAL-145) + +- A market in an auction and party with a partially filled iceberg pegged order switches from cross margin mode to isolated margin mode the unfilled portion of the iceberg pegged order is cancelled (0019-MCAL-146) + +- When a party holds only orders, increases the orders price, the orders will not uncross (0019-MCAL-160) + +- When a party holds orders and positions, and increases the orders price, the orders will not uncross (0019-MCAL-161) + +- When a party holds only orders, increases the orders size, the orders will not uncross (0019-MCAL-162) + +- When a party holds orders and positions, increases the order size, the orders will not uncross (0019-MCAL-163) + +- When a party holds only orders, decreases the orders price, the orders will not uncross (0019-MCAL-164) + +- When a party holds orders and positions, and decreases the orders price, the orders will not uncross (0019-MCAL-165) + +- When a party holds only orders, decreases the orders size, the orders will not uncross (0019-MCAL-166) + +- When a party holds orders and positions, decreases the order size, the orders will not uncross (0019-MCAL-167) + +- When a party holds orders and positions, increase the order size, the order will be stopped (because their margin balance will be less than the margin maintenance level) (0019-MCAL-168) + +- When a party holds orders and positions, decreases the orders size, the order and order margin will be update (0019-MCAL-169) + +- When a party holds orders and positions, decreases the orders price, the order will be fully filled (0019-MCAL-172) + +- When a party holds orders and positions, decreases the orders price, the order will be partially filled (0019-MCAL-173) + +- When a party holds orders and positions, decreases the orders price, the order will be stopped (because if the orders is fully filled and then their margin balance will be less than the margin maintenance level) (0019-MCAL-174) + +- When a party holds orders and positions, decreases the orders price, the order will be stopped (because if the orders is partially filled and then their margin balance will be less than the margin maintenance level) (0019-MCAL-175) + +- When a party holds orders and positions, amend the orders price, the order will be partially filled but order margin level will increase, so the rest of the order is cancelled (0019-MCAL-176) + +**When a party is distressed:** + +- Open positions should be closed in the case of open positions dropping below maintenance margin level, active orders will remain active if closing positions does not lead order margin level to increase.(0019-MCAL-070) + +- Open positions should be closed in the case of open positions dropping below maintenance margin level, active orders will be cancelled if closing positions lead order margin level to increase.(0019-MCAL-071) + +- When a party (who holds open positions and bond account) gets distressed, open positions will be closed, the bond account will be emptied (0019-MCAL-072) + +- When a party (who holds open positions, open orders and bond account) gets distressed, the bond account will be treated as in cross margin mode, however active orders will remain active if closing positions does not lead order margin level to increase. (0019-MCAL-073) + +- When a party (who holds open positions, open orders and bond account) gets distressed, the bond account will be emptied, active orders will be cancelled if closing positions lead order margin level to increase. (0019-MCAL-074) + +**Switch between margin modes:** + +- switch to isolated margin with no position and no order (before the first order ever has been sent) in continuous mode(0019-MCAL-100) + +- switch back to cross margin with no position and no order in continuous mode(0019-MCAL-101) + +- switch to isolated margin with no position and no order (before the first order ever has been sent) in auction(0019-MCAL-102) + +- switch back to cross margin with no position and no order in continuous mode in auction(0019-MCAL-103) + +- switch to isolated margin with position and no orders with margin factor such that position margin is < initial should fail in continuous(0019-MCAL-104) + +- switch to isolated margin with position and no orders with margin factor such that position margin is < initial should fail in auction(0019-MCAL-105) + +- switch to isolated margin without position and with orders with margin factor such that position margin is < initial should fail in continuous(0019-MCAL-106) + +- switch to isolated margin without position and with orders with margin factor such that position margin is < initial should fail in auction(0019-MCAL-107) + +- switch to isolated margin with position and with orders with margin factor such that position margin is < initial should fail in continuous(0019-MCAL-108) + +- switch to isolated margin with position and with orders with margin factor such that position margin is < initial should fail in auction(0019-MCAL-109) + +- switch to isolated margin without position and no orders with margin factor such that there is insufficient balance in the general account in continuous mode(0019-MCAL-110) + +- switch to isolated margin with position and no orders with margin factor such that there is insufficient balance in the general account in continuous mode(0019-MCAL-112) + +- switch to isolated margin with position and no orders with margin factor such that there is insufficient balance in the general account in auction mode(0019-MCAL-113) + +- switch to isolated margin with position and with orders with margin factor such that there is insufficient balance in the general account in continuous mode(0019-MCAL-114) + +- switch to isolated margin with position and with orders with margin factor such that there is insufficient balance in the general account in auction mode(0019-MCAL-142/a>) +- switch to isolate margin with out of range margin factor(0019-MCAL-115) + +- submit update margin mode transaction with no state change (already in cross margin, "change" to cross margin, or already in isolated, submit with same margin factor)(0019-MCAL-116) + +- update margin factor when already in isolated mode to the same cases as in switch to isolated failures.(0019-MCAL-117) + +- switch to isolated margin without position and no orders successful in auction(0019-MCAL-119) + +- switch to isolated margin with position and no orders successful in continuous mode(0019-MCAL-120) + +- switch to isolated margin with position and no orders successful in auction(0019-MCAL-121) + +- switch to isolated margin without position and with orders successful in continuous mode(0019-MCAL-122) + +- switch to isolated margin without position and with orders successful in auction(0019-MCAL-123) + +- switch to isolated margin with position and with orders successful in continuous mode(0019-MCAL-124) + +- switch to isolated margin with position and with orders successful in auction(0019-MCAL-125) + +- increase margin factor in isolated margin without position and no orders successful in continuous mode(0019-MCAL-126) + +- increase margin factor in isolated margin without position and no orders successful in auction(0019-MCAL-127) + +- increase margin factor in isolated margin with position and no orders successful in continuous mode(0019-MCAL-128) + +- increase margin factor in isolated margin with position and no orders successful in auction(0019-MCAL-129) + +- increase margin factor in isolated margin without position and with orders successful in continuous mode(0019-MCAL-130) + +- increase margin factor in isolated margin without position and with orders successful in auction(0019-MCAL-131) + +- increase margin factor in isolated margin with position and with orders successful in continuous mode(0019-MCAL-132) + +- increase margin factor in isolated margin with position and with orders successful in auction(0019-MCAL-133) + +- In cross margin mode for a market with no price monitoring, a party `short 1`, `mark price = 15 900`, `market.linearSlippageFactor = 0.25`, `RF short = 0.1` and order book is + + ```book + buy 1 @ 15 000 + buy 10 @ 14 900 + and + sell 1 @ 100 000 + sell 10 @ 100 100 + ``` + + the maintenance margin for the party is `min(1 x (100 000-159 00), 159 00 x 0.25 x 1) + 0.1 x 1 x 159 00 = 5565` + + for this market the party switches to isolated margin with `margin factor=0.9` then the party will have margin account balance of + `average entry price x current position x new margin factor = 57 500 x 1 x 0.9 = 6325` + the difference topped up from the party’s general account(0019-MCAL-233) + +- In isolated margin mode, a party `short 1@15 900`, `margin factor=0.9` and order book is + + ```book + buy 1 @ 15 000 + buy 10 @ 14 900 + and + sell 1 @ 100 000 + sell 10 @ 100 100 + ``` + + the margin account will hold `average entry price x current position x new margin factor = 57 500 x 1 x 0.9 = 6325` + + for this market the party switches to cross margin and the market has `market.linearSlippageFactor = 0.25`, `RF short = 0.1` then the maintenance margin for the party is `min(1 x (100 000-159 00), 159 00 x 0.25 x 1) + 0.1 x 1 x 159 00 = 5565` + but if `5565 < collatoral release level` the maintenance margin will remain unchanged at `6325` + + the difference topped up from the party’s general account(0019-MCAL-232) + +- switch to cross margin without position and no orders successful in continuous mode(0019-MCAL-134) + +- switch to cross margin without position and no orders successful in auction(0019-MCAL-135) + +- switch to cross margin with position and no orders successful in continuous mode(0019-MCAL-136) + +- switch to cross margin with position and no orders successful in auction(0019-MCAL-137) + +- switch to cross margin without position and with orders successful in continuous mode(0019-MCAL-138) + +- switch to cross margin without position and with orders successful in auction(0019-MCAL-139) + +- switch to cross margin with position and with orders successful in continuous mode(0019-MCAL-140) + +- switch to cross margin with position and with orders successful in auction(0019-MCAL-141) + +**Check order margin:** + +- when party has no position, and place 2 short orders during auction, order margin should be updated(0019-MCAL-200) + +- when party has no position, and place short orders size -3 during auction, and long order size 1 which can offset, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-201) + +- when party has no position, and place short orders size -3 during auction, and long orders size 2 which can offset, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-202) + +- when party has no position, and place short orders size -3 during auction, and long orders size 3 which can offset, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-203) + +- when party has no position, and place short orders size -3 during auction, and long orders size 4, which is over the offset size, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-204) + +- When the party changes the order price during auction, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-205) + +- When the party reduces the order size only during auction, the order margin should be reduced (0019-MCAL-206) + +- when party has no position, and place 2 short orders size 3 and 4 long orders of size 4, which is over the offset size, order margin should be updated using max(price, mark Price, indicative Price)(0019-MCAL-207) + +- GFA order added during auction should not be used to count order margin in continuous(0019-MCAL-220) + +- when party has no position, and place 2 short orders during auction, order margin should be updated(0019-MCAL-221) + +- When the party cancel one of the two orders during continuous, order margin should be reduced. When the party increases the order price during continuous, order margin should increase(0019-MCAL-222) + +- When the party decreases the order price during continuous, order margin should decrease(0019-MCAL-223) + +- When the party decreases the order volume during continuous, order margin should decrease(0019-MCAL-224) + +- When the party increases the order volume while decrease price during continuous, order margin should update accordingly(0019-MCAL-225) + +- When the party's order is partially filled during continuous, order margin should update accordingly(0019-MCAL-226) + +- When the party cancel one of the two orders during continuous, order margin should be reduced(0019-MCAL-227) + +- place a GFA order during continuous, order should be rejected(0019-MCAL-228) + +- When the party has position -1 and order -3, and new long order with size 1 will be offset(0019-MCAL-229) + +- When the party has position -1 and order -3, and new long orders with size 2 will be offset(0019-MCAL-230) + +- When the party has position -1 and order -3, and new long orders with size 3 will be offset(0019-MCAL-231) + + +**Check decimals:** + +- A feature test that checks margin in case market PDP > 0 is created and passes. (0019-MCAL-090) + +- A feature test that checks margin in case market PDP < 0 is created and passes. (0019-MCAL-091) + +**Check API:** + +- For each market and each party which has positions or has switched between margin modes on the market, the API provides the maintenance margin levels. (0019-MCAL-092) + +- For each market and each party which has orders only and no positions or has switched between margin modes on the market + - cross margin to isolated margin, the API provides maintenance margin level of zero. (0019-MCAL-150) + - isolated margin to cross margin, the API provides expected maintenance margin level . (0019-MCAL-151) + +- For each market and each party which has either orders or positions on the market, the API provides the current margin mode and, when in isolated margin mode, margin factor. (0019-MCAL-143) + +## Acceptance Criteria (perpetual market in isolated margin mode) + +- For a perpetual future market, the maintenance margin is equal to the maintenance margin on an equivalent dated future market, plus a component related to the expected upcoming margin funding payment. Specifically: + - If a party is long `1` unit and the mark price is `15 900` and `market.linearSlippageFactor = 0.25` and `RF long = 0.1` and order book is + + ```book + buy 1 @ 15 000 + buy 10 @ 14 900 + and + sell 1 @ 100 000 + sell 10 @ 100 100 + ``` + + then the dated future maintenance margin component for the party is `min(1 x (100000-15900), 15900 x 0.25 x 1) + 0.1 x 1 x 15900 = 5565`. The current accrued funding payment for the perpetual component is calculated using + + ```book + delta_t = funding_period_end - max(funding_period_start, internal_data_points[0].t) + funding_payment = f_twap - s_twap + min(clamp_upper_bound*s_twap,max(clamp_lower_bound*s_twap, (1 + delta_t * interest_rate)*s_twap-f_twap)) + ``` + + Where `f_twap` represents the internal mark price TWAP and `s_twap` represents the TWAP from the external oracle feed. When clamp bounds are large we use: + + ```book + funding_payment = f_twap - s_twap + (1 + delta_t * interest_rate)*s_twap-f_twap + = s_twap * delta_t * interest_rate + ``` + + - If `s_twap = 1600`, `f_twap=1590`, `delta_t = 0.002` and `interest_rate = 0.05` then `funding_payment = 1600 * 0.002 * 0.05 = 0.16`. + - Thus, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 556.5 + 0.5 * 0.16 * 1 = 556.58` (0019-MCAL-053) + + - If instead + - `clamp_upper_bound*s_twap < max(clamp_lower_bound*s_twap, (1 + delta_t * interest_rate)*s_twap-f_twap)` + - `funding payment = f_twap - s_twap + clamp_upper_bound*s_twap = f_twap + s_twap * (clamp_upper_bound - 1)`. + - Then with `s_twap = 1600`, `clamp_upper_bound = 0.05` and `f_twap = 1550`, `funding_payment = 1590 + 1600 * (0.05 - 1) = 1590 - 1520 = 70` + - Thus, with `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 5565 + 0.5 * 70 * 1 = 5600` (0019-MCAL-058) + - However is position is instead `-1`, with the same margin requirement, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 5565 + 0.5 * max(0, 70 * -1) = 5565`(0019-MCAL-054) + + - If instead + - `clamp_upper_bound*s_twap > clamp_lower_bound*s_twap > (1 + delta_t * interest_rate)*s_twap-f_twap)` + - `funding payment = f_twap - s_twap + clamp_lower_bound*s_twap = f_twap + s_twap * (clamp_lower_bound - 1)`. + - Then with `s_twap = 1600`, `clamp_lower_bound = -0.05` and `f_twap = 1700`, `funding_payment = 1700 + 1600 * (-0.05 - 1) = 20` + - Thus, with `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 595 + 0.5 * max(0, 20 * 1) = 605` (0019-MCAL-055) + - However is position is instead `-1`, with the same margin requirement, if `margin funding factor = 0.5`, `total margin requirement = futures margin + funding margin = 595 + 0.5 * max(0, 20 * -1) = 595`(0019-MCAL-056) + + +## Acceptance Criteria (Protocol upgrade) + +- All order margin balances are restored after a protocol upgrade (0019-MCAL-152). +- The `margin mode` and `margin factor` of any given party must be preserved after a protocol upgrade (0019-MCAL-153). - For a perpetual future market, the maintenance margin is equal to the maintenance margin on an equivalent dated future market, plus a component related to the expected upcoming margin funding payment. Specifically: - If a party is long `1` unit and the mark price is `15 900` and `market.maxSlippageFraction[1] = 0.25`, `market.maxSlippageFraction[2] = 0.25` and `RF long = 0.1` and order book is @@ -84,21 +478,151 @@ ## Summary -The *margin calculator* returns the set of relevant margin levels for a given position and entry price: +The *margin calculator* returns the set of margin levels for a given *actual position*, along with the amount of additional margin (if any) required to support the party's *potential position* (i.e. active orders including any that are parked/untriggered/undeployed). -1. ***Maintenance margin*** -1. ***Collateral search level*** -1. ***Initial margin*** -1. ***Collateral release level*** + +### Margining modes + +The system can operate in one of two margining modes for each position. +The current mode will be stored alongside of party's position record. + +1. **Cross-margin mode (default)**: this is the mode used by all newly created positions. +When in cross-margin mode, margin is dynamically acquired and released as a position is marked to market, allowing profitable positions to offset losing positions for higher capital efficiency (especially with e.g. pairs trades). + +1. **Isolated margin mode**: this mode sacrifices capital efficiency for predictability and risk management by segregating positions. +In this mode, the entire margin for any newly opened position volume is transferred to the margin account when the trade is executed. +This includes completely new positions and increases to position size. Other than at time of future trades, the general account will then +*never* be searched for additional funds (a position will be allowed to be closed out instead), nor will profits be moved into the +general account from the margin account. + +### Actual position margin levels + +1. **Maintenance margin**: the minimum margin a party must have in their margin account to avoid the position being liquidated. + +1. **Collateral search level**: when in cross-margin mode, the margin account balance below which the system will seek to recollateralise the margin account back to the initial margin level. + +1. **Initial margin**: when in cross-margin mode, the margin account balance initially allocated for the position, and the balance that will be returned to the margin account after collateral search and release, if possible. When in isolated margin mode, the margin account balance initially allocated for the position. The balance will not be returned to the margin account while the position is open, as there is no collateral search and release. + +1. **Collateral release level**: when in cross-margin mode, the margin account balance above which the system will return collateral from a profitable position to the party's general account for potential use elsewhere. The protocol is designed such that ***Maintenance margin < Collateral search level < Initial margin < Collateral release level***. +### Potential position margin level + +1. **Order margin**: the amount of additional margin on top of the amount in the margin account that is required for the party's current active orders. +Note that this may be zero if the active orders can only decrease the position size. + + Margin levels are used by the protocol to ascertain whether a trader has sufficient collateral to maintain a margined trade. When the trader enters an open position, this required amount is equal to the *initial margin*. Subsequently, throughout the life of this open position, the minimum required amount is the *maintenance margin*. As a trader's collateral level dips below the *collateral search level* the protocol will automatically search for more collateral to be assigned to support this open position from the trader's general collateral accounts. In the event that a trader has collateral that is above the *collateral release level* the protocol will automatically release collateral to a trader's general collateral account for the relevant asset. **Whitepaper reference:** 6.1, section "Margin Calculation" In future there can be multiple margin calculator implementations that would be configurable in the market framework. This spec describes one implementation. +## Isolated margin mode + +When in isolated margin mode, the position on the market has an associated margin factor. +The margin factor must be greater than 0 and less than or equal to 1, and also greater than `max(risk factor long, risk factor short)`. + +Isolated margin mode can be enabled by placing an *update margin mode* transaction. +The protocol will attempt to set the funds within the margin account equal to `average entry price * current position * new margin factor`. +This value must be above the `initial margin` for the current position or the transaction will be rejected. + +The default when placing an order with no change to margin mode specified must be to retain the current margin mode of the position. + +### Placing an order + +When submitting, amending, or deleting an order in isolated margin mode and continuous trading, a two step process will be followed: + + 1. First, the core will check whether the order will trade, either fully or in part, immediately upon entry. If so: + 1. If the trade would increase the party's position, the required additional funds as specified in the Increasing Position section will be calculated. The total expected margin balance (current plus new funds) will then be compared to the `maintenance margin` for the expected position, if the margin balance would be less than maintenance, instead reject the order in it's entirety. If the margin will be greater than the maintenance margin their general account will be checked for sufficient funds. + 1. If they have sufficient, that amount will be moved into their margin account and the immediately matching portion of the order will trade. + 1. If they do not have sufficient, the order will be rejected in it's entirety for not meeting margin requirements. + 1. If the trade would decrease the party's position, that portion will trade and margin will be released as in the Decreasing Position section + 1. If the order is not persistent this is the end, if it is persistent any portion of the order which has not traded in step 1 will move to being placed on the order book. + 1. At this point, the party's general account will be checked for margin to cover the additional amount required for the new orders. Each side can be checked individually and the maximum for either side taken into the order margin pool. This calculation must be rerun every time the party's orders or position change. For each side: + 1. Sort all orders by price, starting from first to execute (highest price for buying, lowest price for selling). + 1. If the party currently has a position `x`, assign `0` margin requirement the first-to-trade `x` of volume on the opposite side as this would reduce their position (for example, if a party had a long position `10` and sell orders of `15` at a price of `$100` and `10` at a price of `$150`, the first `10` of the sell order at `$100` would not require any order margin). + 1. For any remaining volume, sum `side margin = limit price * size * margin factor` for each price level, as this is the worst-case trade price of the remaining component. + 2. Take the maximum margin from the two `side margin`s as the margin required in the order margin account. If the party's `general` account does not contain sufficient funds to cover any increases to the `order margin` account to be equal to `side margin` then: + 1. If a newly placed order is being evaluated, that order is `stopped` + 2. If the evaluation is the result of any other position/order update, all open orders are `stopped` and margin re-evaluated. + 3. The `order margin` account is now updated to the new `side margin` value and any new orders can be placed on the book. + +NB: This means that a party's order could partially match, with a trade executed and some funds moved to the margin account with correct leverage whilst the rest of the order is immediately stopped. + +When submitting, amending, or deleting an order in isolated margin mode and an auction is active there is no concept of an order trading immediately on entry, however the case of someone putting in a sell order for a very low price must be handled (as it is likely to execute at a much higher price). To handle this, when in an auction the amount taken into the order margin account should be the larger of either `limit price * size * margin factor` or `max(mark price, indicative uncrossing price) * size * margin factor`. After uncrossing, all remaining open order volume should be rebased back to simply `limit price * size * margin factor` in the order margin account. All other steps are as above. + +Pegged orders are not supported in isolated margin mode. + +### Increasing Position + +When an order trades which increases the position (increasing the absolute value of the trader's position), the target amount to be transferred is calculated as: + +$$ +\text{margin to add} = \text{margin factor} \cdot \text{sum across executed trades}(|\text{trade size}| \cdot \text{trade price}) +$$ + +This will be taken by performing three steps: + + 1. The margin to add as calculated here is compared to the margin which would have been placed into the order margin account for this order, `limit price * total traded size * margin factor`. + 2. If it is equal, this amount is taken from the order margin account and placed into the margin account + 3. If the order traded at a price which means less margin should be taken then the amount `margin to add` above is taken from the order margin account into the margin account and the excess is returned from the order margin account to the general account + +NB: In implementation, for any volume that trades immediately on entry, the additional margin may be transferred directly from the general account to the margin account. + +### Reducing Position + +When an order trades which reduces the trader's current position the amount to be withdrawn from the margin account is determined by the fraction of the position which is being closed. If the entire position is being closed (either as a result of changing sides as below or just moving to `0`) then the entirety of the margin balance will be moved back to the general account. + +However, if only a fraction is being closed then this fraction should also take into account that the entire position's margin may be due to change, since the current trading price may have diverged from the last mark price update. As such the margin released should be calculated by first calculating what the `theoretical account balance` of the margin account would be if the entire pre-existing position (i.e. the position prior to any reduction) were margined at the VWAP of the executed trade, then taking the proportion of that corresponding to the proportion of the position closed. + +$$ +\text{margin to remove} = \text{theoretical account balance} \cdot \frac{|\text{total size of new trades}|}{|\text{entire position prior to trade}|} +$$ + +Concretely, this should resolve to: + +$$ +\text{margin to remove} = (\text{balance before} + \text{position before} \cdot (\text{new trade VWAP} - \text{mark price})) \cdot \frac{|\text{total size of new trades}|}{|\text{entire position prior to trade}|} +$$ + +Note: This requires a calculation of the position's margin at trade time. + +### Changing Sides + +A single order could move a trader from a short to a long position (or vice versa) which either increases or decreases the required margin. When this occurs, the trade should be seen as two steps. One in which the position is reduced to zero (and so margin can be released) followed immediately by one which increases to the new position. (Note that these should *not* be two separate trades, merely modelled as such. The two account movements should occur at once with no other changes allowed between them). + + +### Position Resolution + +Isolated margin acts slightly differently to cross margining mode at times when the position becomes distressed. In the case of an open position dropping below maintenance margin levels active orders will remain active as these are margined separately and will not be cancelled. + +However in addition at each margin calculation update the returned `order margin` from the calculator should be compared to the balance in the `order margin account`. If the margin calculator `order margin > order margin account balance` all orders should be cancelled and the funds returned to the general account (any active positions will remain untouched and active). + +### Setting margin mode + +When isolated margin mode is enabled, amount to be transferred is a fraction of the position's notional size that must be specified by the user when enabling isolated margin mode. + +The transaction to update/change margin mode can be included in a batch transaction in order to allow updates when placing an order. + +When in isolated margin mode, it is possible to request to both increase or decrease the margin factor setting: + +- The protocol will attempt to set the funds within the margin account equal to `average entry price * current position * new margin factor`. This value must be above the `initial margin` for the current position or the transaction will be rejected. + - If this is less than the balance currently in the margin account, the difference will be moved from the margin account to the general account + - If this is larger than the balance currently in the margin account, the difference will be moved from the general account to the margin account. If there are not enough funds to complete this transfer, the transaction will be rejected and no change of margin factor will occur. + +When switching to isolated margin mode, the following steps will be taken: + + 1. For any active position, calculate `average entry price * abs(position) * margin factor`. Calculate the amount of funds which will be added to, or subtracted from, the general account in order to do this. If additional funds must be added which are not available, reject the transaction immediately. + 2. For any active orders, calculate the quantity `limit price * remaining size * margin factor` which needs to be placed in the order margin account. Add this amount to the difference calculated in step 1. If this amount is less than or equal to the amount in the general account, perform the transfers (first move funds into/out of margin account, then move funds into the order margin account). If there are insufficient funds, reject the transaction. + 3. Move account to isolated margin mode on this market + +When switching from isolated margin mode to cross margin mode, the following steps will be taken: + + 1. Any funds in the order margin account will be moved to the margin account. + 2. At this point trading can continue with the account switched to the cross margining account type. If there are excess funds in the margin account they will be freed at the next margin release cycle. + ## Reference Level Explanation The calculator takes as inputs: @@ -107,14 +631,15 @@ The calculator takes as inputs: - `mark price` - `scaling levels` defined in the risk parameters for a market - `quantitative risk factors` -- `market.maxSlippageFactors` which is a 2 dimensional decimal optional market creation parameter with a default of `[0.1,0.1]` i.e. `[10%,10%]` with the following validation: `0 <= market.maxSlippageFactors[1] <= 1 000 000` and `0 <= market.maxSlippageFactors[2] <= 1 000 000`. +- `market.linearSlippageFactor` which is decimal optional market creation parameter with a default of `0.1` i.e. `10%` with the following validation: `0 <= market.linearSlippageFactor <= 1 000 000`. Note: `open_volume` may be fractional, depending on the `Position Decimal Places` specified in the [Market Framework](./0001-MKTF-market_framework.md). If this is the case, it may also be that order/positions sizes and open volume are stored as integers (i.e. int64). In this case, **care must be taken** to ensure that the actual fractional sizes are used when calculating margins. For example, if Position Decimals Places (PDP) = 3, then an open volume of 12345 is actually 12.345 (`12345 / 10^3`). This is important to avoid margins being off by orders of magnitude. It is notable because outside of margin calculations, and display to end users, the integer values can generally be used as-is. Note also that if PDP is negative e.g. PDP = -2 then an integer open volume of 12345 is actually 1234500. -and returns 4 margin requirement levels +and returns 5 margin requirement levels 1. Maintenance margin +1. Order margin 1. Collateral search level 1. Initial margin 1. Collateral release level @@ -125,7 +650,7 @@ and returns 4 margin requirement levels 1. Calculate the maintenance margin for the riskiest short position. 1. Select the maintenance margin that is highest out of steps 1 & 2. 1. Scale this maintenance margin by the margin level scaling factors. -1. Return 4 numbers: the maintenance margin, collateral search level, initial margin and collateral release level. +1. Return 5 numbers: the maintenance margin, order margin, collateral search level, initial margin and collateral release level. ## Calculation of riskiest long and short positions @@ -149,7 +674,7 @@ with ```formula maintenance_margin_long - = max(min(riskiest_long * slippage_per_unit, product.value(market_observable) * (riskiest_long * market.maxSlippageFraction[1] + riskiest_long^2 * market.maxSlippageFraction[2])), 0) + = max(min(riskiest_long * slippage_per_unit, product.value(market_observable) * (riskiest_long * market.linearSlippageFactor)), 0) + max(open_volume, 0) * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ] + buy_orders * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ]`, ``` @@ -177,7 +702,7 @@ where - **Short positions** are exited by the system considering what the volume weighted price of **buying** the size of the open short position (not riskiest short position) on the order book (i.e. by buying from the offers (asks) on the order book). If there is no open short position, the slippage per unit is zero. -If there is zero or insufficient order book volume on the relevant side of the order book to calculate the `exit_price`, then take `slippage_per_unit = +Infinity` which means that `min(slippage_volume * slippage_per_unit, mark_price * (slippage_volume * market.maxSlippageFraction[1] + slippage_volume^2 * market.maxSlippageFraction[2])) = mark_price * (slippage_volume * market.maxSlippageFraction[1] + slippage_volume^2 * market.maxSlippageFraction[2])` above. +If there is zero or insufficient order book volume on the relevant side of the order book to calculate the `exit_price`, then take `slippage_per_unit = +Infinity` which means that `min(slippage_volume * slippage_per_unit, mark_price * (slippage_volume * market.linearSlippageFactor)) = mark_price * (slippage_volume * market.linearSlippageFactor)` above. ### **Step 2** @@ -187,7 +712,7 @@ Else ```formula maintenance_margin_short - = max(min(abs(riskiest short) * slippage_per_unit, mark_price * (abs(riskiest short) * market.maxSlippageFraction[1] + abs(slippage_volume)^2 * market.maxSlippageFraction[2])), 0) + = max(min(abs(riskiest short) * slippage_per_unit, mark_price * (abs(riskiest short) * market.linearSlippageFactor), 0) + abs(min( open_volume, 0 )) * [ quantitative_model.risk_factors_short ] . [ Product.value(market_observable) ] + abs(sell_orders) * [ quantitative_model.risk_factors_short ] . [ Product.value(market_observable) ]` ``` @@ -197,7 +722,35 @@ where meanings of terms in Step 1 apply except for: ### **Step 3** -`maintenance_margin = max ( maintenance_margin_long, maintenance_margin_short)` +If `open_volume > 0`: + +`maintenance_margin = max(min(open_volume * slippage_per_unit, product.value(market_observable) * (open_volume * market.maxSlippageFraction[1] + open_volume^2 * market.maxSlippageFraction[2])), 0) + + open_volume * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ]` +where + +`slippage_per_unit = max(0, Product.value(market_observable) - Product.value(exit_price))` + +If `open_volume < 0`: + +```formula +maintenance_margin + = max(min(abs(open_volume) * slippage_per_unit, mark_price * (abs(open_volume) * market.maxSlippageFraction[1] + abs(slippage_volume)^2 * market.maxSlippageFraction[2])), 0) + + abs(open_volume) * [ quantitative_model.risk_factors_short ] . [ Product.value(market_observable) ]` +``` + +where + +`slippage_per_unit = max(0, Product.value(market_observable) - Product.value(exit_price))` + +If `open_volume == 0`: + +`maintenance_margin = 0` + +### **Step 4** + +`maintenance_margin_with_orders = max(maintenance_margin_long, maintenance_margin_short)` + +`order_margin = maintenance_margin_with_orders - maintenance_margin` ## Margin calculation for auctions @@ -215,7 +768,7 @@ Note that because the order book is empty during auctions we will always end up ## Scaling other margin levels -### **Step 4** +### **Step 5** The other three margin levels are scaled relative to the maintenance margin level, using scaling levels defined in the risk parameters for a market. @@ -250,8 +803,7 @@ bids: [ {volume: 7, price: $108} ] -market.maxSlippageFraction[1] = 0.25 -market.maxSlippageFraction[2] = 0.001 +market.linearSlippageFactor = 0.25 risk_factor_short = 0.11 risk_factor_long = 0.1 @@ -275,11 +827,12 @@ riskiest_short = min( open_volume + sell_orders, 0 ) = min( 10 - 8, 0 ) = 0 slippage_per_unit = max(0, Product.value(previous_mark_price) - Product.value(exit_price)) = max(0, Product.value($144) - Product.value((1*120 + 4*110 + 5*108)/10)) = max(0, 144 - 110) = 34 -maintenance_margin_long =max(min(riskiest_long * slippage_per_unit, product.value(market_observable) * (riskiest_long * market.maxSlippageFraction[1] + riskiest_long^2 * market.maxSlippageFraction[2])), 0) + +maintenance_margin_long =max(min(riskiest_long * slippage_per_unit, product.value(market_observable) * (riskiest_long * market.linearSlippageFactor)), 0) + max(open_volume, 0 ) * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ] + buy_orders * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ] -= max(min(14 * 34, 144*(14 * 0.25 + 14 * 14 * 0.001), 0) + 10 * 0.1 * 144 + 4 * 0.1 * 144 = max(min(476, 532.224), 0) + 10 * 0.1 * 144 + 4 * 0.1 * 144 = 677.6 += max(min(14 * 34, 144*(14 * 0.25), 0) + 10 * 0.1 * 144 + 4 * 0.1 * 144 = max(min(476, 532.224), 0) + 10 * 0.1 * 144 + 4 * 0.1 * 144 = 677.6 # Step 2 @@ -287,10 +840,19 @@ Since riskiest short == 0 then maintenance_margin_short = 0 # Step 3 -maintenance_margin = max ( 677.6, 0) = 677.6 +Since open_volume == 10 + +maintenance_margin = max(min(open_volume * slippage_per_unit, product.value(market_observable) * (open_volume * market.maxSlippageFraction[1] + open_volume^2 * market.maxSlippageFraction[2])), 0) + + open_volume * [ quantitative_model.risk_factors_long ] . [ Product.value(market_observable) ] + = max(min(14 * 34, 144*(14 * 0.25 + 14 * 14 * 0.001), 0) + 10 * 0.1 * 144 = max(min(476, 532.224), 0) + 10 * 0.1 * 144 = 620 # Step 4 +maintenance_margin_with_orders = max ( 677.6, 0) = 677.6 +order_margin = 677.6 - 620 = 57.6 + +# Step 5 + collateral_release_level = 677.6 * collateral_release_scaling_factor = 677.6 * 1.1 initial_margin = 677.6 * initial_margin_scaling_factor = 677.6 * 1.2 search_level = 677.6 * search_level_scaling_factor = 677.6 * 1.3 @@ -303,9 +865,9 @@ Given the following trader positions: | Tables | Open | Buys | Sells | | ------------- |:-------------:| -----:| -----:| -| case-1 | 1 | 1 | -2 -| case-2 | -1 | 2| 0 -| case-3 | 1 | 0 | -2 +| case-1 | 1 | 1 | -2 | +| case-2 | -1 | 2| 0 | +| case-3 | 1 | 0 | -2 | #### *case-1* diff --git a/protocol/0020-APIS-core_api.md b/protocol/0020-APIS-core_api.md index c29a57d09..d129cc0b6 100644 --- a/protocol/0020-APIS-core_api.md +++ b/protocol/0020-APIS-core_api.md @@ -63,7 +63,7 @@ On any Vega node, I can: | Requirement | Acceptance Criteria code | |-----------|:------------------------:| | List all governance proposals via REST & GRPC |0020-APIS-001| -| List all governance proposals by a specified party via REST & GRPC |0020-APIS-002 +| List all governance proposals by a specified party via REST & GRPC |0020-APIS-002 | | Retrieve a specific governance proposals by id via REST & GRPC |0020-APIS-003 | | Retrieve a list of votes via REST & GRPC |0020-APIS-004| | Retrieve the governance stake for a specified party via REST & GRPC |0020-APIS-005| diff --git a/protocol/0025-OCRE-order_submission.md b/protocol/0025-OCRE-order_submission.md index 14be7ce9b..1edee8821 100644 --- a/protocol/0025-OCRE-order_submission.md +++ b/protocol/0025-OCRE-order_submission.md @@ -41,7 +41,6 @@ Self-trading / "wash" trading is allowed on auction uncrossing (i.e. to leave an - Margin will taken before the order is entered into the book (0025-OCRE-002) - If sufficient margin cannot be reserved, the order will have a status of `REJECTED` (0025-OCRE-003) - Fees are charged as per [0029-FEES](./0029-FEES-fees.md). -- In Spot market, holding will taken before the order is entered into the book(0025-OCRE-005) - If sufficient holding cannot be reserved, the order will have a status of `REJECTED` (0025-OCRE-006) ## Future Work diff --git a/protocol/0026-AUCT-auctions.md b/protocol/0026-AUCT-auctions.md index dc190b537..0657af648 100644 --- a/protocol/0026-AUCT-auctions.md +++ b/protocol/0026-AUCT-auctions.md @@ -6,7 +6,8 @@ Auctions are a trading mode that 'collect' orders during an *auction call period In comparison to continuous trading, the auction mode for a market, is a state of the orderbook where each order placed is just sitting on the book, for a given period of time or until some requirements are met (called `call period`), then the matching orders are uncrossed. -They are mostly useful in less liquid markets, or in specific scenarios where a price must be determined, i.e. at opening of a market, when a potentially excessively large price move might occur (price monitoring) or when liquidity needs to be sourced and aggregated (liquidity monitoring). In traditional markets (where markets open and close every day) we can run an open and closing auction for the price to stabilise at both ends. +They are mostly useful in less liquid markets, or in specific scenarios where a price must be determined, i.e. at opening of a market, when a potentially excessively large price move might occur (price monitoring). +In traditional markets (where markets open and close every day) we can run an open and closing auction for the price to stabilise at both ends. ## Reference-level explanation @@ -22,7 +23,7 @@ All auctions have a `min_auction_length` (which is a single network parameter fo - Any auction that would be less than `min_auction_length` seconds (network parameter) should not be started (e.g. if the market is nearing the end of its open period / active trading). This is to prevent auction calls that are too short given the network latency/granularity, so should be some multiple of the worst case expected block time at some confidence level, which is best maintained by governance voting (hence being a network parameter). - a proposal should be rejected if it would require an auction shorter `min_auction_length` -- for price/liquidity monitoring, etc. the auction must last for at least the `min_auction_length` and therefore we can avoid checking other conditions until that length is reached +- for price monitoring, etc. the auction must last for at least the `min_auction_length` and therefore we can avoid checking other conditions until that length is reached - if the parameter is changed it needs to be re-applied to any current auctions, this means that shortening it could trigger an auction ending ### Opening auctions (at creation of the market) @@ -39,7 +40,7 @@ The frequent batch auction mode is a trading mode in perpetual auction, meaning e.g: auctions could be set to last 10 minutes, then every 10 minutes the book would uncross, potentially generating trades. -Note that FBAs will still have an opening auction (which must have a duration equal to or greater than the minimum batch auction duration, as well as meeting the minimum opening auction duration. Price and liquidity monitoring will be able to override the trading mode and push the market into longer auctions to resolve the triggering event. +Note that FBAs will still have an opening auction (which must have a duration equal to or greater than the minimum batch auction duration, as well as meeting the minimum opening auction duration. Price monitoring will be able to override the trading mode and push the market into longer auctions to resolve the triggering event. #### Duration of frequent batch auctions @@ -97,7 +98,7 @@ Auction periods may be ended with an uncrossing and the creation of any resultin - the auction call period end time being reached (if such a time is set); or - other functionality (related to the type of auction period) that triggers the end of auction. -Auction periods do not end if the resulting state would immediately cause another auction to occur. Instead the current auction gets extended. For example, if a liquidity monitoring auction would be triggered at the end of an opening auction, then the opening auction continues and the *auction extension trigger* field in the [market data API](./0021-MDAT-market_data_spec.md) is updated to account for the fact that the opening auction has been extended due to insufficient liquidity. +Auction periods do not end if the resulting state would immediately cause another auction to occur. Instead the current auction gets extended. ### Ending when a market is going to enter Trading Terminated status @@ -109,7 +110,7 @@ Functionality that either triggers the end of an auction or delays the auction e - opening auction (market creation): [governance](./0028-GOVE-governance.md) - [price monitoring](./0032-PRIM-price_monitoring.md) -- [liquidity monitoring](./0035-LIQM-liquidity_monitoring.md) + ## First/Naive implementation diff --git a/protocol/0027-ASSP-asset_proposal.md b/protocol/0027-ASSP-asset_proposal.md index 159c5d745..26455df6e 100644 --- a/protocol/0027-ASSP-asset_proposal.md +++ b/protocol/0027-ASSP-asset_proposal.md @@ -167,6 +167,7 @@ This must be an integer strictly greater than `0`. - As a user I can vote for an asset proposal. (0027-ASSP-002) - As a user, original submitter of the asset, I can call the node to get a signature of the asset, so I can send it to the asset bridge, and whitelist the asset. (0027-ASSP-003) - `quantum` is a required parameter (0027-ASSP-004) +- If an update asset proposal passed and it changes `quantum` _only_ then this new value becomes used immediately on enactment. (0027-ASSP-025) ### Node actions diff --git a/protocol/0028-GOVE-governance.md b/protocol/0028-GOVE-governance.md index d1afeadfc..48e372655 100644 --- a/protocol/0028-GOVE-governance.md +++ b/protocol/0028-GOVE-governance.md @@ -239,7 +239,7 @@ A market in Proposed state accepts [liquidity commitments](./0044-LIME-lp_mechan [Market parameters](./0001-MKTF-market_framework.md#market) that may be changed are described in the spec for the Market Framework, and additionally the specs for the Risk Model and Product being used by the market. See the [Market Framework spec](./0001-MKTF-market_framework.md#market) for details on these parameters, including those that cannot be changed and the category of the parameters. -To change any market parameter the proposer submits the same data as to create a market with the desired updates to the fields / structures that should change. +To change any market parameter the proposer submits the same data as to create a market with the desired updates to the fields / structures that should change. Changes for markets in a closed/cancelled/terminated state should not be permitted. Ideally, it should be possible to not repeat things that are not changing or are immutable but we leave this to implementation detail. The following are immutable and cannot be changed: @@ -248,7 +248,6 @@ The following are immutable and cannot be changed: - market decimal places - position decimal places - `settlementAsset` -- name ## 3. Change network parameters @@ -258,7 +257,7 @@ All _change network parameter proposals_ have their validation configured by the ## 4.1 Add a new asset -New [assets](./0040-ASSF-asset_framework.md) can be proposed through the governance system. The procedure is covered in detail in the [asset proposal spec](./0027-ASSP-asset_proposal.md)). +New [assets](./0040-ASSF-asset_framework.md) can be proposed through the governance system. The procedure is covered in detail in the [asset proposal spec](./0027-ASSP-asset_proposal.md). All new asset proposals have their validation configured by the network parameters `governance.proposal.asset.`. ## 4.2 Modify an existing asset @@ -387,7 +386,7 @@ Any type of market (either fixed expiry market or perpetual) can be closed via a A proposal to close a market contains: -1. final settlement price formatted accounting for market's decimal places +1. final settlement price (not required for spot markets) formatted accounting for market's decimal places Once market is closed the process cannot be reversed. Note that this implies that once a governance proposal to close the market has been voted in the market will definitely close at the enactment time of that vote at the latest. While the market is still open it's still possible to submit additional governance votes to close the market, however they'll only have any effect if their enactment date is prior to that of the market closure proposal which has already passed. @@ -453,6 +452,28 @@ Notes: - The categorisation of parameters is liable to change and be added to as the protocol evolves. - As these are themselves network parameters, a set of parameters will control these parameters for the actions that update these parameters (including being self-referential), i.e. the parameter `Governance.UpdateNetwork.GovernanceProposalValidation.MinimumRequiredParticipation` would control the amount of voting participation needed to change these parameters. See the Network Parameters spec. +## Batch Proposals + +A `BatchProposalSubmission` is a top-level proposal type (living at the same level in a `Transaction` object as a standard `ProposalSubmission`) which allows grouping of several individual proposals into a single proposal, ensuring that all changes will pass or fail governance voting together. +The batch proposal is a wrapper containing one `rationale` (i.e. `title` and `description`) field as a standard `ProposalSubmission`, one `closingTimestamp` field and a list of `ProposalSubmission`s which omit certain fields. +The individual `ProposalSubmission` have no `rationale` entry (i.e. no `title` and `description`). +The individual `ProposalSubmission` have no `closingTimestamp` entry. +Any governance proposal can be included in a batch _except_ proposals to add new assets. +For avoidance of doubt: asset _change_ proposals can be included. + +Validation should be applied by the protocol when accepting such a transaction to verify that all proposals within the batch meet their own minimum voting periods (if not transaction is rejected). +The enactment timestamp, is customisable and can be different for each proposal within the batch, as long as the minimum enactment time of each individual proposal within the batch is respected and as long as every `enactmentTimestamp` is greater than or equal to `closingTimestamp` of the entire batch. + +Once submitted, a single voting period should be run in which participants may place a single vote to approve/disapprove of the entire batch. It _must_ _not_ be possible to vote for components in the batch separately. +Once the closing timestamp is reached each individual proposal within the batch is evaluated against the votes received. +If all individual proposals would pass (given the votes received, based on their individual rules on participation, majority, LP-ELS voting etc.) then the entire batch passes. +If even one proposal within the batch would fail then the entire batch fails. + +If the batch passes, each of the component proposals should be enacted at their enactment timestamp exactly as if each had been proposed and passed individually. The enactment order of two proposals in the batch with the same enactment timestamp: they enact in the order they appear in the batch. +In particular asset update proposals may emit signed bundles to be submitted to the bridge on ethereum side (for withdrawal delay / threshold updates). + +The number of individual proposals that can be submitted within a single batch is limited to 250. + ## APIs The core should expose via core APIs: @@ -506,7 +527,6 @@ APIs should also exist for clients to: - As the vega network, if a proposal is accepted and the duration required before change takes effect is reached, the changes are applied (0028-GOVE-008) - New market proposals cannot be created before [`limits.markets.proposeEnabledFrom`](../non-protocol-specs/0003-NP-LIMI-limits_aka_training_wheels.md#network-parameters) is in the past (0028-GOVE-024) -- A market that has been proposed and successfully voted through doesn't leave the opening auction until the `enactment date/time` is reached and until sufficient [liquidity commitment](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction) has been made for the market. Sufficient means that it meets all the criteria set in [liquidity monitoring](./0035-LIQM-liquidity_monitoring.md). (0028-GOVE-025) - A market proposal with a negative or non-integer value supplied for market decimal places gets rejected. (0028-GOVE-061) - A market proposal with position decimal places not in `{-6,...,-1,0,1,2,...,6}` gets rejected. (0028-GOVE-062) @@ -528,10 +548,14 @@ APIs should also exist for clients to: - A market change proposal that's to modify any parameters on a market in `pending` state (i.e. voting has successfully completed and the market is in the opening auction) will be accepted and if it's the enactment time happens to be before the opening auction ends then the proposed modification is enacted. (0028-GOVE-070) - In particular a market change proposal that's to modify the parent market on a market in `pending` state (i.e. voting has successfully completed and the market is in the opening auction) will be accepted and if it's the enactment time happens to be before the opening auction ends then the parent is used (assuming the proposed parent doesn't already have a successor). (0028-GOVE-071) - A market change that's to modify any parameters on a market in `pending` state (i.e. voting has successfully completed on the market creation and the market is in the opening auction) will run voting rules the same as market creation proposals i.e. LPs don't get a vote. (0028-GOVE-072) -- A governance proposal to close a market which doesn't specify the final settlement price gets rejected by the markets which require it. (0028-GOVE-108) +- A market change proposal that aims to modify the market name in any state (apart from closed/terminated) will modify the market name at the time of vote enactment. (0028-GOVE-159) +- A market change proposal that aims to modify the market code in any state (apart from closed/terminated) will modify the market code at the time of vote enactment. (0028-GOVE-166) +- A governance proposal to close a market which doesn't specify the final settlement price gets rejected by the markets which require it (non-spot). (0028-GOVE-108) - When there's already been a market closure governance proposal successfully voted in for a given market, but not yet enacted it is still possible to submit additional market closure governance proposals for that market. If another market closure governance proposal gets voted it and it has an earlier enactment time then it's the final settlement price of that proposal which gets used. (0028-GOVE-110) - Governance vote to suspend a market that's currently in continuous trading mode puts it into auction mode at vote enactment time. The only way to put the market back into continuous trading mode is with a successful governance vote to resume the market. (0028-GOVE-113) -- Governance vote to suspend a market that's currently in auction trading mode keeps it in auction mode at vote enactment time. Even if the trigger that originally put the market into auction mode is no longer violated the market must remain in auction. (0028-GOVE-114) +- Governance vote to suspend a market that's currently in auction trading mode keeps it in auction mode at vote enactment time. Even if the trigger that originally put the market into auction mode is no longer violated the market must remain in auction. Resuming the market will then put the market in the state it was in prior to it being suspended. + - monitoring auction (0028-GOVE-114) + - opening auction (0028-GOVE-167) - Resuming a market with other auction triggers active does not put it out of auction until those triggers allow to do so. (0028-GOVE-115) - A market suspended by the governance vote does not allow trade generation of margin account balance reduction. (0028-GOVE-116) - Verify that a party with 0 balance of the governance token, but with sufficient ELS can submit a market change proposal successfully. (0028-GOVE-117) @@ -669,6 +693,40 @@ It is NOT possible to submit a governance proposal where the source account is t - Using a governance proposal to cancel, attempts to cancel an using an invalid transfer ID will result in a proposal rejection which states the transfer does not exist (0028-GOVE-125) - When a transfer is cancelled vega will produce an event conveying the cancellation to datanode. This will contain a cancellation status and zero transfer amount. No ledger events will be produced.(0028-GOVE-126) + +##### Batch Proposals + +- A batch proposal containing one or more component submissions mixing different proposal types can be submitted and is accepted as a valid proposal. (0028-GOVE-146) + +- A batch proposal submitted with component submissions having different one or more enactment timestamps lower than the closing timestamps will be rejected with an informative error message. (0028-GOVE-148) + +- A batch proposal submitted with component submissions with different enactment timestamps, all of which are valid for the type of change being proposed for that enactment time, will be accepted and move to voting. (0028-GOVE-149) + 1. If this proposal is accepted, each of the components will be enacted at the time of their differing enactment timestamps. (0028-GOVE-145) + +- A batch proposal containing + 1. freeform proposal, + 1. an update asset proposal changing the asset quantum, maximum lifetime deposit and withdrawal delay threshold + 1. a network parameter change, + 1. a market proposal, + 1. a change proposal for another market, + 1. volume discount program, + 1. referral program, + 1. governance transfer, + +can be submitted, voted through and each proposal enacted. +On top of that signed bundles for changing withdrawal delay and threshold on the bridge are emitted (0028-GOVE-160) + +- A batch proposal can be submitted changing the same network parameter twice to two different values with two different enactment timestamps. +The voting to approve the batch happens, the batch passes, both changes are observed at the desired time. (0028-GOVE-161) + +A vote cannot be submitted for an individual component in a batch (0028-GOVE-163) + +When a batch contains two proposals with identical enactment timestamps, they are executed in the order the appear in the batch. For example two network parameter update proposals in the same batch with the same enactment timestamp will both be executed, in the order specified. (0028-GOVE-164) + +- A batch proposal can be submitted changing the same network parameter twice to two different values with the same enactment timestamps. +The voting to approve the batch happens, the batch passes, the value of the proposal appearing later in the batch is observed at the desired time. (0028-GOVE-165) + + ##### Network History - A datanode restored from network history will contain any recurring and one-off transfers created prior to the restore and these can be retrieved via APIs on the new datanode.(0028-GOVE-127) diff --git a/protocol/0031-ETHB-ethereum_bridge_spec.md b/protocol/0031-ETHB-ethereum_bridge_spec.md index fb20caa84..8acda13db 100644 --- a/protocol/0031-ETHB-ethereum_bridge_spec.md +++ b/protocol/0031-ETHB-ethereum_bridge_spec.md @@ -109,7 +109,7 @@ The Ethereum Bridge uses 1 network parameter, `blockchains.ethereumConfig`, a JS | Property | Type | Example value | Description | |------------------|--------| ------------|--------------| -| `chain_id` | String | `"3"` | Ethereum [Chain ID](https://eips.ethereum.org/EIPS/eip-155) to connect to +| `chain_id` | String | `"3"` | Ethereum [Chain ID](https://eips.ethereum.org/EIPS/eip-155) to connect to | | `network_id` | String | `"3"` | Ethereum [Network ID](https://eips.ethereum.org/EIPS/eip-155) to connect to | | `collateral_bridge_contract` | {string, integer} | `{address: "0xCcB517899f714BD1B2f32931fF429aDEdDd02A93", deployment_height: 1}` | The address for a deployed instance of the bridge contract | | `staking_bridge_contract` | {string, integer} | `{address: "0xCcB517899f714BD1B2f32931fF429aDEdDd02A93", deployment_height: 1}` | The addresses to listen to for [staking events](./0059-STKG-simple_staking_and_delegating.md). | diff --git a/protocol/0032-PRIM-price_monitoring.md b/protocol/0032-PRIM-price_monitoring.md index 582e68a6b..8b7e388d4 100644 --- a/protocol/0032-PRIM-price_monitoring.md +++ b/protocol/0032-PRIM-price_monitoring.md @@ -13,7 +13,7 @@ Please see the [auction spec](./0026-AUCT-auctions.md) for auction details. ### Note -Price monitoring likely won't be the only possible trigger of auction period ([liquidity monitoring](./0035-LIQM-liquidity_monitoring.md) or governance action could be the other ones). Thus the framework put in place as part of this spec should be flexible enough to easily accommodate other types of triggers. +Price monitoring likely won't be the only possible trigger of auction period e.g. governance action could be the other ones. Thus the framework put in place as part of this spec should be flexible enough to easily accommodate other types of triggers. Likewise, pre-processing transactions will be needed as part of the [fees spec](./0029-FEES-fees.md), hence it should be implemented in such a way that it's easy to repurpose it. @@ -124,7 +124,7 @@ to the risk model and obtains the range of valid up/down price moves per each of - When market is in price monitoring auction, change of a risk model or any of its parameters doesn't affect the previously calculated auction end time, any remaining price monitoring bounds cannot extend the auction further. Upon uncrossing price monitoring bounds get reset using the updated parameter values. (0032-PRIM-014). - Specifying a non-positive horizon results in an error. (0032-PRIM-015). - Specifying a probability outside the range (0.9,1) results in an error. (0032-PRIM-016). -- Specifying a non-positive auction extension results in an error. (0032-PRIM-017). +- Specifying a non-positive auction extension results in an error. (0032-PRIM-017) - Settlement price outside the current price monitoring bounds does not trigger an auction (0032-PRIM-018) - A network trade (during closeout) with a price outside price monitoring bounds does not trigger an auction. (0032-PRIM-019) - Persistent order causing trade with the price outwith both bands triggers an auction. Initial auction duration is equal to the extension period of the first trigger. Once the initial period ends the auction gets extended by the extension period of the second trigger. No other orders placed during auction, auction terminates with a trade from order that originally triggered the auction. (0032-PRIM-020). diff --git a/protocol/0034-PROB-prob_weighted_liquidity_measure.ipynb b/protocol/0034-PROB-prob_weighted_liquidity_measure.ipynb index 990e3f6ed..48d77670c 100644 --- a/protocol/0034-PROB-prob_weighted_liquidity_measure.ipynb +++ b/protocol/0034-PROB-prob_weighted_liquidity_measure.ipynb @@ -15,7 +15,7 @@ "This gives view of liquidity at one instant of time; we then use exponential weighted average over time to obtain the desired measure.\n", "\n", "## Inputs\n", - "- network parameter `market.liquidity.probabilityOfTrading.tau.scaling` which has to be $\\geq 1$.\n", + "- network parameter `market.liquidity.probabilityOfTrading.tau.scaling` which has to be $>0$.\n", "- risk model for the market but with $\\tau$ (`tau`) replaced with `market.liquidity.probabilityOfTrading.tau.scaling x tau`.\n", "- order book volume\n", "\n", @@ -88,7 +88,7 @@ "\n", "Volume implied by the liquidity provision order is that given by [0034-PROB-liquidity_measure.feature](https://github.com/vegaprotocol/vega/blob/develop/integration/features/verified/0034-PROB-liquidity_measure.feature) The feature test has covered following scenarios:\n", "\n", - "1. Orders are correctly cumulated in order book's total size(0034-PROB-002).;\n", + "1. Orders are correctly cumulated in order book's total size(0034-PROB-002);\n", "\n", "2. Probability of trading decreases away from the mid-price (0034-PROB-005).\n", "\n", diff --git a/protocol/0035-LIQM-liquidity_monitoring.md b/protocol/0035-LIQM-liquidity_monitoring.md deleted file mode 100644 index a2392fc87..000000000 --- a/protocol/0035-LIQM-liquidity_monitoring.md +++ /dev/null @@ -1,76 +0,0 @@ -# Liquidity monitoring - -## Summary - -Liquidity in the market is not only a desirable feature from a trader's point of view, but also an important consideration from the risk-management standpoint. Position of a distressed trader can only be liquidated if there's enough volume on the order book to offload it, otherwise a potentially insolvent party remains part of the market. - -Similarly to [price monitoring](./0032-PRIM-price_monitoring.md), we need to be able to detect when the market liquidity drops below the safe level, launch a "liquidity seeking" auction (in which, due to the [liquidity mechanics](./0044-LIME-lp_mechanics.md), there is an incentive through the ability to set fees, to provide the missing liquidity) and terminate it when the market liquidity level is back at a sufficiently high level. - -Note that as long as all pegs that LP batch orders can peg to exists on the book there is one-to-one correspondence between the total stake committed by liquidity providers (LPs), see [LP mechanics](./0044-LIME-lp_mechanics.md) spec, and the total supplied liquidity. -Indeed - -`lp_liquidity_obligation_in_ccy_volume = market.liquidity.stakeToCcyVolume ⨉ stake`. - -Thus it is sufficient to compare `target_stake` with `total_stake`. -Note that [target stake](./0041-TSTK-target_stake.md) is defined in a separate spec. - - -## Liquidity auction parameters - -**c1** - constant multiple for [target stake](./0041-TSTK-target_stake.md) triggering the commencement of liquidity auction. In this spec it is referred to as `c_1` but in fact it `triggering_ratio` in `LiquidityMonitoringParameters` in market creation or update proposal. - -## Total stake - -`total_stake` is the sum the stake amounts committed by all the LPs in the market (see [LP mechanics](./0044-LIME-lp_mechanics.md)) for how LPs commit stake and what it obliges them to do. - -## Trigger for entering an auction - -The auction is triggered when - -`total_stake < c_1 x target_stake`. - -Here 0 < c1 < 1, to reduce the chance of another auction getting triggered soon after e.g. c1 = 0.7. The parameter c1 is a network parameter. - -### Increasing target stake - -If an incoming order would match orders on the book resulting in trades increasing `target_stake` so that liquidity auction gets triggered then: - -- if the incoming order would stay on the book in auction mode the auction should get triggered preemptively (the order doesn't get matched in market's current trading mode, market switches to auction mode and the incoming order gets added to the book once market is in auction mode). - -### Decreasing supplied stake - -If the [liquidity provision transaction would decrease](./0044-LIME-lp_mechanics.md#liquidity-provider-proposes-to-amend-commitment-amount) `supplied_stake` so that liquidity auction gets triggered then a liquidity auction is triggered the next time conditions for liquidity auctions are evaluated. - -If the `supplied_stake` decreases as a result of a closeout of an insolvent liquidity provider, then closeout should proceed and market should go into liquidity auction the next time conditions for liquidity auctions are evaluated. - -## Trigger for exiting the auction - -We exit if - -`total_stake >= target_stake`. - -During the liquidity monitoring auction new or existing LPs can commit more stake (and hence liquidity) through the special market making transaction and enable this by posting enough margin - see the [liquidity provision mechanics](./0044-LIME-lp_mechanics.md) spec for details. These need to be monitored to see if auction mode can be exited. - -## What happens during the auction? - -The auction proceeds as usual. Please see the [auction spec](./0026-AUCT-auctions.md) for details. - -## Frequency of checking for liquidity auction entry conditions - - Through a sequence of actions which occur with the same timestamp the market may be moved into a state in which a liquidity auction is expected and then back out of said state. Ideally, liquidity auctions should only be entered when the market truly requires one as once entered a minimum auction length (controlled by `market.auction.minimumDuration`) must be observed. Even with a very short a minimum auction length, a market flickering between two states is suboptimal. - - To resolve this, the conditions for entering a liquidity auction should only be checked at the end of each batch of transactions occurring with an identical timestamp (in the current Tendermint implementation this is equivalent to once per block). At the end of each such period the auction conditions should be checked and the market moved into liquidity auction state if the conditions for entering a liquidity auction are satisfied. -The criteria for exiting any auction (liquidity or price monitoring) should be checked only on timestamp change (ie block boundary with Tendermint). This means that a market cannot leave a liquidity auction only to immediately re-enter it at the end of the block. - -A liquidity provider amending LP provision order can reduce their stake even if doing so would mean that at the end of block the system enters liquidity auction. - -## Acceptance Criteria - -1. The scenarios in the feature test [0026-AUCT-auction_interaction.feature](https://github.com/vegaprotocol/vega/blob/develop/core/integration/features/verified/0026-AUCT-auction_interaction.feature) are verified and pass. (0035-LIQM-001) -1. A market which enters a state requiring liquidity auction at the end of a block through increased open interest remains in open trading between entering that state and the end of the block. (0035-LIQM-003) -1. A market which enters a state requiring liquidity auction at the end of a block through decreased total stake (e.g. through LP bankruptcy) remains in open trading between entering that state and the end of the block. (0035-LIQM-004) -1. A market which enters a state requiring liquidity auction through increased open interest during a block but then leaves state again prior to block completion never enters liquidity auction. (0035-LIQM-005) -1. A market which enters a state requiring liquidity auction through reduced current stake (e.g. through LP bankruptcy) during a block but then leaves state again prior to block completion never enters liquidity auction. (0035-LIQM-006) -1. If the Max Open Interest field decreases for a created block to a level such that a liquidity auction which is active at the start of a block can now be exited the block stays in auction within the block but leaves at the end. (0035-LIQM-008) -1. When the market parameter `triggeringRatio` for an existing market is updated via governance, the next time conditions for entering auction are evaluated, the new triggering ratio is applied. (0035-LIQM-010) -1. When proposing a market the supplied triggering ratio value must be >= 0 and <= 1 else the proposal is rejected diff --git a/protocol/0041-TSTK-target_stake.md b/protocol/0041-TSTK-target_stake.md index 40a7cd340..9b471de7f 100644 --- a/protocol/0041-TSTK-target_stake.md +++ b/protocol/0041-TSTK-target_stake.md @@ -5,15 +5,11 @@ This spec outlines how to measure how much stake we want committed to a market relative to what is happening on the market (currently open interest). The target stake is a calculated quantity, utilised by various mechanisms in the protocol: -- If the LPs total committed stake is less than c_1 x `target_stake` we trigger liquidity auction. See [Liquidity Monitoring](./0035-LIQM-liquidity_monitoring.md). Note that there is a one-to-one correspondence between the amount of stake LPs committed and the supplied liquidity. -The parameter c_1 is a market parameter defined in the [liquidity Monitoring](./0035-LIQM-liquidity_monitoring.md) spec. -- It is used to set the fee factor for the LPs: see [Setting fees and rewarding LPs](./0042-LIQF-setting_fees_and_rewarding_lps.md). - ### Definitions / Parameters used - **Open interest**: the volume of all open positions in a given market. - `market.stake.target.timeWindow` is a network parameter providing the default length of window over which we measure open interest (see below). This should be measured in seconds and a typical value is one week i.e. `7 x 24 x 3600` seconds. A market proposal / update can override this by setting `timeWindow` in `liquidityMonitoringParameters.targetStakeParameters`. -- `market.stake.target.scalingFactor` is a network parameter providing the default scaling between liquidity demand estimate based on open interest and target stake. A market proposal / update can override this by setting `scalingFactor` in `liquidityMonitoringParameters.targetStakeParameters`. +- `market.stake.target.scalingFactor` is a network parameter providing the default scaling between liquidity demand estimate based on open interest and target stake. - `risk_factor_short`, `risk_factor_long` are the market risk factors, see [the Quant Risk Models spec](./0018-RSKM-quant_risk_models.ipynb). - `mark_price`, see [mark price](./0009-MRKP-mark_price.md) spec. - `indicative_uncrossing_price`, see [auction](./0026-AUCT-auctions.md) spec. diff --git a/protocol/0042-LIQF-setting_fees_and_rewarding_lps.md b/protocol/0042-LIQF-setting_fees_and_rewarding_lps.md index 18556f624..9e1ec4bb6 100644 --- a/protocol/0042-LIQF-setting_fees_and_rewarding_lps.md +++ b/protocol/0042-LIQF-setting_fees_and_rewarding_lps.md @@ -10,7 +10,11 @@ The aim of this specification is to set out how fees on Vega are set based on co - **Target stake**: as defined in [target stake spec](./0041-TSTK-target_stake.md). The ideal amount of stake LPs would commit to a market. - `market.liquidityProvision.minLpStakeQuantumMultiple`: There is a network wide parameter specifying the minimum LP stake as the `quantum` specified per asset, see [asset framework spec](../protocol/0040-ASSF-asset_framework.md). -## Calculating liquidity fee factor +## Calculating the Liquidity Fee Factor + +There are three methods for setting the liquidity fee factor, with the default method being the 'Marginal Cost method.' The liquidity fee setting mechanism is configured per market as part of the market proposal. + +### Marginal Cost method The [liquidity fee factor](./0029-FEES-fees.md) is an input to the total taker fee that a price taker of a trade pays: @@ -34,7 +38,7 @@ We now find the smallest integer `k` such that `[target stake] < sum from i=1 to Finally, we set the liquidity-fee-factor for this market to be the fee `LP-k-liquidity-fee-factor`. -### Example for fee setting mechanism +#### Example for fee setting mechanism using the marginal cost method In the example below there are 3 liquidity providers all bidding for their chosen fee level. The LP orders they submit are sorted into increasing fee order so that the lowest fee bid is at the top and the highest is at the bottom. The fee level chosen for the market is derived from the liquidity commitment of the market (`target stake`) and the amount of stake committed from each bidder. Vega processes the LP orders from top to bottom by adding up the commitment stake as it goes until it reaches a level greater than or equal to the `target stake`. When that point is reached the fee used is the fee of the last liquidity order processed. @@ -49,6 +53,41 @@ In the example below there are 3 liquidity providers all bidding for their chose 1. If the `target stake = 240` then even putting all the liquidity supplied above does not meet the estimated market liquidity demand and thus we set `k=N` and so the market's liquidity-fee-factor is `LP N fee = LP 3 fee = 3.75%`. 1. Initially (before market opened) the `[target stake]` is by definition zero (it's not possible to have a position on a market that's not opened yet). Hence by default the market's initial liquidity-fee-factor is the lowest liquidity-fee-factor. + +### Stake-weighted-average method for setting the liquidity fee factor + +The liquidity fee factor is set as the weighted average of the liquidity fee factors, with weights assigned based on the supplied stake from each liquidity provider, which can also account for the impact of one supplier's actions on others. + +#### Example for fee setting mechanism using the Stake-weighted-average method + +In the example below there are 3 liquidity providers all bidding for their chosen fee level. The overall liquidity fee factor is the weight-average of their nominations: + +```text +[LP 1 stake = 120 ETH, LP 1 liquidity-fee-factor = 0.5%] +[LP 2 stake = 20 ETH, LP 2 liquidity-fee-factor = 0.75%] +[LP 3 stake = 60 ETH, LP 3 liquidity-fee-factor = 3.75%] + +then + +liquidity-fee-factor = ((120 * 0.5%) + (20 * 0.75%) + (60 * 3.75%)) / (120 + 20 + 60) = 1.5% +``` + +### "Constant Liquidity Fee" Method + +The liquidity fee factor is set to a constant directly as part of the market proposal. + +#### Example for fee setting mechanism using the constant method + +In the example below there are 3 liquidity providers all bidding for their chosen fee level and the overall liquidity fee factor is: + +```text +[LP 1 stake = 120 ETH, LP 1 liquidity-fee-factor = 0.5%] +[LP 2 stake = 20 ETH, LP 2 liquidity-fee-factor = 0.75%] +[LP 3 stake = 60 ETH, LP 3 liquidity-fee-factor = 3.75%] +``` + +but the market was proposed with a constant fee of 0.8% so that is what the liquidity-fee-factor will be. + ### Timing market's liquidity-fee-factor changes Once the market opens (opening auction starts) a clock starts ticking. We calculate the `[target stake]` using [target stake](./0041-TSTK-target_stake.md). The fee is re-evaluated using the mechanism above at the start of each epoch using LPs commitments at start of epoch. @@ -57,7 +96,7 @@ Once the market opens (opening auction starts) a clock starts ticking. We calcul At time of call: -- The `liquidity-fee-factor` for the market. +- The `liquidity-fee-factor` for the market, and the method used to calculate it. - Current liquidity provider commitments and their individually nominated fee factors - [Target stake](./0041-TSTK-target_stake.md) @@ -180,7 +219,9 @@ An existing LP has `average entry valuation 1090.9` and `S=110`. Currently the s At every vega time change calculate the liquidity score for each committed LP. This is done by taking into account all orders they have deployed within the `[min_lp_price,max_lp_price]` [range](./0044-LIME-lp_mechanics.md) and then calculating the volume-weighted [probability of trading](./0034-PROB-prob_weighted_liquidity_measure.ipynb) at each price level - call it instantaneous liquidity score. -For orders outside the tightest price monitoring bounds set probability of trading to 0. + +For orders outside the tightest price monitoring bounds set probability of trading to 0. For orders which have less than 10% [probability of trading], we set the probability to 0 when calculating liquidity score. + Note that parked [pegged orders](./0037-OPEG-pegged_orders.md) and not-yet-triggered [stop orders](./0014-ORDT-order_types.md) are not included. Now calculate the total of the instantaneous liquidity scores obtained for each committed LP: @@ -311,7 +352,14 @@ Each LP further gets a performance bonus: $b_i \times B$ with a transfer type th - Liquidity fee factors are recalculated every time the liquidity demand estimate changes. (0042-LIQF-005) - If a change in the open interest causes the liquidity demand estimate to change, then fee factor is correctly recalculated. (0042-LIQF-006) - If passage of time causes the liquidity demand estimate to change, the fee factor is correctly recalculated. (0042-LIQF-007) - +- A market can be proposed with a choice of liquidity fee settings. These settings can be updated by a subsequent market update proposal. Moreover, the correct fee value and liquidity fee setting method can be read from the data node APIs. Upon proposal enactment the new liquidity method is applied to recalculate the liquidity fee. The tests should be carried out with the following methods: + - Weighted average (0042-LIQF-056) + - Constant fee (0042-LIQF-061) + - Marginal cost (0042-LIQF-062) +- The above example for the liquidity fee when the method is weighted-average results in a fee-factor of 1.5% (0042-LIQF-057) +- The above example for the liquidity fee when the method is constant-fee results in a fee-factor of 0.8% (0042-LIQF-058) +- The above example for the liquidity fee when the method is marginal cost results in a fee-factor of `3.75%` (0042-LIQF-059) +- For the constant-fee method validate that the fee factor can only be between 0 and 1 inclusive (0042-LIQF-060) ### CHANGE OF NETWORK PARAMETERS TESTS diff --git a/protocol/0043-MKTL-market_lifecycle.md b/protocol/0043-MKTL-market_lifecycle.md index 4fd63f259..cd66ebce6 100644 --- a/protocol/0043-MKTL-market_lifecycle.md +++ b/protocol/0043-MKTL-market_lifecycle.md @@ -14,7 +14,6 @@ Markets proposed via [governance proposals](./0028-GOVE-governance.md#1-create-m All markets are proposed without any [liquidity commitment](./0044-LIME-lp_mechanics.md#commit-liquidity-network-transaction). If the proposal is successful the market will go into opening auction at least until the proposed `enactment` date. -However, the market may stay in an opening auction past the proposed `enactment` date until at least on party makes a liquidity commitment that meets criteria for exiting [liquidity auction](./0035-LIQM-liquidity_monitoring.md). ## Market lifecycle statuses @@ -27,7 +26,7 @@ A market can progress through a number of statuses through its life. The overall | Pending | Yes | Opening auction | Governance vote passes/wins | Governance vote (to close) OR enactment date reached | Cancelled | No | No trading | Market triggers cancellation condition | N/A | Active | Yes | Normal trading | Enactment date reached and usual auction exit checks pass | Governance vote (to close) OR trigger for trading terminated for of market -| Suspended | Yes | Exceptional auction | Price monitoring or liquidity monitoring trigger, or product lifecycle trigger | Exit conditions met per monitoring spec. that triggered it, no other monitoring triggered or governance vote if allowed (see below) +| Suspended | Yes | Exceptional auction | Price monitoring trigger, or product lifecycle trigger | Exit conditions met per monitoring spec. that triggered it, no other monitoring triggered or governance vote if allowed (see below) | Closed | No | No trading | Governance vote (to close) | N/A | Trading Terminated | No | No trading | Defined by the product (i.e. from a product parameter, specified in market definition, giving close date/time) | Settlement event commences | Settled | No | No trading | Settlement triggered and completed as defined by product | N/A @@ -107,7 +106,7 @@ Auction period ends when any of the following occur: ### Cancelled A market becomes Cancelled when a Market Proposal is successful and conditions are not met to transition the Market to the Active state during the Pending period, and the trading terminated data source input is triggered, see [data sourcing](./0045-DSRC-data_sourcing.md). -When a market transitions to a cancelled state all orders should be cancelled and collateral returned to respective parties general account for the relevant asset, all LP commitments should be cancelled and their bond returned to the general account for the relevant asset. After `market.liquidity.successorLaunchWindowLength` has elapsed since cancellation any insurance pool balance should get [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. +When a market transitions to a cancelled state all orders should be cancelled and collateral returned to respective parties general account for the relevant asset, all LP commitments should be cancelled and their bond returned to the general account for the relevant asset. After `market.liquidity.successorLaunchWindowLength` has elapsed since cancellation any [insurance pool](./0015-INSR-market_insurance_pool_collateral.md) balance should get transferred into the global insurance pool using the same settlement asset. Once "cancelled" there must be no open positions tracked by the protocol for the market and any open positions must have been closed including returning all margin and other related collateral if necessary and also notifying downstream event consumers that the positions are closed. Specific position related actions may be unnecessary if the cancelled state is being entered from a state in which there cannot possibly have been any open positions. All data sources that are only referenced by this market should be unregistered. @@ -128,12 +127,12 @@ All data sources that are only referenced by this market should be unregistered. ### Active -Once the enactment date is reached and the other conditions specified to exit the Pending state are met, the market becomes Active on conclusion of uncrossing of the opening auction. This status indicates it is trading via its normally configured trading mode according to the market framework (continuous trading, frequent batch auction, RFQ, block only, etc.). The specification for the trading mode should describe which orders are accepted and how trading proceeds. The market will terminate trading according to a product trigger (for futures, if the trading termination date is reached) and can be temporarily suspended automatically by various monitoring systems ([price monitoring](./0032-PRIM-price_monitoring.md), [liquidity monitoring](./0035-LIQM-liquidity_monitoring.md)). The market can also be closed via a governance vote (market parameter update) to change the status to closed [see the governance spec](./0028-GOVE-governance.md). +Once the enactment date is reached and the other conditions specified to exit the Pending state are met, the market becomes Active on conclusion of uncrossing of the opening auction. This status indicates it is trading via its normally configured trading mode according to the market framework (continuous trading, frequent batch auction, RFQ, block only, etc.). The specification for the trading mode should describe which orders are accepted and how trading proceeds. The market will terminate trading according to a product trigger (for futures, if the trading termination date is reached) and can be temporarily suspended automatically by ([price monitoring](./0032-PRIM-price_monitoring.md). The market can also be closed via a governance vote (market parameter update) to change the status to closed [see the governance spec](./0028-GOVE-governance.md). **Entry:** - From Pending: enactment date reached and conditions to transition from Pending state to Active as detailed above are met -- From Suspended: conditions specified in [price monitoring](./0032-PRIM-price_monitoring.md) and [liquidity monitoring](./0035-LIQM-liquidity_monitoring.md) are met for the market to exit the suspended status back to Active. +- From Suspended: conditions specified in [price monitoring](./0032-PRIM-price_monitoring.md) are met for the market to exit the suspended status back to Active. **Exit:** @@ -160,7 +159,7 @@ Suspension currently always operates as an auction call period. Depending on the **Exit:** -- Conditions specified in [price monitoring](./0032-PRIM-price_monitoring.md) and [liquidity monitoring](./0035-LIQM-liquidity_monitoring.md) and the usual [ending of auction checks](./0026-AUCT-auctions.md) pass → Active +- Conditions specified in [price monitoring](./0032-PRIM-price_monitoring.md) and the usual [ending of auction checks](./0026-AUCT-auctions.md) pass → Active - Governance vote to close a market passes → Closed - Market was suspended by governance vote of product lifecycle trigger and a governance vote passes to set the status to ACTIVE → Active @@ -209,7 +208,7 @@ A market moves from this termination state to Settled when enough information ex - During the transition out of this state: - All final settlement cashflows are calculated and applied (settled) - Margins are transferred back to general accounts -- No risk management or price/liquidity monitoring occurs +- No risk management or price occurs ### Settled @@ -219,7 +218,7 @@ All money held in margin accounts after final settlement is returned to traders' [LP fees](0042-LIQF-setting_fees_and_rewarding_lps.md) that have been cumulated but not yet paid out are distributed to the market LPs as per the LP spec. After `market.liquidity.successorLaunchWindowLength` has elapsed since the settlement time -- [Insurance pool funds](./0015-INSR-market_insurance_pool_collateral.md) are redistributed equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. For markets that have a named successor market the insurance pool balance is transferred to the insurance pool of the successor market. +- [Insurance pool funds](./0015-INSR-market_insurance_pool_collateral.md) are transferred (after window for successor creation elapsed) into the global insurance pool using the same settlement asset. For markets that have a named successor market the appropriate fraction of the insurance pool balance is transferred to the insurance pool of the successor market. - The market can be deleted entirely at this point, from a core perspective. **Entry:** @@ -244,7 +243,6 @@ After `market.liquidity.successorLaunchWindowLength` has elapsed since the settl ### Market is proposed but rejected (0043-MKTL-001) - 1. Market `m1` is proposed with an internal trading terminated oracle set for some time in the future. Price monitoring is configured (e.g. like `2668-price-monitoring.feature`). Market state is `proposed`. 1. Parties vote against the market proposal. @@ -266,12 +264,6 @@ Market state is `active`. Market state is `suspended`. 1. Price monitoring auction ends and the market is in continuous trading again. The market state is `active`. -1. Parties cancel orders so that there is no "best static bid" on the order book. -The market enters [liquidity monitoring auction](0035-LIQM-liquidity_monitoring.md). -The market state is `suspended`. -1. A party place bid; this becomes a best static bid. -After the specified time the liquidity auction ends. -The market state is `active`. 1. Make sure that trades happen so that at least two parties have open positions. The mark price is `p`. 1. The time specified at market proposal by the internal time oracle is reached. @@ -284,22 +276,8 @@ The market state is `trading terminated`. Parties that had open positions see settlement cash-flows happen. Margin account balances are transferred to the general account. The market state is `settled`. -After `market.liquidity.successorLaunchWindowLength` has passed since market settlement, any insurance pool balance is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. - -### Lifecycle happy path in Spot market (0043-MKTL-006) +After `market.liquidity.successorLaunchWindowLength` has passed since market settlement, any [insurance pool](./0015-INSR-market_insurance_pool_collateral.md) balance is transferred into the global insurance pool using the same settlement asset. -1. Market `m1` is proposed. Price monitoring is configured (e.g. like `2668-price-monitoring.feature`). -Market state is `proposed`. -The LP bond of the party that proposed the market is transferred from general to bond account. -1. Market `m1` is accepted and enters opening auction. -Market state is `pending`. -1. Parties place orders and at least one trade happens in continuous trading mode. -Market state is `active`. -1. Parties place orders so that a [price monitoring auction is triggered](0032-PRIM-price_monitoring.md). -Market state is `suspended`. -1. Price monitoring auction ends and the market is in continuous trading again. -The market state is `active`. -1. When a new governance proposal for "closing" the Spot market, then market state is `trading terminated`. ### Lifecycle happy path in Perpetual market(0043-MKTL-009) @@ -314,12 +292,6 @@ Market state is `active`. Market state is `suspended`. 1. Price monitoring auction ends and the market is in continuous trading again. The market state is `active`. -1. Parties cancel orders so that there is no "best static bid" on the order book. -The market enters [liquidity monitoring auction](0035-LIQM-liquidity_monitoring.md). -The market state is `suspended`. -1. A party place bid; this becomes a best static bid. -After the specified time the liquidity auction ends. -The market state is `active`. 1. Make sure that trades happen so that at least two parties have open positions. 1. An oracle event arrives which triggers the perpetual market's interim settlement logic, causing cashflow transfers but the market remains open. @@ -328,25 +300,18 @@ The market state is `active`. When this is approved and enacted the market state is `closed`. Parties that had open positions see settlement cash-flows happen to settle positions. Margin account balances are transferred to the general account. -After `market.liquidity.successorLaunchWindowLength` has passed since market settlement, any insurance pool balance is [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. +After `market.liquidity.successorLaunchWindowLength` has passed since market settlement, any [insurance pool](./0015-INSR-market_insurance_pool_collateral.md) balance is transferred into the global insurance pool using the same settlement asset. -### Market never leaves opening auction, trading terminated triggered, market cancelled (0043-MKTL-003) +### Market never leaves opening auction, trading terminated triggered, market cancelled (0043-MKTL-010) 1. A market is proposed, approved by governance process and enters the opening auction (Pending state). 1. Trading terminated data source triggers before the market leaves the opening auction (so market never left Pending state so far). 1. All orders should be cancelled and collateral returned to respective parties general account for the relevant asset. 1. All LP commitments should be cancelled and their bond returned to the general account for the relevant asset. -1. After `market.liquidity.successorLaunchWindowLength` has elapsed since market cancellation, any insurance pool balance should be [redistributed](./0015-INSR-market_insurance_pool_collateral.md) equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. +1. After `market.liquidity.successorLaunchWindowLength` has elapsed since market cancellation, any insurance pool balance should be transferred into the global insurance pool using the same settlement asset. 1. All data sources that are only referenced by that market are unregistered. 1. The market state is set to cancelled. -### Market (Spot) never leaves opening auction, market cancelled by governance proposal(0043-MKTL-007) - -1. A market is proposed, approved by governance process and enters the opening auction (Pending state). -1. Market cancelled before the market leaves the opening auction (so market never left Pending state so far). -1. All orders should be cancelled and holdings returned to respective parties general account for the relevant asset. -1. All LP commitments should be cancelled and their bond returned to the general account for the relevant asset. -1. The market state is set to cancelled. ### Market gets closed via a governance proposal (0043-MKTL-004) @@ -358,14 +323,6 @@ After `market.liquidity.successorLaunchWindowLength` has passed since market set 1. All the funds from market specific accounts get released to appropriate accounts; the insurance pool perhaps after the delay to allow for transfer into a successor market. 1. Market gets deleted. -### Market (Spot) gets closed via a governance proposal (0043-MKTL-008) - -1. Once the governance proposal to close the market gets enacted any auction that the market may be in gets uncrossed and trades get generated. -1. All the other orders are cancelled and no further trades get generated. -1. Any new orders get rejected. -1. Liquidity commitments cannot be modified or cancelled. -1. All the funds from market specific accounts get released to appropriate accounts. -1. Market gets deleted. ### Market gets suspended via a governance proposal diff --git a/protocol/0044-LIME-lp_mechanics.md b/protocol/0044-LIME-lp_mechanics.md index 8ee5da6df..a38ad7b91 100644 --- a/protocol/0044-LIME-lp_mechanics.md +++ b/protocol/0044-LIME-lp_mechanics.md @@ -92,7 +92,7 @@ Liquidity provider bond account: - A liquidity provider can only prompt a transfer of funds to or from this account by (re)submitting the LP commitment transaction: a valid transaction to create, increase, or decrease their commitment to the market. - Transfers to/from this account also occur when it is used for settlement or margin shortfall, when penalties are applied, and if the account is under-collateralised because of these uses and is subsequently topped up to the commitment amount during collateral search (see below) - Collateral withdrawn from this account may only be transferred to either: - - The insurance pool of the market for markets trading on margin (in event of penalties/slashing) + - The insurance pool of the market for markets trading on margin or the network treasury for the asset (for spot markets) (in event of penalties/slashing) - The liquidity provider's margin account or the network's settlement account/other participant's margin accounts (during a margin search and mark to market settlement) in the event that they have zero balance in their general account. - The liquidity provider's general account (in event of liquidity provider reducing their commitment) @@ -160,7 +160,6 @@ Now transfer $(1-\text{market.liquidity.earlyExitPenalty}) \cdot \text{penalty-i Finally update the ELS as per the [ELS calculation](0042-LIQF-setting_fees_and_rewarding_lps.md) using the entire $\text{commitment-variation}_i$ as the `delta`. -Note that as a consequence the market may land in a liquidity auction the next time conditions for liquidity auctions are evaluated (but there is no need to tie the event of LP(s) reducing their commitment to an immediate liquidity auction evaluation). ## Fees @@ -341,14 +340,10 @@ Note: - When a LP increases their commitment then: - It takes effect immediately for the purposes of LP stake supplied to the market - In terms of the liquidity they are expected to supply: this only takes effect from the start of the next epoch - (0044-LIME-050) + (0044-LIME-050). - LP can decrease or cancel their commitment and it will take effect immediately without incurring penalties (0044-LIME-051). - If target stake is 0 then any LP can cancel their commitment without incurring penalties (0044-LIME-053) -- Consider a market in liquidity auction, when a LP increases their commitment it will take effect immediate for the purposes of LP stake supplied to the market. Where LP `supplied stake > target stake` the market will leave liquidity auction when the liquidity auction ends - - In terms of the liquidity they are expected to supply: this only takes effect from the start of the next epoch - (0044-LIME-102) - - For a market that is in continuous trading and a single LP has committed liquidity: - The LP can cancel their commitment at any time (though this may involve incurring a penalty) (0044-LIME-060). - During continuous trading an LP can submit a transaction to decrease commitment but it will only happen at the end of current epoch. (0044-LIME-101) diff --git a/protocol/0051-PROD-product.md b/protocol/0051-PROD-product.md index 4d186b9aa..065647167 100644 --- a/protocol/0051-PROD-product.md +++ b/protocol/0051-PROD-product.md @@ -120,11 +120,11 @@ APIS should be available to: Settlement assets: - A product of any type cannot be created without specifying at least one settlement asset (0051-PROD-001). For product perpetual: (0051-PROD-007) -- The settlement asset or assets must exist at the time when the product is created (0051-PROD-002). For product perpetual: (0051-PROD-008) +- The settlement asset or assets must exist at the time when the product is created (0051-PROD-002).For product perpetual: (0051-PROD-008) Product updates via governance: -- The settlement asset / settlement assets cannot be changed on a product via governance (0051-PROD-003). F for product perpetual: (0051-PROD-009) +- The settlement asset / settlement assets cannot be changed on a product via governance (0051-PROD-003).For product perpetual: (0051-PROD-009) ## See also diff --git a/protocol/0053-PERP-product_builtin_perpetual_future.md b/protocol/0053-PERP-product_builtin_perpetual_future.md index 7323fd116..2dcfa6914 100644 --- a/protocol/0053-PERP-product_builtin_perpetual_future.md +++ b/protocol/0053-PERP-product_builtin_perpetual_future.md @@ -4,9 +4,13 @@ This built-in product provides perpetual futures contracts that are cash-settled Background reading: [1](https://www.paradigm.xyz/2021/05/everlasting-options/#Perpetual_Futures), [2](https://arxiv.org/pdf/2212.06888.pdf). -Perpetual futures are a simple "delta one" product. Mark-to-market settlement occurs with a predefined frequency as per [0003-MTMK-mark_to_market_settlement](0003-MTMK-mark_to_market_settlement.md). Additionally, a settlement using external data is carried out whenever `settlement_schedule` is triggered. Data obtained from the `settlement_data` oracle between two consecutive `settlement_schedule` events is used to calculate the funding payment and exchange cashflows between parties with open positions in the market. +Perpetual futures are a simple "delta one" product. Mark-to-market settlement occurs with a predefined frequency as per [0003-MTMK-mark_to_market_settlement](0003-MTMK-mark_to_market_settlement.md). Additionally, a settlement using external data (funding payment) is carried out whenever `settlement_schedule` is triggered. Data obtained from the `settlement_data` oracle between two consecutive `settlement_schedule` events is used to calculate the funding payment and exchange cashflows between parties with open positions in the market. -Unlike traditional futures contracts, the perpetual futures never expire. Without the settlement at expiry there would be nothing in the fixed-expiry futures to tether the contract price to the underlying spot market it's based on. To assure that the perpetuals market tracks the underlying spot market sufficiently well a periodic cashflow is exchanged based on the relative prices in the two markets. Such payment covering the time period $t_{i-1}$ to $t_i$ takes the basic form $G_i = \frac{1}{t_i-t_{i-1}} \int_{t_{i-1}}^{t_i}(F_u-S_u)du$, where $F_u$ and $S_u$ are respectively: the perpetual futures price and the spot price at time $u$. We choose to use the mark price to approximate $F_u$ and oracle to approximate $S_u$, so this is effectively the difference between the time-weighted average prices (TWAP) of the two. An optional interest rate and clamp function are included in the funding rate calculation, see the [funding payment calculation](#funding-payment-calculation) section for details. +Unlike traditional futures contracts, the perpetual futures never expire. Without the settlement at expiry there would be nothing in the fixed-expiry futures to tether the contract price to the underlying spot market it's based on. To assure that the perpetuals market tracks the underlying spot market sufficiently well a periodic cashflow is exchanged based on the relative prices in the two markets. Such payment covering the time period $t_{i-1}$ to $t_i$ takes the basic form $G_i = \frac{1}{t_i-t_{i-1}} \int_{t_{i-1}}^{t_i}(F_u-S_u)du$, where $F_u$ and $S_u$ are respectively: the perpetual futures price and the spot price at time $u$. +We choose to use either: + +- the mark price of the market to approximate $F_u$ or +- configure the "market price for funding purposes" as part of the market proposal and use this methodology to approximate $F_u$ and oracle to approximate $S_u$, so this is effectively the difference between the time-weighted average prices (TWAP) of the two. An optional interest rate and clamp function are included in the funding rate calculation, see the [funding payment calculation](#funding-payment-calculation) section for details. ## 1. Product parameters @@ -17,6 +21,9 @@ Unlike traditional futures contracts, the perpetual futures never expire. Withou 1. `interest_rate`: a continuously compounded interest rate used in funding rate calculation. 1. `clamp_lower_bound`: a lower bound for the clamp function used as part of the funding rate calculation. 1. `clamp_upper_bound`: an upper bound for the clamp function used as part of the funding rate calculation. +1. `scaling_factor`: optional scaling factor applied to funding payment. +1. `rate_lower_bound`: optional lower bound applied to funding payment such that the resulting funding rate will never be lower than the specified value. +1. `rate_upper_bound`: optional upper bound applied to funding payment such that the resulting funding rate will never be greater than than the specified value. Validation: @@ -24,7 +31,17 @@ Validation: - `interest_rate` in range `[-1,1]`, - `clamp_lower_bound` in range `[-1,1]`, - `clamp_upper_bound` in range `[-1,1]`, -- `clamp_upper_bound` >= `clamp_lower_bound`. +- `scaling_factor` any positive real number, +- `rate_lower_bound` any real number, +- `rate_upper_bound` any real number, +- `clamp_upper_bound` >= `clamp_lower_bound`, +- `rate_upper_bound` >= `rate_lower_bound`. + +When migrating legacy markets the following value should be used: + +- `scaling_factor` = `1.0`, +- `rate_lower_bound` = -`max supported value`, +- `rate_upper_bound` = `max supported value`. ### Example specification @@ -83,62 +100,58 @@ Every time a [mark to market settlement](./0003-MTMK-mark_to_market_settlement.m When the `settlement_schedule` event is received we need to calculate the funding payment. Store the current vega time as `funding_period_end`. -If there are no oracle data points with a timestamp less than `funding_period_end` available then funding payment is skipped and `funding_period_start` gets overwritten with `funding_period_end`. +Skip the funding payment calculation (set payment to `0`) if no spot (external) data has been ingested since market was created, otherwise calculate the funding payment as outlined below. + +#### TWAP calculation -If such points are available then the calculations discussed in the following subsections get executed and funding payments get exchanged. +Same methodology applies to spot (external) and perps (internal). The available prices (spot and perps considered separately of course) should be used to calculate the time weighted average price within the funding period. If no observations at or prior to the funding period start exist then the start of the period used for calculation should be moved forward to the time of the first observation. An observation is assumed to be applying until a subsequent observation is available. Periods spent in auction should be excluded from the calculation. This implies that spot datapoints received during the auction except for the latest one should be disregarded. Please refer to the acceptance criteria for a detailed example. -#### TWAP spot price calculation +Calculation of the TWAP is carried out by maintaining the following variables: `numerator`, `denominator`, `previous_price` and `previous_time`. It's also assumed that a function `current_time()` is available which returns the current Vega time, and a function `in_auction()` exists which returns `true` if the market being considered is currently in auction and `false` otherwise. +The variables are maintained as follows. -Traverse all the available oracle data point tuples `(s,t)` and calculate the time-weighted average spot price (`s_twap`) as: +When a new `price` observation arrives: ```go -var previous_point -sum_product := 0 - -for p := range oracle_data_points { - if p.t <= funding_period_start { - previous_point = p - continue - } - if p.t >= funding_period_end { - break - } - if previous_point != nil { - sum_product += previous_point*(p.t-max(funding_period_start,previous_point.t)) - } - previous_point = p +if previous_price != nil && in_auction() { + time_delta = current_time()-previous_time + numerator += previous_price*time_delta + denominator += time_delta } +previous_price = price +previous_time = current_time() +``` + +When the market goes into auction: -sum_product += previous_point.s*(funding_period_end-max(funding_period_start,previous_point.t)) -s_twap = sum_product / (funding_period_end - max(funding_period_start, oracle_data_points[0].t)) +```go +time_delta = current_time()-previous_time +numerator += previous_price*time_delta +denominator += time_delta ``` -Only the oracle data point with largest timestamp that's less than or equal to `funding_period_end` (and any data points with larger timestamps) need to be kept from that point on. +When the market goes out of auction: -#### TWAP mark price calculation +```go +previous_time = current_time() +``` -Traverse all the available internal data point tuples `(f,t)` and calculated the time-weighted average mark price (`f_twap`) as: +When the funding payment cue arrives TWAP gets calculated and returned as: ```go -var previous_point -sum_product := 0 - -for p := range internal_data_points { - if p.t <= funding_period_start { - previous_point = p - continue - } - if previous_point != nil { - sum_product += previous_point*(p.t-max(funding_period_start,previous_point.t)) - } - previous_point = p +if !in_auction() { + time_delta = current_time()-previous_time + numerator += previous_price*time_delta + denominator += time_delta +} +previous_time = current_time +if denominator == 0 { + return 0 } +return numerator / denominator -sum_product += previous_point.f*(funding_period_end-max(funding_period_start,previous_point.t)) -f_twap = sum_product / (funding_period_end - max(funding_period_start, internal_data_points[0].t)) ``` -Only the internal data point with largest timestamp needs to be kept from that point on. +Note that depending on what type of oracle is used for the spot price it may be that the oracle points only become known shortly before or at the funding payment cue time, so the above pseudocode is just an illustration of how these quantities should be calculated and the implementation will need to be able to apply such calculation retrospectively. #### Funding payment calculation @@ -151,6 +164,36 @@ funding_payment = f_twap - s_twap + min(clamp_upper_bound*s_twap,max(clamp_lower where `(1 + delta_t * interest_rate)` is the linearisation of `exp(delta_t*interest_rate)` and `delta_t` is expressed as a year fraction. +Furthermore, if any time was spent in auction during the funding period then the funding payment should be scaled down by the fraction of the period spent outside of auction: + +`period_duration = period_end - period_start` + +```go +funding_payment = (period_duration-time_spent_in_auction)/period_duration * funding_payment +``` + +Please note that this implies no funding payments for periods during which the market has been in auction / suspended for their entire duration. + +If `scaling_factor` is specified set: + +```go +funding_payment = scaling_factor * funding_payment +``` + +If `rate_lower_bound` is specified set: + +```go +funding_payment = max(rate_lower_bound*s_twap, funding_payment) +``` + +If `rate_upper_bound` is specified set: + +```go +funding_payment = min(rate_upper_bound*s_twap, funding_payment) +``` + +Please note that scaling should happen strictly before any of the bounds are applied, i.e. if all 3 parameters are specified then the resulting funding rate is guaranteed to fall within the specified bounds irrespective of how big the scaling factor may be. + #### Funding rate calculation While not needed for calculation of cashflows to be exchanged by market participants, the funding rate is useful for tracking market's relation to the underlying spot market over time. @@ -158,7 +201,7 @@ While not needed for calculation of cashflows to be exchanged by market particip Funding rate should be calculated as: ```go -funding_rate = (f_twap - s_twap) / s_twap +funding_rate = funding_payment / s_twap ``` and emitted as an event. @@ -222,7 +265,6 @@ In both cases the estimates are for a hypothetical position of size 1. 1. [Mark to market settlement](./0003-MTMK-mark_to_market_settlement.md) works correctly with a predefined frequency irrespective of the behaviour of any of the oracles specified for the market. (0053-PERP-005) 1. Receiving an event from the settlement schedule oracle during the opening auction does not cause settlement. (0053-PERP-006) 1. Receiving correctly formatted data from settlement data oracles and settlement schedule oracles during continuous trading results in periodic settlement. (0053-PERP-007) -1. Receiving correctly formatted data from the settlement data and settlement schedule oracles during liquidity monitoring auction results in the exchange of periodic settlement cashflows. Market remains in liquidity monitoring auction until enough additional liquidity gets committed to the market. (0053-PERP-008) 1. Receiving correctly formatted data from the settlement data and settlement schedule oracles during price monitoring auction results in the exchange of periodic settlement cashflows. Market remains in price monitoring auction until its original duration elapses, uncrosses the auction and goes back to continuous trading mode. (0053-PERP-009) 1. When the funding payment is positive the margin levels of parties with long positions are larger than what the basic margin calculations imply. Parties with short positions are not impacted. (0053-PERP-015) 1. When the funding payment is negative the margin levels of parties with short positions are larger than what the basic margin calculations imply. Parties with long positions are not impacted. (0053-PERP-016) @@ -234,3 +276,97 @@ In both cases the estimates are for a hypothetical position of size 1. 1. A perpetual market which is active and has open orders, after checkpoint restart, is in opening auction. All margin accounts are transferred to general accounts. (0053-PERP-022) 1. A perpetual market which is active and has open orders. Wait for a new network history snapshot to be created. Load a new data node from network history. All market data is preserved. (0053-PERP-023) 1. When the funding payment does not coincide with mark to market settlement time, a party has insufficient funds to fully cover their funding payment such that the shortfall amount if $x$ and the balance of market's insurance pool is $\frac{x}{3}$, then the entire insurance pool balance gets used to cover the shortfall and the remaining missing amount $\frac{2x}{3}$ gets dealt with using loss socialisation. (0053-PERP-024) + +1. Assume a market trades steadily generating a stream in mark price observations, but the first spot price observation only arrives during the 4th funding period of that market. Then funding payments for periods 1, 2 and 3 all equal 0. (0053-PERP-025) + +1. Assume the market has been in a long auction so that a funding period has started and ended while the market never went back into continuous trading. In that case the funding payment should be equal to 0 and no transfers should be exchanged. (0053-PERP-026) + +- It is possible to obtain a time series for the price used for “vega side price” of the funding twap from the data node from the time of the market proposal enactment onwards (subject to data node retention policies).(0053-PERP-043) + +1. Assume a 10 minute funding period. Assume a few funding periods have already passed for this market. + +Assume the last known mark price before the start of the period to be `10` and that it gets updated every 2 minutes as follows: +| Time (min) since period start | mark price | +| ----------------------------- | ----------- | +| 1 | 11 | +| 3 | 10 | +| 5 | 9 | +| 7 | 8 | +| 9 | 7 | + +Assume the last known spot price before this funding period is `11`. Then assume the subsequent spot price observations get ingested according to the schedule specified below: +| Time (min) since period start | spot price | +| ----------------------------- | ----------- | +| 1 | 9 | +| 3 | 10 | +| 5 | 12 | +| 6 | 11 | +| 7 | 8 | +| 9 | 14 | + +Then, assuming no auctions during the period we get: +$\text{internal TWAP}= \frac{10\cdot(1-0)+11\cdot(3-1)+10\cdot(5-3)+9\cdot(7-5)+8\cdot(9-7)+7\cdot(10-9)}{10}=9.3$, +$\text{external TWAP}=\frac{11\cdot(1-0)+9\cdot(3-1)+10\cdot(5-3)+12\cdot(6-5)+11\cdot(7-6)+8\cdot(9-7)+14\cdot(10-9)}{10}=10.2$. (0053-PERP-027) + +1. Assume a 10 minute funding period. Assume a few funding periods have already passed for this market. Furthermore, assume that in this period that market is in an auction which starts 5 minutes into the period and ends 7 minutes into the period. Assume `interest_rate`=`clamp_lower_bound`=`clamp_upper_bound`=`0`, `scaling_factor`=`1` and no rate upper or lower bound. + +Assume the last known mark price before the start of the period to be `10` and that it gets updated as follows: +| Time (min) since period start | mark price | +| ----------------------------- | ----------- | +| 1 | 11 | +| 3 | 11 | +| 7 | 9 | +| 8 | 8 | +| 10 | 30 | + +Assume the last known spot price before this funding period is `11`. Then assume the subsequent spot price observations get ingested according to the schedule specified below: +| Time (min) since period start | spot price | +| ----------------------------- | ----------- | +| 1 | 9 | +| 3 | 10 | +| 5 | 30 | +| 6 | 11 | +| 8 | 8 | +| 9 | 14 | + +Then, taking the auction into account we get: +$\text{internal TWAP}=\frac{10\cdot(1-0)+11\cdot(3-1)+11\cdot(5-3)+9\cdot(8-7)+8\cdot(10-8)+30\cdot(10-10)}{8}=9.875$, +$\text{external TWAP}=\frac{11\cdot(1-0)+9\cdot(3-1)+10\cdot(5-3)+11\cdot(8-7)+8\cdot(9-8)+14\cdot(10-9)}{8}=10.25$, +$\text{funding payment}=(10-(7-5))/10 * (9.875 - 10.25) = -0.3$. (0053-PERP-036) + +When $\text{clamp lower bound}=\text{clamp upper bound}=0$, $\text{scaling factor}=2.5$ and the funding period ends with $\text{internal TWAP}=99$, $\text{external TWAP} = 100$ then the resulting funding rate equals $-0.025$. (0053-PERP-029) + +When $\text{clamp lower bound}=\text{clamp upper bound}=0$, $\text{scaling factor}=1$, $\text{rate lower bound}=-0.005$, $\text{rate upper bound}=0.015$ and the funding period ends with $\text{internal TWAP}=99$, $\text{external TWAP} = 100$ then the resulting funding rate equals $-0.005$. (0053-PERP-030) + +When $\text{clamp lower bound}=\text{clamp upper bound}=0$, $\text{scaling factor}=1$, $\text{rate lower bound}=-0.015$, $\text{rate upper bound}=0.005$ and the funding period ends with $\text{internal TWAP}=101$, $\text{external TWAP} = 100$ then the resulting funding rate equals $0.005$. (0053-PERP-031) + +When migrating the market existing prior to introduction of the additional parameters their values get set to: + +- $\text{scaling factor}=1$, +- $\text{rate lower bound}= -\text{max supported value}$, +- $\text{rate upper bound}= \text{max supported value}$ +(0053-PERP-032). + +It is possible to create a perpetual futures market which uses the last traded price algorithm for its mark price but uses "impact volume of notional of 1000 USDT" for the purpose of calculating the TWAP of the market price for funding payments (0053-PERP-033). + +It is possible to create a perpetual futures market which uses an oracle source (same as that used for funding) for the mark price determining the mark-to-market cashflows and that uses "impact volume of notional of 1000 USDT" for the purpose of calculating the TWAP of the market price for funding payments (0053-PERP-034). + +It is possible to create a perpetual futures market which uses an oracle source (same as that used for funding) for the mark price determining the mark-to-market cashflows and that uses "time-weighted trade prices in over `network.markPriceUpdateMaximumFrequency` if these have been updated within the last 30s but falls back onto impact volume of notional of 1000 USDT" for the purpose of calculating the TWAP of the market price for funding payments (0053-NEBULA-035). + +When funding payments are due to the network party they are paid into the market insurance pool (0053-PERP-037). + +When funding payments are due from the network party they are paid from the market insurance pool (0053-PERP-038). + +If a market insurance pool does not have enough funds to cover a funding payment, loss socialisation occurs and the total balances across the network remains constant (0053-PERP-039). + +Assert that the scaling factor is applied before the funding cap is applied (0053-PERP-040). + +Assert all funding payments are correct when a perpetual market is suspended and then terminated via governance. (0053-PERP-041). + +The upper and lower clamp values are being correctly validated as per the [parameters defined in the spec](https://github.com/vegaprotocol/specs/blob/palazzo/protocol/0053-PERP-product_builtin_perpetual_future.md#1-product-parameters). (0053-PERP-042). + +Launch a perpetual futures market which sets `internalCompositePrice` to `Nil` (mark price is used) for the "vega side price" for funding calculation. Submit a market update proposal to change this to a composite price with a configuration which uses the impact notional price from the order book. Observe that the new methodology for funding calculations is applied correctly from enactment onwards. (0053-PERP-044). + +Launch a perpetual futures market which sets `internalCompositePrice` to a configuration which uses the impact notional price from the order book. for the "vega side price" for funding calculation. Submit a market update proposal to change this `Nil` (so that mark price gets used for the vega side price). Observe that the new methodology for funding calculations is applied correctly from enactment onwards. (0053-PERP-045). + +Launch a perpetual futures market which uses the "Last Traded Price" for the "vega side price" for funding calculation. Submit a market update proposal to change this to a composite price with a configuration which uses the impact notional price from the order book. Observe that the new methodology for funding calculations is applied correctly from enactment onwards. (0053-PERP-046). diff --git a/protocol/0056-REWA-rewards_overview.md b/protocol/0056-REWA-rewards_overview.md index 5f2ede06f..52f0ab470 100644 --- a/protocol/0056-REWA-rewards_overview.md +++ b/protocol/0056-REWA-rewards_overview.md @@ -282,6 +282,7 @@ $$s_{i} = \frac{d_{i}}{\sum_{i=1}^{n}d_{i}}$$ ### Funding reward accounts (0056-REWA-001) + Trading reward accounts are defined by a pair: [`payout_asset, dispatch_strategy`]. There are two assets configured on the Vega chain: $VEGA and USDT. @@ -294,6 +295,7 @@ Run for another epoch with no fee generated. Expect no transfer to be made to th ### Funding reward accounts - with markets in scope (0056-REWA-002) + There are two assets configured on the Vega chain: $VEGA and USDT. Setup a recurring transfer of 1000 $VEGA with the following dispatch strategy: asset=`USDT`, metric=`DISPATCH_METRIC_TAKER_FEES_PAID`, markets=[`market1`, `market2`]. @@ -304,6 +306,7 @@ Run for another epoch with no fee generated. Expect no transfer to be made to th ### Distributing fees paid rewards (0056-REWA-010) + #### Rationale 1 A market has 2 reward accounts for the metric, one paying in $VEGA and the other paying in USDC. @@ -351,6 +354,7 @@ At the end of epoch 2: ### Distributing fees paid rewards - unfunded account (0056-REWA-011) + #### Rationale 2 This is identical to [acceptance code `REWA 010`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-fees-paid-rewards-0056-rewa-010) just without funding the corresponding reward account. @@ -369,6 +373,7 @@ At the end of epoch 2 although there was trading in the market `ETHUSD-MAR22`, n ### Distributing fees paid rewards - funded account - no trading activity (0056-REWA-012) + #### Rationale 3 After having an epoch with trading activity, fund the reward account, but have no trading activity and assert that no payout is made. @@ -393,6 +398,7 @@ Looking only at epoch 3 - as no trading activity was done, we expect the reward ### Distributing fees paid rewards - multiple markets (0056-REWA-013) + #### Rationale 4 There are multiple markets, each paying its own reward where due. @@ -432,6 +438,7 @@ The calculation of eligibility is identical to [acceptance code `REWA 010`](http ### Distributing maker fees received rewards (0056-REWA-020) + #### Rationale 5 A market has 2 reward accounts for the metric, one paying in $VEGA and the other paying in USDC. @@ -477,6 +484,7 @@ At the end of epoch `2` `party_0` is paid `120 x 2.8 / (2.79+2.8)` USDC from the ### Distributing maker fees received rewards - unfunded account (0056-REWA-021) + #### Rationale 6 This is identical to [acceptance code `REWA 020`](https://github.com/vegaprotocol/specs/blob/master/protocol/0056-REWA-rewards_overview.md#distributing-maker-fees-received-rewards-0056-rewa-020) just without funding the corresponding reward account. @@ -495,7 +503,6 @@ At the end of epoch 2 although there was trading in the market `ETHUSD-MAR22`, n ### Distributing maker fees received rewards - funded account - no trading activity (0056-REWA-022) - #### Rationale 7 After having an epoch with trading activity, fund the reward account, but have no trading activity and assert that no payout is made. @@ -630,7 +637,6 @@ Looking only at epoch 3 - as no trading activity was done, we expect the reward ### Distributing LP fees received - multiple markets (0056-REWA-033) - #### Rationale 12 There are multiple markets, each paying its own reward where due. @@ -901,6 +907,7 @@ At the end of epoch 3, 10000 VEGA should be distributed split between the `BTCUS ### Updating the network parameter `rewards.marketCreationQuantumMultiple` (0056-REWA-050) + #### Rationale 22 When the network parameter `rewards.marketCreationQuantumMultiple` is changed via governance, the change should take affect @@ -953,6 +960,8 @@ At the end of epoch 2, 10000 VEGA rewards should be distributed to the `ETHUSDT` - If a party is a consensus or standby validator their validator ranking reward metric should be set to their ranking score (0056-REWA-091). - If a party is not a consensus or standby validator their validator ranking reward metric should be set to `0` (0056-REWA-092). +- For a party that is a consensus or standby validator, the [staking requirement](https://github.com/vegaprotocol/specs/blob/palazzo/protocol/0057-TRAN-transfers.md#recurring-transfers-to-reward-accounts) and [notional time-weighted average position requirement](https://github.com/vegaprotocol/specs/blob/palazzo/protocol/0057-TRAN-transfers.md#recurring-transfers-to-reward-accounts) do not apply to their validator ranking metric. (0056-REWA-109). +- A party does not need to meet the staking requirements and notional time-weighted average position set in the recurring transfer for market creation reward metric. (0056-REWA-110). ### Distribution Strategy diff --git a/protocol/0057-TRAN-transfers.md b/protocol/0057-TRAN-transfers.md index 146d2402e..4cffbc22f 100644 --- a/protocol/0057-TRAN-transfers.md +++ b/protocol/0057-TRAN-transfers.md @@ -39,9 +39,11 @@ In order to prevent the abuse of user-initiated transfers as spam attack there w ## Minimum transfer amount -This is controlled by the `transfer.minTransferQuantumMultiple` and quantum specified for the [asset](0040-ASSF-asset_framework.md)). +This is controlled by the `transfer.minTransferQuantumMultiple` and quantum specified for the [asset](0040-ASSF-asset_framework.md). The minimum transfer amount is `transfer.minTransferQuantumMultiple x quantum`. +If a user is transferring funds from a vested account, if their balance (expressed in quantum) is less than the minimum amount, they should be able to transfer the full balance (note, transferring less than the full balance is not permitted). + ## Recurring transfers A party can also setup recurring transfers which will happen at the end of every epoch, before the next epoch begins. @@ -153,13 +155,39 @@ Note: if there is no market with contribution to the reward metric - no transfer ## Fees -A fee is taken from all transfers, and paid out to validators in a similar manner to the existing [infrastructure fees](0061-REWP-pos_rewards.md). For recurring transfers, the fee is charged each time the transfer occurs. +A fee is taken from all transfers (except transfers from a vested account to a general account held by the same key), and paid out to validators in a similar manner to the existing [infrastructure fees](0061-REWP-pos_rewards.md). For recurring transfers, the fee is charged each time the transfer occurs. + +The fee is determined by the `transfer.fee.factor` and is subject to a cap defined by the multiplier `transfer.fee.maxQuantumAmount` as specified in the network parameters, which governs the proportion of each transfer taken as a fee. -The fee is set by the `transfer.fee.factor` [network parameter](#network-parameters) that defines the proportion of each transfer taken as a fee. +As such, the transfer fee value used will be: `min(transfer amount x transfer.fee.factor, transfer.fee.maxQuantumAmount x quantum)`, `quantum` is for asset The fee is taken from the transfer initiator's account immediately on execution, and is taken on top of the total amount transferred. It is [paid in to the infrastructure fee pool](./0029-FEES-fees.md#collecting-and-distributing-fees). Fees are charged in the asset that is being transferred. +Fee are primarily a spam-protection mechanism, so for accounts generating "useful activity" discounts apply. + +### Transfer fee discounts + +Let `D` stand for `transfer.feeDiscountDecayFraction`. This is a network parameter that specifies the how cumulated trading fees decay for the purpose of being used to do transfer-fee-free transfers. Minimum value is `0`, maximum value is any decimal strictly less than `1` and default it `0.5`. +Let `M` stand for network parameter `transfer.feeDiscountMinimumTrackedAmount`. Minimum value is `0`, there is no maximum beyond that dictated by the data type used and the default is `0.001`. + +For each party and for each asset store the an amount which tracks all trading fees paid and received by the party with transfer fees subtracted and the amount decayed as specified below. + +For each key for each asset assume you store a value denoted `c`. +During the epoch `k`: + +- if the party makes a transfer and `f` would be the theoretical fee the party should pay then the fee on the transfer that is actually charged is `-min(f-c,0)`. The system subsequently updates `c <- max(0,c-f)`. + +At the end of epoch `k`: + +1. update `c <- c x D`, i.e. apply the decay factor `D` + +1. update `c <- c + all_trading_fees_for_trades_involved_in`, where `all_trading_fees_for_trades_involved_in` are the cumulated trading fees paid by the aggressive party (taker fees) but also cumulated (with a +sign) the trading fees result from any trade in which the party was involved as the passive party (i.e. their limit order got lifted). + +1. if `c` is less than `M x quantum` (where quantum is the asset quantum) then set `c <- 0`. + +We need appropriate APIs to enable the frontend to display the amount eligible for fee-free transfers / correctly display the fee on any transfer a party is proposing. + ## Proposed command This new functionality requires the introduction of a new command in the transaction API. The payload is as follows: @@ -217,6 +245,9 @@ message CancelTransfer { | `spam.protection.maxUserTransfersPerEpoch` | String (integer) | strictly greater than `0` | `"20"` | The most transfers a use can initiate per epoch | | `transfer.minTransferQuantumMultiple` | String (decimal) | greater than or equal to `0`| `"0.1"` | This, when multiplied by `quantum` (which is specified per asset) determines the minimum transfer amount | | `transfer.fee.factor` | String (decimal) | in `[0.0,1.0]` | `"0.001"` | The proportion of the transfer charged as a fee | +| `transfer.fee.maxQuantumAmount` | String (decimal) | greater than or equal to `0` | `"100"` | The cap of the transfer fee | +| `transfer.feeDiscountMinimumTrackedAmount` | String (decimal) | greater than or equal to `0` | `"0.001"` | The lower bound of transfer fee tracked | +| `transfer.feeDiscountDecayFraction` | String (decimal) | greater than or equal to `0` and strictly less than `1` | `"0.5"` | The speed of cumulated trading fees decay for the purpose of being used to do transfer-fee-free transfers | ## Acceptance criteria @@ -224,22 +255,48 @@ message CancelTransfer { - As a user I can transfer funds from a general account I control to an other party's general account. Such transfer can be immediate or delayed. (0057-TRAN-001) - As a user I **cannot** transfer funds from a general account I control to reward account with a one-off transfer. (0057-TRAN-002) -- As a user I can transfer funds from a general account I control to an locked_for_staking. Such transfer can be immediate or delayed. This functionality is currently not implemented (so don't try to test) (0057-PALAZZO-003). +- As a user I can transfer funds from a general account I control to a locked_for_staking. Such transfer can be immediate or delayed. This functionality is currently not implemented (so don't try to test) (0057-PALAZZO-003). - As a user I can transfer funds from a locked_from_staking account under my control to any party's general_account. Such transfer can be immediate or delayed. This functionality is currently not implemented (so don't try to test) (0057-PALAZZO-004) - As a user I cannot transfer funds from accounts that I do not control. (0057-TRAN-005) - As a user I cannot transfer funds from accounts I own but from the type is not supported: - for accounts created in a futures market, bond and margin (0057-TRAN-006) -- As a user I can do a transfer from any of the valid accounts (I control them and they're a valid source), and fees are taken from the source account when the transfer is executed. (0057-TRAN-007) - - The fee cost is correctly calculated using the network parameter +- As a user I can do a transfer from any of the valid accounts (I control them and they're a valid source), and fees are taken from the source account when the transfer is executed (when `transfer amount x transfer.fee.factor <= transfer.fee.maxQuantumAmount x quantum`). (0057-TRAN-007) + - The fee cost is correctly calculated using the network parameters listed above. + - If I have enough funds to pay transfer and fees, the transfer happens. + - If I do not have enough funds to pay transfer and fees, the transfer is cancelled. + - The fees are being paid into the infrastructure pool. + - The transfer fee discount is correctly applied with network parameter`transfer.feeDiscountDecayFraction`(0057-TRAN-014) + - The fee-free transfer amount is accessible through the API (0057-TRAN-017) +- As a user I can do a transfer from any of the valid accounts (I control them and they're a valid source), and fees are taken from the source account when the transfer is executed (when `transfer amount x transfer.fee.factor > transfer.fee.maxQuantumAmount x quantum`). (0057-TRAN-011) + - The fee cost is correctly calculated using the network parameters listed above. - If I have enough funds to pay transfer and fees, the transfer happens. - If I do not have enough funds to pay transfer and fees, the transfer is cancelled. - - The fees are being paid into the infrastructure pool + - The fees are being paid into the infrastructure pool. + - The transfer fee discount is correctly applied with network parameter `transfer.feeDiscountDecayFraction` (0057-TRAN-015) + - The fee-free transfer amount is accessible through the API (0057-TRAN-018) +- when a party makes a transfer and fee-free discount is `c = 0`, then the full transfer fee amount is paid (0057-TRAN-016) + - The fee cost is correctly calculated using the network parameter `transfer.fee.factor`. + - If I have enough funds to pay transfer and fees, the transfer happens. + - If I do not have enough funds to pay transfer and fees, the transfer is cancelled. + - The fees are being paid into the infrastructure pool. +- when a party paid taker fee `g` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in the next epoch when a party (did not generate any fees) makes a transfer and the theoretical fee the party should pay is `f`, fee-free amount is then `c = 0.9 x g`. If `c > f`, then no transfer fee is paid (0057-TRAN-019) +- when a party made maker fee `g` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in the next epoch when a party (did not generate any fees) makes a transfer and the theoretical fee the party should pay is `f`, fee-free amount is then `c = 0.9 x g`. If `c > f`, then no transfer fee is paid (0057-TRAN-020) +- when a party paid taker fee `g` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in the next epoch when a party (did not generate any fees) makes a transfer and the theoretical fee the party should pay is `f`, fee-free amount is then `c = 0.9 x g`. If `c > f`, then no transfer fee is paid. And a party makes another transfer, and the theoretical fee the party should pay is `f`, then the party is not getting any fee-free discount(0057-TRAN-021) +- when a party made maker fee `g` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in the next epoch when a party (did not generate any fees) makes a transfer and the theoretical fee the party should pay is `f`, fee-free amount is then `c = 0.9 x g`. If `c > f`, then no transfer fee is paid. And a party makes another transfer, and the theoretical fee the party should pay is `f`, then the party is not getting any fee-free discount(0057-TRAN-022) +- when a party paid taker fee `f` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in 3 epochs the fee-free discount amount would be `c = 0.9^3 x f`, when a party makes a transfer and the theoretical fee the party should pay is `f1`, and `f1 <= 0.729 x f`, then no amount is paid for transfer (0057-TRAN-023) +- when a party received maker fee `f` in previous epoch, and `transfer.feeDiscountDecayFraction = 0.9`, then in 3 epochs the fee-free discount amount would be `c = 0.9^3 x f`, when a party makes a transfer and the theoretical fee the party should pay is `f1`, and `f1 <= 0.729 x f`, then no amount is paid for transfer (0057-TRAN-024) +- when a party makes a transfer and `f` would be the theoretical fee the party should pay then the fee on the transfer that is actually charged is `-min(f-c,0)`. The system subsequently updates `c <- max(0,c-f)`. At the end of epoch, update `c <- c x D` and `c <- c + all_trading_fees_for_trades_involved_in`, if `c < M x quantum`(M is `transfer.feeDiscountMinimumTrackedAmount`), then set `c <- 0` (0057-TRAN-027) +- As a user I can do a transfer from a vested account to a general account held by the same key without incurring any fees (0057-TRAN-066). +- If a user transfers funds from their vested account to any valid account other than their general account for that asset, they will incur fees. This includes accounts not owned by the user. (0057-TRAN-069). +- As a user, I **can not** transfer a quantum amount less than `transfer.minTransferQuantumAmount` from any of the valid accounts excluding a vested account (0057-TRAN-067). +- As a user, I **can** transfer a quantum amount less than `transfer.minTransferQuantumAmount` from a vested account if and only if I transfer the full balance (0057-TRAN-068). - As a user, when I initiate a delayed transfer, the funds are taken from my account immediately (0057-TRAN-008) - The funds arrive in the target account when the transaction is processed (i.e. with the correct delay), which is not before the timestamp occurs - A delayed transfer that is invalid (to an invalid account type) is rejected when it is received, and the funds are not taken from the origin account. - The spam protection mechanics prevent me to do more than `spam.protection.maxUserTransfersPerEpoch` transfers per epoch. (0057-TRAN-009) - A delayed one-off transfer cannot be cancelled once set-up. (0057-TRAN-010) - A one-off transfer `to` a non-`000000000...0`, and an account type that a party cannot have, must be rejected (0057-TRAN-059) +- As a user, I can accumulate the fees I collect over an epoch. When I initiate a transfer that incurs a transfer fee, I have the ability to view the amount that is exempt from transfer fees through the API. (0057-TRAN-012) ### Recurring transfer tests @@ -250,15 +307,24 @@ As a user I can create a recurring transfer _which expires after a specified epo - The same amount is transferred every epoch. - In the epoch after the `end epoch`, no transfers are executed. -As a user I can create a recurring transfer _that decreases over time_ (0057-TRAN-051) +As a user I can create a recurring transfer _that decreases over time_ (0057-TRAN-051) when `start amount x transfer.fee.factor <= transfer.fee.maxQuantumAmount x quantum` - I specify a start and end epoch, and a factor of `0.7` - Until the start epoch is reached not transfers are executed -- Once I reach the start epoch transfers happen and the first transfer is for the `start amount`. The fee amount taken from the source account is `start amount x transfer.fee.factor` and transferred to the infrastructure fee account for the asset. +- Once I reach the start epoch transfers happen and the first transfer is for the `start amount`. The fee amount taken from the source account is `min(start amount x transfer.fee.factor, transfer.fee.maxQuantumAmount x quantum)` and transferred to the infrastructure fee account for the asset. - The transfer at end of `start epoch + 1` is `0.7 x start amount` and the fee amount is `0.7 x start amount x transfer.fee.factor`. - The amount transferred every epoch decreases. - After I reach the epoch `?`, no transfers are executed anymore +As a user I can create a recurring transfer _that decreases over time_ (0057-TRAN-065) when `start amount x transfer.fee.factor > transfer.fee.maxQuantumAmount x quantum` + +- I specify a start and end epoch, and a factor of `0.7` +- Until the start epoch is reached not transfers are executed +- Once I reach the start epoch transfers happen and the first transfer is for the `start amount`. The fee amount taken from the source account is `min(start amount x transfer.fee.factor, transfer.fee.maxQuantumAmount x quantum)` and transferred to the infrastructure fee account for the asset. +- The transfer at end of `start epoch + 1` is `0.7 x start amount` and the fee amount is `0.7 x transfer.fee.maxQuantumAmount x quantum`. +- The amount transferred every epoch decreases. +- After I reach the epoch `?`, no transfers are executed anymore + As a user I can create a recurring transfer that recurs forever, with the same balance transferred each time (0057-TRAN-052) - I specify a start and no end epoch, and a factor of `1` @@ -306,12 +372,15 @@ A user's recurring transfer to a reward account does not occur if there are no p - The value of `marketCreationQuantumMultiple` is `10^6` and `quantum` for `USDT` is `1`. - I specify a start and no end epoch, and a factor of 1 to a reward account `ETHUSDT | market creation | $VEGA` - In the first epoch no trading occurs and nothing is transferred to the reward account at the end of the epoch -- In the second epoch, 2 * 10^6 trading occurs, and at the end of the epoch the transfer to the reward account occurs +- In the second epoch, 2 x 10^6 trading occurs, and at the end of the epoch the transfer to the reward account occurs - At the end of the third epoch, no transfer occurs If the network parameter `transfer.fee.factor` is modified, this modification is applied immediately, i.e., transfers are accepted/rejected according to the new parameter. This holds for both increase and decrease. (0057-TRAN-062) +If the network parameter `transfer.fee.maxQuantumAmount` is modified, this modification is applied +immediately, i.e., transfers are accepted/rejected according to the new parameter. This holds for both increase and decrease. (0057-TRAN-064) + If the network parameter `spam.protection.maxUserTransfersPerEpoch` is modified, this modification is applied immediately, i.e., transfers are accepted/rejected according to the new parameter. This holds for both increase and decrease. In the case of a decrease, existing recurring transfers are not cancelled. (0057-TRAN-060) If the network parameter `transfer.minTransferQuantumMultiple` is modified, this modification is applied diff --git a/protocol/0059-STKG-simple_staking_and_delegating.md b/protocol/0059-STKG-simple_staking_and_delegating.md index 3cb481662..f99f68a59 100644 --- a/protocol/0059-STKG-simple_staking_and_delegating.md +++ b/protocol/0059-STKG-simple_staking_and_delegating.md @@ -164,7 +164,7 @@ Another edge case is the following: during the epoch the party had x tokens asso Actual validator score calculation is in [simple scheme for Sweetwater](0061-simple-POS-rewards\ -\ SweetWater.md) and it introduces its own network parameters. -See the [network paramters spec](./0054-NETP-network_parameters.md#current-network-parameters) for a full list of parameters. +See the [network parameters spec](./0054-NETP-network_parameters.md#current-network-parameters) for a full list of parameters. ## Acceptance Criteria diff --git a/protocol/0062-SPAM-spam_protection.md b/protocol/0062-SPAM-spam_protection.md index 57a715b82..fd5b559a6 100644 --- a/protocol/0062-SPAM-spam_protection.md +++ b/protocol/0062-SPAM-spam_protection.md @@ -1,15 +1,5 @@ # Spam protection -At this point, the network cannot reject a transaction based on any data that is not the shared state of the blockchain. This means, it is unavoidable that one spammer can essentially fill a block. - -What the network can do is: - -- remove the offending transactions after the block is scheduled, i.e., not process them -- update the state once a block is finalised and block transactions based on the new state -- delete transactions from every (honest) validator's mempool based on the new state. - -Thus, no matter what the anti-spam policy is, there is a scenario where someone creates a lot of identities and spams one block with each. Therefore, we have to enforce a minimum investment to be allowed to send anything to the Vega network. - ## Governance spam The spam protection enforcement for governance actions require that a public key must have a set minimum amount of tokens to be allowed to issue a proposal or vote on a proposal (`spam.protection.proposal.min.tokens`/`spam.protection.voting.min.tokens`). If the network detects successful spam in spite of this minimum, then the limit can be increased automatically. @@ -20,18 +10,16 @@ The following three policies are also specific to governance actions: - Any governance proposal transaction can be rejected if a party has less than `spam.protection.proposal.min.tokens`. Setting these reasonably high provides some level of protection. - Any qualified voter can vote `spam.protection.max.votes` times per epoch per active proposal (e.g., if it's `3` then one initial vote and 2 follow-on votes to change their mind. -If 3 blocks in a row are filled with spam, i.e., parties send substantially more than 3 votes, let's say 50 votes), then the number of required tokens is doubled, up to a maximum of 1600. - All are network parameters and thus up for discussion/governance vote. A change of parameters takes effect in the epoch following the acceptance of the corresponding proposal. ### Policy Enforcement The policy enforcement mechanism rejects governance messages that do not follow the anti-spam rules. This can happen in two different ways: -- pre-block rejection: A transaction is rejected before it enters the validators' mempool. For Tendermint-internal reasons, this can only happen based on the global state coordinated through the previous block; in particular, it cannot be based on any other transactions received by the validator but not yet put into a block (e.g., only three transactions per party per block). Once a block is scheduled, all validators also test all transactions in their mempool to confirm they are still passing the test, and remove them otherwise. -- post-block-rejection: A transaction has made it into the block, but is rejected before it is passed to the application layer. This mechanism allows for more fine-grained policies than the previous one, but at the price that the offending transaction has already taken up space in the blockchain. +- **pre-block rejection**: A transaction is rejected before it enters the validators' mempool. For Tendermint-internal reasons, this can only happen based on the global state coordinated through the previous block; in particular, it cannot be based on any other transactions received by the validator but not yet put into a block (e.g., only three transactions per party per block). Once a block is scheduled, all validators also test all transactions in their mempool to confirm they are still passing the test, and remove them otherwise. +- **post-block-rejection**: A transaction has made it into the block, but is rejected before it is passed to the application layer. This mechanism allows for more fine-grained policies than the previous one, but at the price that the offending transaction has already taken up space in the blockchain. This is currently not used and only kept in here for reference. -The policies enforced are relatively simple: +The policies enforced are the following thresholds: ```text num_votes = 3 // maximum number of times per epoch a tokenholder van change their vote on an issue @@ -48,11 +36,8 @@ max_batch_size = 15 // maximal number of transactions allowed (for consistency reasons, the prevailing source for all parameter values is the [defaults](https://github.com/vegaprotocol/vega/blob/develop/core/netparams/defaults.go)code file. In case of differences, the information in that file is the valid one). -As (due to Tendermint constraints) it is currently possible to exceed all thresholds within one block, an attacker can always spam one block; to mitigate this, a attacker that does so is temporarily banned. For now, all bans are independent, i.e., a ban due to excessive voting only affects further votes. - -- Any tokenholder with more than `min_voting_tokens` tokens on a public key has `num_votes` voting attempts per epoch and proposal, i.e., they can change their mind `num_votes-1` times in one epoch. This means a transaction is pre-block rejected if there are `num_votes` or more on the same proposal in the blockchain in the same epoch, and post_block rejected if there are `num_votes` or more on the same proposal in the blockchain plus earlier in the current block. -- Any tokenholder that had more than 50% of its governance transactions post-rejected is banned for max (30 seconds, 1/48 of an epoch) or until the next epoch starts, and all of its governance related transactions (but no trading related transactions) are immediately rejected. E.g., if the epoch duration is 1 day, then the ban period is 30 minutes. If however the epoch is 10 seconds, then the ban period is 30 seconds (or until the start of the next epoch). The test for 50% of the governance transactions is repeated once the next governance related transaction is post-rejected, so it is possible for a violating party to get banned quite quickly again; the test is only done in case of a new post-rejection, so the account does not get banned twice just because the 50% quota is still exceeded when the ban ends. The voting counters are unaffected by the ban, so voting again on a proposal that already had the full number of votes in the epoch will lead to a rejection of the new vote; this is now unlikely to trigger a new ban, as this rejection will happen pre-consensus, and thus not affect the 50% rule. -- A proposal can only be issued by a tokenholder with more than `min_proposing_tokens` associated with one public key at the start of the epoch. Also (like above), only `num_proposals` proposals can be made per tokenholder per epoch. For example, every proposal past `num_proposals` in an epoch is rejected by post-block-rejected if the sum of their proposals in past blocks and the ones in the current block exceed `num_proposals`, or pre-block rejected if the sum of proposals already in the blockchain for that epoch equals or exceeds `num_proposals`. This parameter is the same for **all proposals**. There also is a separate parameter to the same end that is enforced in the core. For Sweetwater, both these parameters had the same value, but the spam protection value can be set lower, as the amplification effect of a proposal (i.e., a proposal resulting in a very large number of votes) would also then be covered by the core. +- Any tokenholder with more than `min_voting_tokens` tokens on a public key has `num_votes` voting attempts per epoch and proposal, i.e., they can change their mind `num_votes-1` times in one epoch. This means a transaction is **pre-block rejected** if there are `num_votes` or more on the same proposal in the blockchain in the same epoch. +- A proposal can only be issued by a tokenholder with more than `min_proposing_tokens` associated with one public key at the start of the epoch. Also (like above), only `num_proposals` proposals can be made per tokenholder per epoch. Thus, proposals get **pre-block rejected** if the sum of proposals already in the blockchain for that epoch equals or exceeds `num_proposals`. This parameter is the same for **all proposals**. There also is a separate parameter to the same end that is enforced in the core. For Sweetwater, both these parameters had the same value, but the spam protection value can be set lower, as the amplification effect of a proposal (i.e., a proposal resulting in a very large number of votes) would also then be covered by the core. ### Notes @@ -60,16 +45,6 @@ As (due to Tendermint constraints) it is currently possible to exceed all thresh - Every tokenholder with more than `min_voting_tokens` can spam exactly one block. - There is some likelihood that policies will change. It would thus be good to have a clean separation of policy definition and enforcement, so a change in the policies can be implemented and tested independently of the enforcement code. -### Increasing thresholds - -If on average for the last 10 blocks, more than 30% of all voting and proposal transactions need to be post-rejected, then the network is under spam attack. In this case, the `min_voting_tokens` value is doubled, until it reaches 1600. The threshold is then not increased for another 10 blocks. At the beginning of every epoch, the value of `min_voting_tokens` is reset to its original. - -### Issues - -**It is possible for a tokenholder to deliberately spam the network to block poorer parties from voting.** - -Due to the banning policy this is not doable from one key, but with a sybil attack it can be done. If this ends up being a problem, we can address it by increasing the ban-time. - ## Withdrawal spam As unclaimed withdrawals do not automatically expire, an attacker could generate a large number of messages as well as an ever-growing data structure through [withdrawal requests](0030-ETHM-multisig_control_spec.md). @@ -88,13 +63,26 @@ The [on-chain referral program](./0083-RFPR-on_chain_referral_program.md) adds t - `UpdateReferralSet` - `ApplyReferralCode` -To avoid spamming of `CreateReferralSet` and `UpdateReferralSet` transactions, a party must meet the staked governance tokens ($VEGA) threshold set by the network parameter `referralProgram.minStakedVegaTokens`. A party who does not meet this requirement should have any transactions of the aforementioned types pre-block rejected. +To avoid spamming of `CreateReferralSet` and `UpdateReferralSet` transactions, a party must meet the staked governance tokens ($VEGA) threshold set by the network parameter `referralProgram.minStakedVegaTokens`. A party also must meet the balance holding threshold set by the network parameter `spam.protection.referralProgram.min.funds`. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. A party who does not meet this requirements should have any transactions of the aforementioned types pre-block rejected. + +To avoid spamming of `ApplyReferralCode`, a party must meet the deposited funds threshold set by the network parameter `spam.protection.applyReferral.min.funds`. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. A party who does not meet this requirement should have any transactions of the aforementioned type pre-block rejected. This requirement will be checked against snapshots of account balances taken at a frequency determined by the network parameter `spam.protection.balanceSnapshotFrequency`. This network parameter is a duration (e.g. `5s`, `1m5s`). + +Further, each party is allowed to submit up to `n` transactions per epoch where `n` is controlled by the respective network parameter for that transaction type (`spam.protection.max.CreateReferralSet`, `spam.protection.max.UpdateReferralSet`, `spam.protection.max.ApplyReferralCode`). + +### Party Profile spam + +The [party profile feature](./0088-PPRF-party_profile.md) adds a transaction types which can be submitted with no cost/risk to the party: + +- `UpdatePartyProfile` + +To avoid spamming of `UpdatePartyProfile`, a party must meet the deposited funds threshold set by the network parameter `spam.protection.updatePartyProfile.min.funds`. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. A party who does not meet this requirement should have any transactions of the aforementioned type pre-block rejected. This requirement will be checked against snapshots of account balances taken at a frequency determined by the network parameter `spam.protection.balanceSnapshotFrequency`. This network parameter is a duration (e.g. `5s`, `1m5s`). + +**Note** `spam.protection.updatePartyProfile.min.funds` must be an integer greater than or equal to `0` (and default to `10`). -To avoid spamming of `ApplyReferralCode`, a party must meet the deposited funds threshold set by the network parameter `spam.protection.applyReferral.min.funds`. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. A party who does not meet this requirement should have any transactions of the aforementioned type pre-block rejected. This requirement will be checked against snapshots of account balances taken at a frequency determined by the network parameter `spam.protection.balanceSnapshotFrequency`. This network parameter is a duration (e.g. `5s`, `1m5s`). +Further, each party is allowed to submit up to `n` transactions per epoch where `n` is controlled by the respective network parameter for the transaction type (`spam.protection.max.updatePartyProfile`). -Further, each party is allowed to submit up to `n` transactions per epoch where `n` is controlled by the respective network parameter for that transaction type (`spam.protection.max.CreateReferralSet`, `spam.protection.max.UpdateReferralSet`, `spam.protection.max.ApplyReferralCode`). Any party who submits more than `n` transactions of a single referral transaction type in a single epoch will have all future transactions of that type pre-block rejected. +**Note** `spam.protection.max.updatePartyProfile` must be an integer greater than or equal to `0` (and default to `5`). -Any party who manages to fit more then `n` transactions of a single type into a single block will have their excess transactions post-block rejected. A party who has more than 50% of their transactions post-block rejected will be banned for 1/48th of an epoch, or un till the end of the current epoch, whichever comes first. ### Related topics @@ -116,35 +104,29 @@ More than 360 delegation changes in one epoch (or, respectively, the value of `s - Transactions creating more than `spam.protection.max.proposals` proposals in one epoch are rejected. (0062-SPAM-004) - Transactions submitting votes by parties with less than `spam.protection.voting.min.tokens` of Vega associated are rejected. (0062-SPAM-005) - Transactions submitting a vote more than `spam.protection.max.votes` times on any one proposal are rejected. (0062-SPAM-006) -- Above thresholds are exceeded in one block, leading to a post-block-rejection (0062-SPAM-007) -- If 50% of a parties votes/transactions are post-block-rejected, it is blocked for 4 Epochs and unblocked afterwards again (0062-SPAM-008) -- It is possible for spam transactions to fill a block (0062-SPAM-010) -- Parties that continue spamming are blocked and eventually unblocked again (0062-SPAM-011) - Any rejection due to spam protection is reported to the user upon transaction submission detailing which criteria the key exceeded / not met (0062-SPAM-013) -- If a party is banned for too many voting-rejections, it still can send trading related transactions which are not banned. (0062-SPAM-025) -- If the ban of a party ends because the banning time is up, transactions from that party are no longer rejected (0062-SPAM-015) -- If the ban of a party ends because the epoch ends, transactions from that party are no longer rejected (0062-SPAM-016) -- If a party gets banned, the ban ends due to the epoch ending, and it gets banned again at the beginning of the new epoch, the ban still lasts the entire time (or until the next epoch end), i.e., the ban-expiration timer is reset. (0062-SPAM-017) -- If a party gets banned several times during an epoch, all banns last for the defined time or until the epoch ends (try with at least three banns) (0062-SPAM-018) -- A ban only affects transactions of the type that caused the ban, i.e., a voting ban only affects further votes.(0062-SPAM-019) -- After having been banned for too many votes and unbanned, with the maximum number of votes in that epoch exceeded, any additional votes are rejected without a new ban. (0062-SPAM-020) - Try to create a withdrawal bundle for an amount smaller than defined by `spam.protection.minimumWithdrawalQuantumMultiple x quantum` and verify that it is rejected (0062-SPAM-021) - Try to set `spam.protection.minimumWithdrawalQuantumMultiple` to `0` and verify that the parameter is rejected.(0062-SPAM-022) - Increase `spam.protection.minimumWithdrawalQuantumMultiple` and verify that a withdrawal transaction that would have been valid according to the former parameter value is rejected with the new one. (0062-SPAM-023) - Decrease `spam.protection.minimumWithdrawalQuantumMultiple` and verify that a withdrawal transaction that would have been invalid with the old parameter and is valid with the new value and is accepted.(0062-SPAM-024) - Issue a valid withdrawal bundle. Increase `spam.protection.minimumWithdrawalQuantumMultiple` to a value that would no longer allow the creation of the bundle. Ask for the bundle to be re-issued and verify that it's not rejected. (0062-PALAZZO-001) -- A party staking less than `referralProgram.minStakedVegaTokens` should have any `CreateReferralSet` transactions pre-block rejected (0062-SPAM-026). -- A party staking less than `referralProgram.minStakedVegaTokens` should have any `UpdateReferral` transactions pre-block rejected (0062-SPAM-027). -- Given longer than `spam.protection.balanceSnapshotFrequency` has elapsed since a party deposited or transferred funds, a party who has less then `spam.protection.applyReferral.min.funds` in their accounts should have any `ApplyReferralCode` transactions pre-block rejected. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. (0062-SPAM-028). +- A party staking less than `referralProgram.minStakedVegaTokens` should have any `CreateReferralSet` transactions **pre-block** rejected (0062-SPAM-026). +- A party staking less than `referralProgram.minStakedVegaTokens` should have any `UpdateReferral` transactions **pre-block** rejected (0062-SPAM-027). +- A party holding less than `spam.protection.referralSet.min.funds x quantum` should have any `CreateReferralSet` transactions **pre-block** rejected (0062-SPAM-033). +- A party holding no less than `spam.protection.referralSet.min.funds x quantum` should not have any `CreateReferralSet` transactions **pre-block** rejected (0062-SPAM-035). +- Given longer than `spam.protection.balanceSnapshotFrequency` has elapsed since a party deposited or transferred funds, a party who has less then `spam.protection.applyReferral.min.funds` in their accounts should have any `ApplyReferralCode` transactions **pre-block** rejected. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. (0062-SPAM-028). - A party who has submitted strictly more than `spam.protection.max.CreateReferralSet` `CreateReferralSet` transactions in an epoch should have any future `CreateReferralSet` transactions in that epoch **pre-block** rejected (0062-SPAM-029). -- A party who has submitted more than `spam.protection.max.CreateReferralSet` transactions in the current epoch plus in the current block, should have their transactions submitted in the current block **post-block** rejected (0062-SPAM-032). -- A party who has more than 50% of their `CreateReferralSet` transactions post-block rejected should be banned for 1/48th of an epoch or un till the end of the current epoch (whichever comes first). When banned for the above reason, `CreateReferralSet` transactions should be pre-block rejected (0062-SPAM-033). +- A party who has submitted more than `spam.protection.max.CreateReferralSet` transactions in the current epoch plus in the current block, should have their transactions submitted in the current block **pre-block** rejected (0062-SPAM-032). - A party who has submitted strictly more than `spam.protection.max.updateReferralSet` `UpdateReferralSet` transactions in an epoch should have any future `UpdateReferralSet` transactions in that epoch **pre-block** rejected (0062-SPAM-030). -- A party who has submitted more than `spam.protection.max.updateReferralSet` transactions in the current epoch plus in the current block, should have their transactions submitted in the current block **post-block** rejected (0062-SPAM-034). -- A party who has more than 50% of their `UpdateReferralSet` transactions post-block rejected should be banned for 1/48th of an epoch or un till the end of the current epoch (whichever comes first). When banned for the above reason, `UpdateReferralSet` transactions should be pre-block rejected (0062-SPAM-035). +- A party who has submitted more than `spam.protection.max.updateReferralSet` transactions in the current epoch plus in the current block, should have their transactions submitted in the current block **pre-block** rejected (0062-SPAM-034). - A party who has submitted strictly more than `spam.protection.max.applyReferralCode` `ApplyReferralCode` transactions in an epoch should have any future `ApplyReferralCode` transactions in that epoch **pre-block** rejected (0062-SPAM-031). -- A party who has submitted more than `spam.protection.max.applyReferralCode` transactions in the current epoch plus in the current block, should have their transactions submitted in the current block **post-block** rejected (0062-SPAM-036). -- A party who has more than 50% of their `ApplyReferralCode` transactions post-block rejected should be banned for 1/48th of an epoch or un till the end of the current epoch (whichever comes first). When banned for the above reason, `ApplyReferralCode` transactions should be pre-block rejected (0062-SPAM-037). +- A party who has submitted more than `spam.protection.max.applyReferralCode` transactions in the current epoch plus in the current block, should have their transactions submitted in the current block **pre-block** rejected (0062-SPAM-036). +- Given longer than `spam.protection.balanceSnapshotFrequency` has elapsed since a party deposited or transferred funds, a party who has less then `spam.protection.updatePartyProfile.min.funds` in their accounts should have any `UpdatePartyProfile` transactions **pre-block** rejected. All assets count towards this threshold and balances should be scaled appropriately by the assets quantum. (0062-SPAM-037). +- A party holding 2 assets, and each of the assets are less than `spam.protection.referralSet.min.funds` x quantum, but the sum of the 2 assets are greater than `spam.protection.referralSet.min.funds` x quantum, then the party should not have any `UpdateReferralSettransactions` pre-block rejected (0062-SPAM-042). +- A party holding less than `spam.protection.referralProgram.min.funds` x quantum should have any `UpdateReferralSettransactions` pre-block rejected (0062-SPAM-040). +- A party holding no less than `spam.protection.referralSet.min.funds` x quantum and staking no less than `referralProgram.minStakedVegaTokens` should not have any `UpdateReferralSettransactions` pre-block rejected (0062-SPAM-041). +- A party who has submitted strictly more than `spam.protection.max.updatePartyProfile` `UpdatePartyProfile` transactions in an epoch should have any future `UpdatePartyProfile` transactions in that epoch **pre-block** rejected (0062-SPAM-038). +- A party who has submitted more than `spam.protection.max.updatePartyProfile` `UpdatePartyProfile` transactions in the current epoch plus in the current block, should have their `UpdatePartyProfile` transactions submitted in the current block **pre-block** rejected (0062-SPAM-039). > **Note**: If other governance functionality (beyond delegation-changes, votes, and proposals) are added, the spec and its acceptance criteria need to be augmented accordingly. This issue will be fixed in a follow up version. diff --git a/protocol/0065-FTCO-floating_point_consensus.md b/protocol/0065-FTCO-floating_point_consensus.md index f9f88609e..b21e061fd 100644 --- a/protocol/0065-FTCO-floating_point_consensus.md +++ b/protocol/0065-FTCO-floating_point_consensus.md @@ -98,7 +98,6 @@ This section outlines floating-point quantities `vega` currently relies on: - When the opening auction ends (choose uncrossing price that's different from first indicative uncrossing price) price monitoring bounds and probability of trading get recalculated. - When the market goes into price monitoring auction the state variables stay the same as prior to its' start, when that auction concludes (choose a price that's not been traded at before) price monitoring bounds and probability of trading get recalculated again and the time-based trigger countdown gets reset. - When the time-based trigger elapses price monitoring bounds and probability of trading get recalculated. - - When the market goes into liquidity monitoring auction the state variables stay the same as prior to its' start, when that auction concludes (choose a price that's not been traded at before) price monitoring bounds and probability of trading get recalculated again and the time-based trigger countdown gets reset. 1. Event announcing diverging values gets emitted (0065-FTCO-004). - For all the state variables nodes submit candidate values that differ by up to half the tolerance. diff --git a/protocol/0068-MATC-matching_engine.md b/protocol/0068-MATC-matching_engine.md index faf8fd03a..9173435cc 100644 --- a/protocol/0068-MATC-matching_engine.md +++ b/protocol/0068-MATC-matching_engine.md @@ -73,7 +73,7 @@ When a [market moves into an auction](./0026-AUCT-auctions.md#upon-entering-auct - All [PEGGED](./0014-ORDT-order_types.md#auction) orders are parked (and have their status set to PARKED). (0068-MATC-026) - All [GFN](./0014-ORDT-order_types.md#time-in-force---validity) orders are cancelled. (0068-MATC-027). -- All [GTC/GTT](./0014-ORDT-order_types.md#time-in-force---validity) orders remain on the book untouched. (0068-MATC-028). +- All [GTC/GTT](./0014-ORDT-order_types.md#time-in-force---validity) orders remain on the book untouched. (0068-MATC-028) When a market [market exits an auction](./0026-AUCT-auctions.md#upon-exiting-auction-mode): diff --git a/protocol/0072-SPPW-spam-protection-PoW.md b/protocol/0072-SPPW-spam-protection-PoW.md index d0db667ef..c251bfac5 100644 --- a/protocol/0072-SPPW-spam-protection-PoW.md +++ b/protocol/0072-SPPW-spam-protection-PoW.md @@ -47,26 +47,13 @@ Furthermore, the validators check that: - The same identifier has not been used for another transaction from a previously committed block. If the same identifier is used for several transactions in the same block, these transactions need to be removed during post-processing, and the initiator blocked as a spammer. - The same block has not been used for more than `spam.pow.numberOfTxPerBlock` transactions by the same party per spam difficulty level (i.e., if `spam.pow.increaseDifficulty` is `= 1`, the same block can be used for more transactions if the PoW accordingly increases in difficulty). -Violations of the latter rules cannot lead to a transaction being removed, as different validators have a different view on this; however, they can be verified post-agreement, and the offending vega-key can be banished for the duration of 1/48 of an Epoch with a minimum duration of 30 seconds. E.g. if the epoch duration is 1 day, then the ban period is 30 minutes. If however the epoch is 10 seconds, then the ban period is 30 seconds; this is measured starting at the blocktime in which the violation occurs, and transactions are allowed again in the first block after. Validators should return a meaningful error message to the wallet to let it know that/why a transaction got rejected. -Linking a transaction to a too old block will not lead to a banishment, but only to a rejection of the offending transaction. - Notes: - We do not require feeding the hash of the actual transaction into the hash function; this allows users to pre-compute the PoW and thus allows them to perform low latency transactions. - As for replay protection, there is a danger that a trader communicates with a slow validator, and thus gets a wrong block number. The safest is to check validators worth > 1/3 of the weight and take the highest block hash. -- Due to Tendermint constraints, a decision if a transaction is to be rejected or not can only be done based on information that is either synchronised through the chain or contained in the transaction itself, but not based on any other transactions in the mempool. Thus, if a client ties too many transactions to the same block or does not execute the increased difficulty properly, we can not stop this pre-agreement, only detect it post-agreement. This is the reason why some violations are punished with banishment rather than prevented. -- In the [0062 spam protection spec](./0062-SPAM-spam_protection.md), we want to do anti-spam before verifying signatures; this order, however, cannot be done if the consequence of spam is banishment. - -Thus, here the order is: -1. check if the account is banished and (if so) ignore the transaction -2. check if the basic PoW with lowest difficulty is done properly -3. verify the signatures -4. put the transaction on the blockchain -5. if the signed transactions violate the conditions, issue the banishment -6. if the signed transactions in a block violate the conditions, remove the offending ones from the block before calling vega [May need discussion] Depending on how things pan out, we may have an issue with the timing; to make sure traders have sufficient time to get the block height needs us to have a large parameter of `spam.pow.numberOfPastBlocks`, which may allow too many transactions. There are ways to fix this (e.g., the block height needs to end with the same bit as the validator ID), but for now we assume this doesn't cause an issue. @@ -89,10 +76,9 @@ All Vega clients that submitted transactions can verify that their transaction h - A message with a missing/wrong PoW is rejected (0072-SPPW-001) - Reusing the same PoW for several messages is detected and the messages are rejected (0072-SPPW-002) -- Linking too many transactions to the same block is detected and leads to a blocking of that account (if the increasing difficulty is turned off) (0072-SPPW-003) -- Linking too many transactions with a low difficulty level to a block is detected and leads to blocking of the account (if increasing difficulty is turned on) (0072-SPPW-004) +- Linking too many transactions to the same block is detected and leads to rejecting the transactions that are too many (if the increasing difficulty is turned off) (0072-SPPW-003) +- Linking too many transactions with a low difficulty level to a block is detected and leads rejecting the transactions that are too many (0072-SPPW-004) - Reusing a transaction identifier in a way that several transactions with the same ID end up in the same block is detected and the transactions are rejected (0072-SPPW-005) -- A blocked account is unblocked after the maximum of 1/48 of an Epoch or 30 seconds. For transactions sent in the meantime, a meaningful error message is returned. (0072-SPPW-006) - PoW attached to a valid transaction will be accepted provided it's using correct chain ID and, at time of submission, the block hash is one of the last `spam.pow.numberOfPastBlocks` blocks. (0072-SPPW-007) - For each transaction less than or equal to `spam.pow.numberOfTxPerBlock` in a block `spam.pow.difficulty` zeros are needed in the proof-of-work (0072-SPPW-008) - For each `spam.pow.numberOfTxPerBlock` sized block of transactions greater than `spam.pow.numberOfTxPerBlock` an additional 0 is required in the proof-of-work (1 additional zero for the first batch, two additional for the second batch etc) (0072-SPPW-009) @@ -123,7 +109,6 @@ All Vega clients that submitted transactions can verify that their transaction h - `Spam.pow.difficulty` is increased and `spam.pow.increaseDifficulty` is increased (0 to 1), and `spam.pow.numberOfTXPerBlock` is increased. - `Spam.pow.difficulty` is decreased and `spam.pow.increaseDifficulty` is increased (0 to 1), and `spam.pow.numberOfTXPerBlock` is increased. - `Spam.pow.difficulty` is increased and `spam.pow.increaseDifficulty` is increased (0 to 1), and `spam.pow.numberOfTXPerBlock` is decreased. - - `Spam.pow.difficulty` is decreased and `spam.pow.increaseDifficulty` is increased (0 to 1), and `spam.pow.numberOfTXPerBlock` is decreased. (0072-SPPW-019) diff --git a/protocol/0073-LIMN-limited_network_life.md b/protocol/0073-LIMN-limited_network_life.md index 45991078f..756469fc3 100644 --- a/protocol/0073-LIMN-limited_network_life.md +++ b/protocol/0073-LIMN-limited_network_life.md @@ -158,24 +158,6 @@ If for `network.checkpoint.timeElapsedBetweenCheckpoints` the value is set to `0 1. The LP party has general account balance in USD of `9000` and bond account balance `1000` on the market `id_xxx`. 1. The other party has no open orders anywhere and general account balance in USD of `other_gen_bal + other_margin_bal`. -#### Test case 3.1.1: Spot market is proposed, accepted, restored (0073-LIMN-102) - -1. There is an asset USD and no asset proposals. -1. There are no markets and no market proposals. -1. There is a party a party called `LP party` with general balance of 10 000 USD. -1. A market is proposed by a party called `LP party` and has enactment date 1 year in the future. The market has id `id_xxx`. -1. `LP party` commits a stake of 1000 USD to `id_xxx`. -1. Other parties vote on the market and the proposal is accepted (passes rules for vote majority and participation). The market has id `id_xxx`. -1. The market is in `pending` state, see [market lifecycle](../protocol/0043-MKTL-market_lifecycle.md). -1. Another party places a limit sell order on the market and has `other_gen_bal`, holding balance `other_hold_bal`. -1. Enough time passes so a checkpoint is created and no party submitted any withdrawal transactions throughout. -1. The network is shut down. -1. The network is restarted with the checkpoint hash from the above checkpoint in genesis. The checkpoint restore transaction is submitted and processed. -1. There is an asset USD. -1. There is a market with `id_xxx` with all the same parameters as the accepted proposal had. -1. The LP party has general account balance in USD of `9000` and bond account balance `1000` on the market `id_xxx`. -1. The other party has no open orders anywhere and general account balance in USD of `other_gen_bal + other_hold_bal`. - #### Test case 3.1.2: Perpetual market is proposed, accepted, restored (0073-LIMN-105) 1. There is an asset USD and no asset proposals. @@ -283,14 +265,6 @@ for product perpetuals:(0073-LIMN- 4. The network is restarted with the checkpoint hash from the above checkpoint in genesis. The checkpoint restore transaction is submitted and processed. 5. That party has a USD general account balance of 200 USD -### Test case 4b: In Spot market, party's Holding Account balance is put in to a General Account balance for that asset after a reset (0073-LIMN-080) - -1. A party has USD general account balance of 100 USD. -2. That party has USD holding account balance of 50 USD. -3. The network is shut down. -4. The network is restarted with the checkpoint hash from the above checkpoint in genesis. The checkpoint restore transaction is submitted and processed. -5. That party has a USD general account balance of 150 USD - ### Test case 5: Add or remove stake during checkpoint restart (0073-LIMN-017) 1. There is a Vega token asset. @@ -350,7 +324,6 @@ for product perpetuals:(0073-LIMN- ### Test case 13: A market with future enactment date can become enacted after being restored from checkpoint (0073-LIMN-028) - 1. There is an asset USD and no asset proposals. 1. There are no markets and no market proposals. 1. There is a party a party called `LP party` with general balance of 10 000 USD. @@ -377,11 +350,11 @@ for product perpetuals:(0073-LIMN- 1. The market is not restored (it doesn't exist in core i.e. it's not possible to submit orders or LP provisions to this market) (0073-LIMN-029). 1. If the market exists in the data node it is marked as settled with no settlement price info (0073-LIMN-030) 1. For parties that had margin balance position on the market this is now in their general account for the asset. (0073-LIMN-031) -1. In Spot market, for parties that had holdings in the holding account on the market this is now in their general account for the asset. (0073-LIMN-084) 1. The LP fees that were not distributed have been transferred to the Vega treasury for the asset. (0073-LIMN-032). -1. The insurance pool balance has been redistributed equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. (0073-LIMN-112) +1. The insurance pool balance has been transferred into the global insurance pool using the same settlement asset. (0073-LIMN-115) 1. The LP bond account balance has been transferred to the party's general account for the asset. (0073-LIMN-034). + ### Test case 15: Market with trading terminated that settled is not restored, collateral moved correctly 1. Propose, enact, trade in the market, submit trading terminated and settlement data, observe final settlement cashflows for at least 2 parties. @@ -389,9 +362,8 @@ for product perpetuals:(0073-LIMN- 1. Restart Vega, load LNL checkpoint. 1. The market is not restored (it doesn't exist in core i.e. it's not possible to submit orders or LP provisions to this market) (0073-LIMN-040). 1. If the market exists in the data node it is marked as settled with correct settlement data. (0073-LIMN-041) -1. For parties that had margin balance position on the market this is now in their general account for the asset. (0073-LIMN-042) -1. In Spot market, for parties that had holdings in their holding accounts on the market this is now in their general account for the asset. (0073-LIMN-088) -1. The insurance pool balance has been redistributed equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. (0073-LIMN-113) +1. For parties that had margin balance position on the market this is now in their general account for the asset. (0073-LIMN-042 +1. The insurance pool balance has been transferred into the global insurance pool using the same settlement asset. (0073-LIMN-116) 1. The LP bond account balance has been transferred to the party's general account for the asset. (0073-LIMN-044). ### Test case 16: Markets can be settled and terminated after restore as proposed @@ -411,9 +383,8 @@ for product perpetuals:(0073-LIMN- 1. The market is not restored (it doesn't exist in core i.e. it's not possible to submit orders or LP provisions to this market) (0073-LIMN-060). 1. If the market exists in the data node it is labelled as `cancelled` (0073-LIMN-061). 1. For parties that had margin balance position on the market this is now in their general account for the asset. (0073-LIMN-062) -1. In Spot market, for parties that had holdings in their holding accounts on the market this is now in their general account for the asset. (0073-LIMN-094) 1. The LP fees that were not distributed have been transferred to the Vega treasury for the asset. (0073-LIMN-063). -1. The insurance pool balance has been redistributed equally between the global insurance pool and the insurance pools of the remaining active markets using the same settlement asset. (0073-LIMN-114) +1. The insurance pool balance has been transferred into the global insurance pool using the same settlement asset. (0073-LIMN-117) 1. The LP bond account balance has been transferred to the party's general account for the asset. (0073-LIMN-065). ### Test case 18: market definition is the same pre and post LNL restore diff --git a/protocol/0074-BTCH-batch-market-instructions.md b/protocol/0074-BTCH-batch-market-instructions.md index 3deffde49..c0413bf07 100644 --- a/protocol/0074-BTCH-batch-market-instructions.md +++ b/protocol/0074-BTCH-batch-market-instructions.md @@ -25,12 +25,13 @@ Overall, building the ability to handle batches of market instructions in a sing - **Cancellations**: this is a list (repeated field) of Cancel Order instructions - **Amendments**: this is a list (repeated field) of Amend Order instructions - **Submissions**: this is a list (repeated field) of Submit Order instructions -- The total number of instructions across all three lists (i.e. sum of the lengths of the lists) must be less than or equal to the current value of the network parameter `network.spam_protection.max.batch.size`. +- Additionally the batch may contain a single transaction to change the current margin mode. If this transaction fails, all later transactions within the batch (cancellations/amendments/submissions) for the market referred to in the update are Stopped for reason MARGIN_MODE_UPDATE_FAILED. +- The total number of instructions across all three lists (i.e. sum of the lengths of the lists) must be less than or equal to the current value of the network parameter `network.spam_protection.max.batch.size`. The margin mode update transaction is not included in this limit. ### Processing a batch - A batch is considered a single transaction, with a single transaction ID and a single timestamp applying to all instructions within it. Each instruction should be given a sub-identifier and index allowing it to be placed sequentially in the transaction (e.g. by consumers of the event stream). These identifiers must be sufficient for a user to determine which instruction within a batch any result (order updates, trades, errors, etc.) relates to. -- The batches must be processed in the order **all cancellations, then all amendments, then all submissions**. This is to prevent gaming the system, and to prevent any order being modified by more than one action in the batch. +- The batches must be processed in the order **all cancellations, then all amendments, any margin mode update, then all submissions**. This is to prevent gaming the system, and to prevent any order being modified by more than one action in the batch. Updating the margin mode after cancellations and amendments allows the party to have control of which orders are outstanding when the margin mode changes. - When processing each list, the instructions within the list must be processed in the order they appear in the list (i.e. in the order prescribed by the submitter). (Notwithstanding that each list is processed in its entirety before moving onto the next list, in the order specified above). - All instructions within each list must be validated as normal **at the time that the instruction is processed**. That is, instructions cannot be pre-validated as a batch. If a prior instruction, would create a state that would cause a later instruction to fail validation, the later instruction must fail validation (and vice verse). If validation fails, that instruction must be skipped and the subsequent instructions must still be processed. Any validation or other errors should be returned, as well as a reference to the instruction to which they relate, in the response. - Any errors encountered in processing an instruction after it passes validation must cause it to be skipped, and the errors, as well as the instruction to which they relate, must be available in the result of the transaction. @@ -60,3 +61,5 @@ After entering or exiting an auction mid-batch, the full batch must be processed - Funds released by cancellations or amendments within the batch should be immediately available for later instructions (0074-BTCH-009). - If an instruction within a batch causes another party to become distressed, position resolution should be attempted before further instructions within the batch are executed (0074-BTCH-010) - Instructions within the same category within a batch should be executed in the order they are received. For example, if two Cancellations instructions are submitted in a batch: [C1, C2], then C1 should be executed before C2. (0074-BTCH-011) +- If the margin mode update transaction fails all transactions in the batch referring to the same market are Stopped. (0074-BTCH-020) +- If the margin mode update transaction fails all transactions in the batch referring to a different market are attempted as usual. (0074-BTCH-021) diff --git a/protocol/0079-TGAP-transaction_gas_and_priority.md b/protocol/0079-TGAP-transaction_gas_and_priority.md index 6e8e6c579..ad00f423c 100644 --- a/protocol/0079-TGAP-transaction_gas_and_priority.md +++ b/protocol/0079-TGAP-transaction_gas_and_priority.md @@ -113,7 +113,6 @@ There are three priority categories: ### Test we don't overfill a block with a market (0079-TGAP-005) - 1. Set `network.transactions.maxgasperblock = 500` and `network.transaction.defaultgas = 1`. 1. Place 2 matching orders, 1 buy order below the matching price and 1 sell order above the matching price. Uncross the opening auction. 1. Place 3 pegged orders with different non-zero offsets. diff --git a/protocol/0081-SUCM-successor_markets.md b/protocol/0081-SUCM-successor_markets.md index 417e417e5..89641c0a3 100644 --- a/protocol/0081-SUCM-successor_markets.md +++ b/protocol/0081-SUCM-successor_markets.md @@ -11,7 +11,7 @@ For [details of virtual stake calculation see how LPs are rewarded](./0042-LIQF- Many derivative markets would terminate and settle periodically but would be part of a lineage. Think e.g. of a [cash-settled future](./0016-PFUT-product_builtin_future.md) written on the same underlying that settles every three months. Successor markets are a feature that allows for markets to have a lineage, but most importantly allows LPs to keep their virtual stake built up on one market (parent) in the lineage to be transferred to the next one (successor). -Moreover, part of the insurance pool of a parent market can be earmarked for transfer to the successor market instead of being distributed network wide (other markets in same settlement asset, global insurance pool). +Moreover, part of the insurance pool of a parent market can be earmarked for transfer to the successor market instead of being transferred into the global insurance pool. ## Relevant network / market parameters @@ -63,17 +63,20 @@ Market proposal may specify parent market ID. If it does then: - It must also specify insurance pool fraction (0081-SUCM-001) - The product type, settlement asset and margin asset must match between parent and successor; if not proposal is rejected: - futures to perpetuals (0081-SUCM-002) + - perpetuals to spot (0081-SUCM-033) + - spot to futures (0081-SUCM-034) - It is possible for the successor to specify different trading termination and settlement oracle data (0081-SUCM-003). + It is possibly to cancel a [perpetual futures](./0053-PERP-product_builtin_perpetual_future.md) market via governance and propose a new perpetual futures market as a successor of the aforementioned cancelled / to be cancelled with different `market_decimal_places` and `position_decimal_places`; the LPs virtual stakes are carried over (0081-SUCM-015). Two proposals that name the same parent can be submitted. Both can be approved by governance. The proposed market that clears the opening auction first gets a share of the insurance pool, and the virtual stakes get carried over. Once the first market clears the opening auction, the other market is "Rejected," and all assets committed into LP bond accounts will be immediately released. Orders placed into the opening auction will be cancelled, and the assets held to support any party's orders will be released. (0081-SUCM-005). A new market proposal sets parent market Id to a market that has settled. The parent market has non-zero insurance pool balance. If the new market clears the opening auction before `parent settlement time + market.liquidity.successorLaunchWindowLength` then the virtual stakes are carried over and the relevant fraction of the insurance pool is transferred over (0081-SUCM-006). -A new market proposal sets parent market Id to a market that has settled. The parent market has non-zero insurance pool balance. If the new market clears the opening auction after `parent settlement time + market.liquidity.successorLaunchWindowLength` then no virtual stakes are carried over, the successor market is not a successor market anymore, it's just a market like any other, and the insurance pool balance will be distributed equally across the global insurance pool and all markets with the same settlement asset, including those markets which are still in opening auction (0081-SUCM-035) +A new market proposal sets parent market Id to a market that has settled. The parent market has non-zero insurance pool balance. If the new market clears the opening auction after `parent settlement time + market.liquidity.successorLaunchWindowLength` then no virtual stakes are carried over, the successor market is not a successor market anymore, it's just a market like any other, and the insurance pool balance will be transferred into the global insurance pool (0081-SUCM-036) -Successor markets cannot be enacted if the parent market is still in the "proposed" state. Successor market proposals can be submitted when the parent market is still in proposed state. When the voting period for the successor market ends then either: a) the parent market is already enacted in which case the successor market moves from "proposed" in to opening auction/"pending" state. Or the parent market is still in "proposed" state in which case successor market is rejected. (0081-SUCM-008) +Successor markets cannot be enacted if the parent market is still in the "proposed" state. Successor market proposals can be submitted when the parent market is still in proposed state. When the voting period for the successor market ends then either: the parent market is already enacted in which case the successor market moves from "proposed" in to opening auction/"pending" state. Or the parent market is still in "proposed" state in which case successor market is rejected. (0081-SUCM-008) Successor markets which are proposed whilst the parent is also still in a "proposed" state, will be rejected if the parent is rejected. (0081-SUCM-027) @@ -102,7 +105,7 @@ When fetching a market that is part of a "parent / successor chain", we should s ### Snapshots / checkpoints / Protocol Upgrade / Network History -After a LNL checkpoint restart the successor (child) / parent market state is preserved where applicable inc. the LPs ELS (0081-SUCM-016) +After a LNL checkpoint restart the successor (child) / parent market state is preserved where applicable inc. the LPs ELS (0081-SUCM-016) A market which has been settled, but is still inside successor expiry window, is retained in a checkpoint, and can be used by a successor market after restart(0081-SUCM-029) @@ -112,7 +115,7 @@ A market which has been settled, and already has a child which has succeeded it, For a parent and child (explicitly: the child has left opening auction), after a checkpoint restart, parent and child both enter opening auction again. It is not possible to propose a new market which attempts to succeed that parent.(0081-SUCM-032) -After snapshot restart the successor (child) / parent market state is preserved where applicable inc. the LPs ELS (0081-SUCM-017) +After snapshot restart the successor (child) / parent market state is preserved where applicable including the LPs ELS (0081-SUCM-017) A market which has expired before a protocol upgrade is still eligible to be used as a successor market after the upgrade, if it is inside the successor time window (0081-SUCM-025) diff --git a/protocol/0082-ETHD-ethereum-data-source.md b/protocol/0082-ETHD-ethereum-data-source.md index 4cb4bb620..314f96533 100644 --- a/protocol/0082-ETHD-ethereum-data-source.md +++ b/protocol/0082-ETHD-ethereum-data-source.md @@ -150,6 +150,7 @@ Select { 13. Different contracts on different markets - Create 2 markets with ethereum oracle settlement data sources containing different contract addresses and with the *same* settlement keys, but with all conditions and filters the same. Confirm that sending settlement value that passes the market spec filter only settles one market. (0082-ETHD-051) 14. Phase 2 - System needs to emit an error via the TX RESULT event if the data source does NOT emit events in a timely fashion. e.g. if the data source is expected to emit events every 5 minutes and if we do not receive 3 consecutive events , then raise an error via the TX RESULT event (0082-PLAZZO-003) 15. Phase 2 - Define behaviour for missed events / missed scheduled smart contract calls - e.g. if an oracle data source is scheduled to emit events every 10 minutes and we miss 5 events because of protocol upgrade or some other outage - then do we catch up those events or skip those events ? Maybe this is defined in the oracle data source definition (0082-PLAZZO-004) +16. Create a perpetual futures market which uses Ethereum oracle reads/events contract data for settlement payment schedule from; the trigger for the countdown to the first funding payment being publication of a valid value for the index price. The index price must not be available at the time the market is created and leaves opening auction; it must only become available sometime after. The aim is to test futures markets for underlyings that don't trade *yet* be where there is an agreed oracle source that will start publishing the price *once* they begin trading. (0082-ETHD-052). ### New Network parameters diff --git a/protocol/0083-RFPR-on_chain_referral_program.md b/protocol/0083-RFPR-on_chain_referral_program.md index 8b9b9d7b9..43ac39928 100644 --- a/protocol/0083-RFPR-on_chain_referral_program.md +++ b/protocol/0083-RFPR-on_chain_referral_program.md @@ -103,11 +103,11 @@ When submitting a referral program proposal through governance the following con - the number of tiers in `benefit_tiers` must be less than or equal to the network parameter `referralProgram.maxReferralTiers`. - all `minimum_running_notional_taker_volume` values must be an integer value strictly greater than `0`. - all `minimum_epochs` values must be an integer strictly greater than 0 -- all `referral_reward_factor` values must be greater than or equal to `0` and less than or equal to the network parameter `referralProgram.maxReferralRewardFactor`. +- all `referral_reward_factor` values must be greater than `0` and less than or equal to the network parameter `referralProgram.maxReferralRewardFactor`. - the number of tiers in `staking_tiers` must be less than or equal to the network parameter `referralProgram.maxReferralTiers`. - all `minimum_staked_tokens` values must be an integer value strictly greater than `0`. - all `referral_reward_multiplier` values must be a float value greater than or equal to `1`. -- all `referral_discount_factor` values must be greater than or equal to `0` and be less than or equal to the network parameter `referralProgram.maxReferralDiscountFactor`. +- all `referral_discount_factor` values must be greater than `0` and be less than or equal to the network parameter `referralProgram.maxReferralDiscountFactor`. - `window_length` must be an integer strictly greater than zero. The referral program will start the epoch after the `enactment_timestamp` is reached. @@ -144,7 +144,8 @@ To create a referral set and generate a referral code, the party must submit a s - `name`: mandatory string team name - `team_url`: optional string of a link to a team forum, discord, etc. (defaults to empty string / none-type) - `avatar_url`: optional string of a link to an image to be used as the teams avatar (defaults to empty string / none-type) - - `closed`: optional boolean, defines whether a team is accepting new members (defaults to false) + - `closed`: optional boolean, defines whether a team is closed to members not specified in the `allow_list`, i.e. when `True` the team is joinable by invite only. + - `allow_list`: optional list of public keys which defines which parties are allowed to join if the team is joinable by invite only. *Example: if party wants to create a simple referral set.* @@ -169,6 +170,21 @@ message CreateReferralSet{ } ``` +*Example: if party wants to create a referral set and team.* + +```protobuf +message CreateReferralSet{ + is_team: True + team_details: { + name: "VegaRocks", + team_url: "https://discord.com/channels/vegarocks", + avatar_url: "https://vega-rocks/logo-360x360.jpg", + allow_list: ["publ1ck3y001", "publ1ck3y002", publ1ck3y003] + closed: False, + } +} +``` + When the network receives a valid `CreateReferralSet` transaction, the network will create a referral set with the referral set `id` as the referral code. Any future parties who [apply](#applying-a-referral-code) the referral code will be added to the referral set. ### Updating a referral set @@ -186,7 +202,8 @@ To update a referral set the party submit a signed `UpdateReferralSet` transacti - `name`: optional string team name - `team_url`: optional string of a link to a team forum, discord, etc. - `avatar_url`: optional string of a link to an image to be used as the teams avatar - - `closed`: optional boolean, defines whether a team is accepting new members + - `closed`: optional boolean, defines whether a team is closed to members not specified in the `allow_list`, i.e. when `True` the team can be joined by invite only. + - `allow_list`: optional list of public keys which defines which parties are allowed to join if the team is joinable by invite only. ```protobuf message UpdateReferralSet{ @@ -200,7 +217,9 @@ message UpdateReferralSet{ } ``` -If a referral set is currently designated as a team, a referrer should be able to "close" their team to any new members by setting the `closed` field to `True`. Note, closing a team is the same as closing a referral set and as such all `ApplyReferralCode` transactions applying the referral code associated with the closed referrals set should be rejected. +If a referral set is currently designated as a team, a referrer should be able to "close" their team to any new members not specified in the `allow_list` by setting the `closed` field to `True`. + +Note, if a referrer updates `closed` or the `allow_list` defining the parties which are allowed to join the team, the updated list is only used to validate attempts to join the team, i.e. existing members are not removed from the team. If a referral set is currently designated as a team, a party is able to effectively "disband" a team by updating their referral set and setting their `is_team` value to `False`. Note a team should only be "disbanded" and removed from leaderboards at the end of the current epoch after rewards have been distributed. @@ -209,6 +228,7 @@ If a referral set is currently designated as a team, a party is able to effectiv To apply a referral code and become a referee, a party must fulfil the following criteria: - party must not currently be a **referrer** +- party must not currently be a **referee** associated with a valid referral set (Note: for a set to be valid, the sets referrer must be meeting the staking requirement.) To become a referee, a referee must submit a signed `ApplyReferralCode` transaction with the following fields: @@ -220,9 +240,25 @@ message ApplyReferralCode{ } ``` -If a party is not currently a referee, they must immediately be added to the referral set and [benefit factors and reward multipliers updated](#setting-benefit-factors-and-reward-multipliers) accordingly. Their key must then become associated with the referrer's key. All referral rewards will be transferred to this referrer's key, regardless of whether the party reapplies a new referral code. +If a party is not currently a referee, they must immediately be added to the referral set and [benefit factors and reward multipliers updated](#setting-benefit-factors-and-reward-multipliers) accordingly. If a team exists for this referral set, they will also be added to that team. -If a party is already a referee, and submits another `ApplyReferralCode` transaction, they will not be transferred to the new referral set but they will be added to the associated team at the start of the next epoch (providing a team exists). Note, if the referee has submitted multiple transactions in an epoch, the referee will be added to the new team specified in the latest valid transaction. + +If a party is already a referee, and submits another `ApplyReferralCode` transaction, the transaction will be rejected unless the referrer of the current referral set is not meeting the [staking requirement](#creating-a-referral-set). In this case, the party will be removed from the current referral set and added to the new referral set. + + +### Joining a team + +If a party is already a referee in a valid set, to join or move between teams, a party can submit a `JoinTeam` transaction with the following fields: + +- `id`: the id of the team they wish to join + +```protobuf +message JoinTeam{ + id: "mYr3f3rra1c0d3" +} +``` + +The party will be added to the team providing the team is not `closed` (anyone can join) or the team is `closed` (joinable by invite only) and the party is specified in the `allow_list`. ### Party volumes @@ -406,8 +442,8 @@ The Estimate Fees API should now calculate the following additional information: - the number of tiers in `benefit_tiers` must be less than or equal to the network parameter `referralProgram.maxReferralTiers` (0083-RFPR-002). - all `minimum_running_notional_taker_volume` values must be an integer strictly greater than 0 (0083-RFPR-051). - all `minimum_epochs_in_team` values must be an integer strictly greater than 0 (0083-RFPR-003). - - all `referral_reward_factor` values must be greater than or equal to `0` and less than or equal to the network parameter `referralProgram.maxReferralRewardFactor` (0083-RFPR-004). - - all `referral_discount_factor` values must be greater than or equal to `0` and be less than or equal to the network parameter `referralProgram.maxReferralDiscountFactor` (0083-RFPR-005). + - all `referral_reward_factor` values must be greater than `0` and less than or equal to the network parameter `referralProgram.maxReferralRewardFactor` (0083-RFPR-004). + - all `referral_discount_factor` values must be greater than `0` and be less than or equal to the network parameter `referralProgram.maxReferralDiscountFactor` (0083-RFPR-005). - the `window_length` must be an integer strictly greater than zero (0083-RFPR-006). 1. A referral program should be started the first epoch change after the `enactment_datetime` is reached (0083-RFPR-007). 1. A referral program should be closed the first epoch change after the `end_of_program_timestamp` is reached (0083-RFPR-008). @@ -442,26 +478,33 @@ The Estimate Fees API should now calculate the following additional information: 1. If a party is currently the referrer of a referral set from which a team **has not** yet been created, the party can **create** a team by submitting a signed `UpdateReferralSet` transaction and setting `is_team=True` (0083-RFPR-022). 1. If a party is currently the referrer of a referral set from which a team **has** already been created, the party can **update** a team by submitting a signed `UpdateReferralSet` transaction specifying the fields they want to update (0083-RFPR-023). 1. If a party submits an `UpdateReferralSet` transaction for a referral set they are not the referrer off, the transaction should be rejected (0083-RFPR-024). +1. If a referrer updates the `allow_list` associated with the team, existing members who are no longer on the allow_list should **not** be removed from the team (0083-RFPR-067). #### Applying a referral code -1. If a party **is not** currently a **referee**, if they submit a signed `ApplyReferralCode` transaction then: (0083-RFPR-025) +1. If a party **is not** currently a **referee**, if they submit a signed `ApplyReferralCode` transaction then: (0083-RFPR-059) - the party **will** be added to the associated referral set. - - the party **will** be added to the associated team (if one exists and the team is not closed). - -1. If a party **is** currently a **referee** (and the referrer **is** meeting the staking requirement), if they submit a signed `ApplyReferralCode` transaction then: (0083-RFPR-026) + - the party **will** be added to the associated team (if one exists) and the team is not `closed` or the team is `closed` and the party is allowed by the `allow_list`. - - the party **will not** be added to the associated referral set. - - the party **will** be added to the associated team (if one exists and the team is not closed). -1. If a party **is** currently a **referee** (and the referrer **is not** meeting the staking requirement), if they submit a signed `ApplyReferralCode` transaction then: (0083-RFPR-027). +1. If a party **is** currently a **referee** (and the referrer **is not** meeting the staking requirement), if they submit a signed `ApplyReferralCode` transaction then: (0083-RFPR-060). - the party **will** be added to the associated referral set. - - the party **will** be added to the associated team (if one exists and the team is not closed). + - the party **will** be added to the associated team (if one exists) and the team is not `closed` or the team is `closed` and the party is allowed by the `allow_list`. +1. If a party **is** currently a **referee** (and the referrer **is** meeting the staking requirement), if they submit a signed `ApplyReferralCode` transaction then the transaction will be rejected. (0083-RFPR-052). + +1. If a party submits an `ApplyReferralCode` transaction, a team exists for the specified `id` and the team is not `closed` then the party **should** be added to the team. (0083-RFPR-061). +1. If a party submits an `ApplyReferralCode` transaction, a team exists for the specified `id` and the team is `closed` then the party **will** be added to the team if **they are** specified in the `allow_list`. (0083-RFPR-062). +1. If a party submits an `ApplyReferralCode` transaction, a team exists for the specified `id` and the team is `closed` then the party **will not** be added to the team if **they are not** specified in the `allow_list`. (0083-RFPR-063). 1. An `ApplyReferralCode` transaction should be rejected if the party is a **referrer** (0083-RFPR-029). -1. An `ApplyReferralCode` transaction should be rejected if the `id` in the `ApplyReferralCode` transaction is for a referral set which is designated as a team and has set the team to be closed (0083-RFPR-030). + +### Joining a team + +1. If a party submits a `JoinTeam` transaction and the team is not `closed` then the party **should** be added to the team. (0083-RFPR-064). +1. If a party submits a `JoinTeam` transaction and the team is `closed` then the party **will** be added to the team if **they are** specified in the `allow_list`. (0083-RFPR-065). +1. If a party submits a `JoinTeam` transaction and the team is `closed` then the party **will not** be added to the team if **they are not** specified in the `allow_list`. (0083-RFPR-066). #### Epoch and running volumes diff --git a/protocol/0085-RVST-rewards_vesting.md b/protocol/0085-RVST-rewards_vesting.md index c94c4ae84..2148d392f 100644 --- a/protocol/0085-RVST-rewards_vesting.md +++ b/protocol/0085-RVST-rewards_vesting.md @@ -34,6 +34,8 @@ When transferring funds from the vesting account to the vested account, a new tr Once vested rewards are transferred to the vested account, the party will be able to transfer funds to their general account using a normal transfer. +When transferring funds from a vested account to the general account held by their key a party will incur no transfer fees and if transferring the full balance they will not be subject to the minimum quantum transfer amount. + Alternatively, they can leave their rewards in the vested account to increase their total rewards balance and receive a multiplier on their reward payout share. The size of this multiplier is dependent on their total rewards balance, i.e. the sum of the parties locked rewards, vesting rewards and vested rewards. Note, funds removed from the vested account are not included in this total. Note, a party will be unable to transfer funds in to the vested account. @@ -103,4 +105,3 @@ Must expose the following: 1. A parties `reward_distribution_bonus_multiplier` should be set equal to the value in the highest tier where they fulfil the `minimum_quantum_balance` required. (0085-RVST-012) 1. Funds in both the parties vesting account and vested account should contribute to their `minimum_quantum_balance`. (0085-RVST-013) 1. Assuming all parties perform equally, a party with a greater `reward_distribution_bonus_multiplier` should receive a larger share of a reward pool. (0085-RVST-014) - diff --git a/protocol/0087-EVMD-eth-rpc-and-evm-data-source.md b/protocol/0087-EVMD-eth-rpc-and-evm-data-source.md new file mode 100644 index 000000000..55b12ffa3 --- /dev/null +++ b/protocol/0087-EVMD-eth-rpc-and-evm-data-source.md @@ -0,0 +1,146 @@ +# Ethereum RPC and EVM based data sources + +## Summary + +This specification adds a new way of sourcing data from any chain or Ethereum Layer 2 blockchain (L2) that supports Ethereum RPC calls and runs an EVM. + +> [!TIP] +> A Layer 2 blockchain refers to network protocols that are layered on top of a Layer 1 solution. Layer 2 protocols use the Layer 1 blockchain for network and security infrastructure. + +The data is to be ingested as an Ethereum [data source](./0045-DSRC-data_sourcing.md) but pointing at a different RPC endpoint. +Hence this is in addition to and building upon [Ethereum data source](./0082-ETHD-ethereum-data-source.md). + +## Description + +In addition to listening to Ethereum events and reading from Ethereum contracts as described in [Ethereum data source](./0082-ETHD-ethereum-data-source.md) it will be possible for Vega nodes to listen to events from and read from other chains that implement Ethereum RPC and run EVM, in particular Ethereum L2s. + +The overarching principle is that the chain provides ethereum RPC / EVMs and thus contracts and ABIs are assumed to be functionally the same as on Ethereum itself. + +## Registration / removal + +A new network parameter, a JSON list of network id / chain id / name one per Ethereum RPC and EVM chain will be used for setting supported L2. This is set via [governance](./0028-GOVE-governance.md). +Name `blockchains.ethereumRpcAndEvmCompatDataSourcesConfig`. +Example value: + +```json +{"configs": [{"network_id": 10, "chain_id": 10, "confirmations": 3, "name":"optimism"}]} +``` + +Duplicate values of `network_id`, `chain_id` or `name` are not allowed (an update will be rejected at validation stage). +Any update must always change the entire JSON (it's not possible to change individual entries). +In current minimal scope, at proposal validation, check that only change is + +1. changing number of confirmation or +1. adding another source. + +For later release: A proposal to *remove* a registered Ethereum RPC+EVM compatible chain / L2 must fail at enactment stage if a market is referencing an `EthRpcEvmCompatible` data source. + +In current minimal scope: A proposal for a new market will fail at validation stage if it's referencing an `EthRpcEvmCompatible` that's not registered. + +For later release: A proposal for a new market will fail at enactment stage if it's referencing an `EthRpcEvmCompatible` that's not registered. + + +## Acceptance criteria + +### External Oracles - Creation + +- It is possible to add `EthRpcEvmCompatible` via governance (0087-EVMD-001). + +### External Oracles - External Chain Config Changes + +- At network proposal validation step we check that the only change to `blockchains.ethereumRpcAndEvmCompatDataSourcesConfig` is to either change number of confirmations or add another external chain. (0087-EVMD-043) + +### External Oracles - Deactivation (not scoped in Palazzo milestone) + +- It is possible to remove an `EthRpcEvmCompatible` via governance. The proposal will fail at enactment stage if there is any market that's not settled / closed that reference the `EthRpcEvmCompatible`. This is a future requirement and does not have an AC code. + + +### External Oracles - Market Amendments + +- It may happen that an `EthRpcEvmCompatible` that cannot be read from is proposed (because not enough validator nodes have it configured etc.). In that case a proposed / enacted market will not see the oracle inputs but it must be possible to change the said market via on-chain governance (0087-EVMD-003). +- Update an existing futures market using the market update proposal to change the `EthRpcEvmCompatible` chain referenced, smart contract address and read method. The changes take effect after the market update proposal is enacted and data is sourced from the new smart contract. The old data source will be deactivated when the proposal is enacted (0087-EVMD-004) +- Using the market update proposal all data elements for the ethereum oracles can be updated. On successful enactment, a new oracle data source is created for the market. In case any existing data source matches the new data source, then a new data source is not created and the existing one is used (0087-EVMD-005) +- Ensure existing oracle data sources are deactivated when market data sources are amended on another market. Create 2 markets to use different ethereum oracles for termination and settlement. Two sets of ethereum oracles are created and are ACTIVE. Then amend Market 2 to use exactly the same ethereum oracles for termination and settlement as Market1. Now ,the ethereum oracles originally created for for Market2 should be set to DEACTIVATED. No new ethereum oracles should be created and the Market2 should use the existing ethereum oracles created for Market1 (0087-EVMD-006) +- Ensure that when a market data source type is amended from internal, external, ethereum or open (coinbase) to an alternative for both termination and settlement we see that old data source is deactivated (if no other market is using) and we see the new data source created and it supports termination and settlement specific to its data source type (0087-EVMD-007) + + +### External Oracles - Validations + +- A new market proposal that reference an `EthRpcEvmCompatible` that's not active will fail at validation stage (0087-EVMD-008) +- A market change proposal that reference an `EthRpcEvmCompatible` that's not active will fail at validation stage (0087-EVMD-044) +- Validate if the smart contract address is valid (0087-EVMD-009) +- Validate if the data elements of the oracle data source is valid - e.g. call the smart contract and check if the types in the ABI match whats provided in the oracle spec (0087-EVMD-010) +- Validations for min / max frequency of listening for events / read a smart contract (0087-EVMD-011) +- When a proposal that uses ethereum oracles, defines incorrect data (contract address, ABI) the system should return an error and the proposal should not pass validation (0087-EVMD-012) +- Any mismatch between expected fields/field types and received fields/field types should emit an error event (0087-EVMD-013) + + +### Usage + +- Two different markets may reference two identical `EthRpcEvmCompatible` contracts and ABIs, even with same contract address *but* on two different Ethereum L2s and they each get the correct values i.e. if we have market 1 with + +```yaml +source: EthereumEvent { + source_chain_id: 0x123 + contract: 0xDEADBEEF + ABI: "...JSON..." + event: "MyFaveEvent" +``` + +and market 2 with + +```yaml +source: EthereumEvent { + source_chain_id: 0x789 + contract: 0xDEADBEEF + ABI: "...JSON..." + event: "MyFaveEvent" +``` + +Then market 1 only sees events of that type from `EthRpcEvmCompatible` 0x123 while market 2 only sees events of that type from `EthRpcEvmCompatible` 0x789 (0087-EVMD-014). + +- It should be possible to use only `EthRpcEvmCompatible` data sources in a market proposal, or create any combination with any of the other types of currently existing external or internal data sources (0087-EVMD-015) +- Create a market to use an internal data source to terminate a market and an `EthRpcEvmCompatible` to settle the market (0087-EVMD-016) +- Create a market to use an external data source to terminate a market and an `EthRpcEvmCompatible` to settle the market (0087-EVMD-017) +- Create a market to use an open oracle data source to settle a market and an `EthRpcEvmCompatible` to terminate the market (0087-EVMD-018) +- `EthRpcEvmCompatible` events should only be sent when the filter is matched, this can be verified using an API and the core/data node events (BUS_EVENT_TYPE_ORACLE_DATA) (0087-EVMD-019) +- `EthRpcEvmCompatible` data sources should only forward data after a configurable number of confirmations (0087-EVMD-020) +- Create 2 markets to use the same `EthRpcEvmCompatible` data source for termination say DS-T1 but two different `EthRpcEvmCompatible` data sources for settlement DS-S1 and DS-S2. Now trigger the termination ethereum oracle data source. Both markets should be terminated and the data source DS-T1 is set to DEACTIVATED and the data sources DS-S1 and DS-S2 are still ACTIVE. Now settle market1. DS-S1 is set to DEACTIVATED and DS-S2 is still active. (0087-EVMD-021) +- Create a market to use an `EthRpcEvmCompatible` data source for termination configured such that - it expects a boolean value True for termination and the contract supplying the termination value is polled every 5 seconds. Set the contract to return False for termination. The market is not terminated. The data source is still ACTIVE and no BUS_EVENT_TYPE_ORACLE_DATA events for that ethereum oracle spec are emitted. Then set the contract to return True for termination. The market is terminated and an event for BUS_EVENT_TYPE_ORACLE_DATA for the ethereum oracle data spec is received and the ethereum oracle is set to DEACTIVATED. (0087-EVMD-022) +- One oracle data event is emitted for data that matches each data source - Create 2 markets with ethereum oracle settlement specs that use the same settlement key such that - the first settlement spec expects settlement data to be greater than 100 and the second expects greater than 200. Now send it a settlement data of 300. One single event BUS_EVENT_TYPE_ORACLE_DATA for the settlement data is emitted for each matching `EthRpcEvmCompatible` data source i.e. in this case, two oracle data events will be emitted - one for each settlement data source. Both markets are settled and both the data sources are DEACTIVATED. (0087-EVMD-023) +- Different oracle data events for multiple spec id's with non matching filter values - Create 2 markets with ethereum oracle settlement specs that use the same settlement key such that - the first settlement spec expects settlement data to be greater than 100 and the second expects greater than 200. Now send it a settlement data of 50. NO data events for BUS_EVENT_TYPE_ORACLE_DATA. Send settlement data of 150. One single event BUS_EVENT_TYPE_ORACLE_DATA emitted for the settlement data is emitted with matching ethereum oracle data spec for Market1, market1 is settled and the data source is set to DEACTIVATED. Send settlement data of 250. One single event BUS_EVENT_TYPE_ORACLE_DATA emitted for the settlement data is emitted with matching ethereum oracle data spec for Market2, Market2 is settled and the data source is set to DEACTIVATED. (0087-EVMD-024) +- Network wide contract error should be reported via oracle data events (0087-EVMD-025) +- Different contracts on different markets - Create 2 markets with `EthRpcEvmCompatible` settlement data sources containing different contract addresses and with *different* settlement keys, but with all conditions and filters the same. Confirm that sending settlement value that passes the market spec filter only settles one market. (0087-EVMD-026) +- Different contracts on different markets - Create 2 markets with `EthRpcEvmCompatible` settlement data sources containing different contract addresses and with the *same* settlement keys, but with all conditions and filters the same. Confirm that sending settlement value that passes the market spec filter only settles one market. (0087-EVMD-027) + + +### Negative Tests + +- Set up a new data source with invalid contract address - should fail validations (0087-EVMD-028). +- Create an oracle that calls a read method of a smart contract and specify an incorrect ABI format for the event. Proposal should fail validation and should return an error (0087-EVMD-029) +- Set up a network such that different vega nodes receive conflicting results from an identical `EthRpcEvmCompatible` contract call. Attempt to settle a market using that contract. Observe that if there are not enough nodes voting in agreement, the market is not settled (0087-EVMD-030) + +### API + +- Ability to query data source specs defined for ethereum oracle sources, for settlement and termination, via an API endpoint (REST, gRPC and graphQL) - filters should be available for data source - internal OR external, status - Active / Inactive / Expired (0087-EVMD-031) +- Ability to query historic data sent by an ethereum oracle source, for settlement and termination, and processed by a market in vega network (0087-EVMD-032) + + +### Snapshots + +- Oracle data sources linked to markets should be stored on snapshots and should be able to be restored from snapshots. The states of the oracle data sources should be maintained across any markets where they are linked to `EthRpcEvmCompatible` data sources. (0087-EVMD-033) + +### Protocol Upgrade + +- Have a network running with a couple of futures markets with a mix of internal, external, open ethereum and `EthRpcEvmCompatible` oracles . Perform a protocol upgrade. Once the network is up , the state of the various data sources should be the same as before the protocol upgrade (0087-EVMD-034) +- Have a network running with a couple of perpetual markets with a mix of internal, external, open, ethereum and `EthRpcEvmCompatible` oracles . Perform a protocol upgrade. Once the network is up , the state of the various data sources should be the same as before the protocol upgrade (0087-EVMD-035) +- Create a futures market with an `EthRpcEvmCompatible` for termination such that it polls at a specific time. Perform a protocol upgrade such that the termination triggers in the middle of the protocol upgrade. Once the network is up , the termination should be triggered and the market should be terminated. (0087-EVMD-036) +- Create a futures market with an `EthRpcEvmCompatible` for settlement such that it polls at a specific time. Perform a protocol upgrade such that the settlement price matching the filters is triggered in the middle of the protocol upgrade. Once the network is up , the settlement should be triggered and the market should be terminated. (0087-EVMD-037) +- Create a perpetual market with an `EthRpcEvmCompatible` for settlement such that it polls at a specific time. Perform a protocol upgrade such that the settlement price matching the filters is triggered in the middle of the protocol upgrade. Once the network is up , the settlement should be triggered and the market should be terminated. (0087-EVMD-038) +- Ensure that markets with `EthRpcEvmCompatible` termination and settlement data sources continue to successfully terminate and settle markets after the protocol upgrade. (0087-EVMD-039) + +### Perpetual futures focused tests + +- Update an existing perpetuals market using the market update proposal to change from Ethereum to `EthRpcEvmCompatible` chain referenced, smart contract address and read method. The changes take effect after the market update proposal is enacted and data is sourced from the new smart contract. The old data source will be deactivated when the proposal is enacted (0087-EVMD-040). +- Create a perpetual futures market which uses an `EthRpcEvmCompatible` chain reads/events contract data for settlement payment schedule from chain `A` and `EthRpcEvmCompatible` chain reads/events contract data for the index price from chain `B` (0087-EVMD-041). +- Create a perpetual futures market which uses an `EthRpcEvmCompatible` chain reads/events contract data for settlement payment schedule from; the trigger for the countdown to the first funding payment being publication of a valid value for the index price. The index price must not be available at the time the market is created and leaves opening auction; it must only become available sometime after. The aim is to test futures markets for underlyings that don't trade *yet* be where there is an agreed oracle source that will start publishing the price *once* they begin trading. (0087-EVMD-042). diff --git a/protocol/0088-PPRF-party_profile.md b/protocol/0088-PPRF-party_profile.md new file mode 100644 index 000000000..9fb2e81ca --- /dev/null +++ b/protocol/0088-PPRF-party_profile.md @@ -0,0 +1,37 @@ +# On-chain Party Profile + +The on-chain party profile feature allows users to associate a unique alias and free-form metadata with a public key they own. This data is stored on-chain and is exposed to anyone through APIs. + + +## Updating a party profile + +Any user with a public key is able to update their party profile by submitting a signed `UpdatePartyProfile` transaction. + +To prevent spam attacks, the number of transactions which can be sent in an epoch per party is restricted and a parties total balance across all assets (expressed in quantum) must exceed `spam.protection.updatePartyProfile.min.funds`. Refer to the [spam protection specification](./0062-SPAM-spam_protection.md#party-profile-spam) for further details on these restrictions. + +An `UpdatePartyProfile` has the following fields. + +- `alias`: an optional string which can be as an alternative identifier for the public key in any dApp (e.g. in competition leaderboards). +- `metadata`: an optional list of metadata key value pairs to further describe a public key. + +If metadata is specified, the metadata must adhere to the following rules: + +- no more than 10 key value pairs can be specified +- a key must be no longer than 32 characters +- a value must be no longer than 255 characters + +If an alias is specified, it must adhere to the following rules: + +- the alias must be no longer than 32 characters +- the alias must be unique (i.e. the alias must not already be associated with an existing party profile) + +**Note:** +In order to validate uniqueness of an alias, core must keep a record of the aliases associated with each party. If a party is no longer active. + +### Acceptance Criteria + +- If a party updates there profile with an alias of a length greater than 32 characters, the transaction is rejected. (0088-PPRF-001) +- If a party updates there profile with an alias which is already associated with another key, the transaction is rejected. (0088-PPRF-002) +- If a party update there profile with metadata with more then 10 key pairs, the transaction is rejected. (0088-PPRF-003) +- If a party update there profile with metadata with a key of a length more than 32 characters, the transaction is rejected. (0088-PPRF-004) +- If a party update there profile with metadata with a value of a length more than 255 characters, the transaction is rejected. (0088-PPRF-005) diff --git a/protocol/README.md b/protocol/README.md index 14f8dc393..1961d9d08 100644 --- a/protocol/README.md +++ b/protocol/README.md @@ -29,7 +29,6 @@ ## Market protections - [Price monitoring](./0032-PRIM-price_monitoring.md) -- [Liquidity monitoring](./0035-LIQM-liquidity_monitoring.md) ## Liquidity diff --git a/protocol/categories.json b/protocol/categories.json index 1ec59f955..3491d10f7 100644 --- a/protocol/categories.json +++ b/protocol/categories.json @@ -3,10 +3,10 @@ "specs": ["0017-PART", "0022-AUTH", "0051-PROD", "0016-PFUT", "0053-PERP", "0040-ASSF", "0013-ACCT", "0054-NETP", "0068-MATC", "0067-KEYS", "0057-TRAN", "0052-FPOS", "0080-SPOT"] }, "Markets": { - "specs": ["0035-LIQM", "0032-PRIM", "0043-MKTL", "0026-AUCT", "0006-POSI", "0008-TRAD", "0001-MKTF", "0009-MRKP", "0012-POSR", "0021-MDAT", "0039-MKTD", "0070-MKTD"] + "specs": ["0035-LIQM", "0032-PRIM", "0043-MKTL", "0026-AUCT", "0006-POSI", "0008-TRAD", "0001-MKTF", "0009-MRKP", "0012-POSR", "0021-MDAT", "0039-MKTD", "0070-MKTD", "0081-SUCM"] }, "Settlement & Oracles": { - "specs": ["0002-STTL", "0003-MTMK", "0045-DSRC", "0046-DSRM", "0047-DSRF", "0048-DSRI"] + "specs": ["0002-STTL", "0003-MTMK", "0045-DSRC", "0046-DSRM", "0047-DSRF", "0048-DSRI", "0082-ETHD", "0087-EVMD"] }, "Protections": { "specs": ["0073-LIMN", "0072-SPPW", "0062-SPAM", "0060-WEND", "0003-NP-LIMI", "0072-SPPW", "0078-NWLI", "0079-TGAP"] @@ -35,6 +35,9 @@ "Data": { "specs": ["0076-DANO", "0020-APIS", "0007-POSN"] }, + "Referral": { + "specs": ["0083-RFPR", "0084-VDPR", "0085-RVST", "0086-ASPR"] + }, "UI": { "specs": ["1000-ASSO", "1001-VEST", "1002-STAK", "1003-INCO", "1004-VOTE", "3002-PROP", "1006-NETW", "1007-WALL", "3000-DEPO", "3001-WITH", "5000-MARK", "6000-COLL", "6001-SORD", "6002-MORD", "6003-POSI", "6004-FILL"] }, diff --git a/protocol/cosmic-features.json b/protocol/cosmic-features.json new file mode 100644 index 000000000..571708e67 --- /dev/null +++ b/protocol/cosmic-features.json @@ -0,0 +1,943 @@ +{ + "Iceberg Orders": { + "milestone": "deployment-1", + "acs": [ + "0014-ORDT-039", + "0014-ORDT-007", + "0014-ORDT-008", + "0014-ORDT-009", + "0014-ORDT-010", + "0014-ORDT-011", + "0014-ORDT-012", + "0014-ORDT-013", + "0014-ORDT-014", + "0014-ORDT-015", + "0014-ORDT-016", + "0014-ORDT-017", + "0014-ORDT-018", + "0014-ORDT-020", + "0014-ORDT-021", + "0014-ORDT-022", + "0014-ORDT-023", + "0014-ORDT-024", + "0014-ORDT-025", + "0014-ORDT-026", + "0014-ORDT-027", + "0014-ORDT-028", + "0014-ORDT-029", + "0014-ORDT-030", + "0014-ORDT-031", + "0014-ORDT-032", + "0014-ORDT-033", + "0014-ORDT-034", + "0014-ORDT-037", + "0014-ORDT-038", + "0014-ORDT-035", + "0014-ORDT-036", + "0014-ORDT-069", + "0014-ORDT-070" + ] + }, + "Stop Orders": { + "milestone": "deployment-1", + "acs": [ + "0014-ORDT-041", + "0014-ORDT-042", + "0014-ORDT-043", + "0014-ORDT-044", + "0014-ORDT-045", + "0014-ORDT-046", + "0014-ORDT-047", + "0014-ORDT-048", + "0014-ORDT-049", + "0014-ORDT-050", + "0014-ORDT-051", + "0014-ORDT-052", + "0014-ORDT-053", + "0014-ORDT-054", + "0014-ORDT-055", + "0014-ORDT-056", + "0014-ORDT-058", + "0014-ORDT-059", + "0014-ORDT-060", + "0014-ORDT-061", + "0014-ORDT-062", + "0014-ORDT-063", + "0014-ORDT-064", + "0014-ORDT-065", + "0014-ORDT-066", + "0014-ORDT-067", + "0014-ORDT-068", + "0014-ORDT-071", + "0014-ORDT-072", + "0014-ORDT-073", + "0014-ORDT-074", + "0014-ORDT-075", + "0014-ORDT-076", + "0014-ORDT-077", + "0014-ORDT-078", + "0014-ORDT-079", + "0014-ORDT-080", + "0079-TGAP-004", + "0079-TGAP-005" + ] + }, + "Successor Markets": { + "milestone": "deployment-1", + "acs": [ + "0001-MKTF-006", + "0001-MKTF-007", + "0001-MKTF-008", + "0001-MKTF-009", + "0001-MKTF-010", + "0028-GOVE-071", + "0028-GOVE-093", + "0042-LIQF-031", + "0042-LIQF-048", + "0042-LIQF-033", + "0042-LIQF-034", + "0081-SUCM-001", + "0081-SUCM-003", + "0081-SUCM-005", + "0081-SUCM-006", + "0081-SUCM-008", + "0081-SUCM-027", + "0081-SUCM-009", + "0081-SUCM-010", + "0081-SUCM-011", + "0081-SUCM-014", + "0081-SUCM-028", + "0081-SUCM-018", + "0081-SUCM-012", + "0081-SUCM-023", + "0081-SUCM-024", + "0081-SUCM-013", + "0081-SUCM-016", + "0081-SUCM-029", + "0081-SUCM-030", + "0081-SUCM-031", + "0081-SUCM-032", + "0081-SUCM-017", + "0081-SUCM-025", + "0081-SUCM-026", + "0081-SUCM-020", + "0081-SUCM-021", + "0081-SUCM-022", + "0081-SUCM-035" + ] + }, + "Transfers by governance": { + "milestone": "deployment-2", + "acs": [ + "0028-GOVE-073", + "0028-GOVE-074", + "0028-GOVE-128", + "0028-GOVE-119", + "0028-GOVE-120", + "0028-GOVE-132", + "0028-GOVE-122", + "0028-GOVE-077", + "0028-GOVE-079", + "0028-GOVE-081", + "0028-GOVE-082", + "0028-GOVE-083", + "0028-GOVE-084", + "0028-GOVE-085", + "0028-GOVE-086", + "0028-GOVE-087", + "0028-GOVE-088", + "0028-GOVE-089", + "0028-GOVE-091", + "0028-GOVE-092", + "0028-GOVE-094", + "0028-GOVE-095", + "0028-GOVE-096", + "0028-GOVE-099", + "0028-GOVE-100", + "0028-GOVE-101", + "0028-GOVE-102", + "0028-GOVE-130", + "0028-GOVE-131", + "0028-GOVE-103", + "0028-GOVE-133", + "0028-GOVE-129", + "0028-GOVE-104", + "0028-GOVE-105", + "0028-GOVE-106", + "0028-GOVE-107", + "0028-GOVE-123", + "0028-GOVE-124", + "0028-GOVE-125", + "0028-GOVE-126", + "0028-GOVE-127", + "0028-GOVE-140", + "0028-GOVE-141", + "0028-GOVE-142", + "0028-GOVE-143", + "0028-GOVE-144", + "0028-GOVE-154", + "0028-GOVE-155", + "0028-GOVE-156", + "0028-GOVE-157", + "0028-GOVE-158", + "0055-TREA-011", + "0013-ACCT-032", + "0013-ACCT-026", + "0013-ACCT-027", + "0013-ACCT-028" + ] + }, + "Perpetuals": { + "milestone": "deployment-2", + "acs": [ + "0001-MKTF-005", + "0001-MKTF-011", + "0001-MKTF-012", + "0053-PERP-001", + "0053-PERP-002", + "0053-PERP-003", + "0053-PERP-004", + "0053-PERP-005", + "0053-PERP-006", + "0053-PERP-007", + "0053-PERP-008", + "0053-PERP-009", + "0053-PERP-015", + "0053-PERP-016", + "0053-PERP-017", + "0053-PERP-018", + "0053-PERP-019", + "0053-PERP-020", + "0053-PERP-021", + "0053-PERP-022", + "0053-PERP-023", + "0043-MKTL-009", + "0051-PROD-007", + "0051-PROD-008", + "0051-PROD-009", + "0070-MKTD-017", + "0070-MKTD-018", + "0070-MKTD-019", + "0073-LIMN-105", + "0073-LIMN-106", + "0073-LIMN-107", + "0073-LIMN-108", + "0073-LIMN-109", + "0073-LIMN-110", + "0073-LIMN-111", + "0081-SUCM-002", + "0081-SUCM-015", + "0014-ORDT-120", + "0014-ORDT-121", + "0014-ORDT-122", + "0014-ORDT-123", + "0053-PERP-024" + ] + }, + "Ethereum Oracles": { + "milestone": "deployment-2", + "acs": [ + "0082-ETHD-001", + "0082-ETHD-002", + "0082-ETHD-003", + "0082-ETHD-005", + "0082-ETHD-006", + "0082-ETHD-007", + "0082-ETHD-010", + "0082-ETHD-011", + "0082-ETHD-012", + "0082-ETHD-013", + "0082-ETHD-014", + "0082-ETHD-015", + "0082-ETHD-016", + "0082-ETHD-017", + "0082-ETHD-018", + "0082-ETHD-019", + "0082-ETHD-020", + "0082-ETHD-021", + "0082-ETHD-022", + "0082-ETHD-023", + "0082-ETHD-024", + "0082-ETHD-025", + "0082-ETHD-028", + "0082-ETHD-029", + "0082-ETHD-030", + "0082-ETHD-034", + "0082-ETHD-035", + "0082-ETHD-038", + "0082-ETHD-039", + "0082-ETHD-040", + "0082-ETHD-041", + "0082-ETHD-042", + "0082-ETHD-043", + "0082-ETHD-044", + "0082-ETHD-045", + "0082-ETHD-047", + "0082-ETHD-048", + "0082-ETHD-049", + "0082-ETHD-050", + "0082-ETHD-051" + ] + }, + "SLA": { + "milestone": "deployment-2", + "acs": [ + "0042-LIQF-032", + "0042-LIQF-050", + "0042-LIQF-051", + "0042-LIQF-052", + "0042-LIQF-037", + "0042-LIQF-038", + "0042-LIQF-041", + "0042-LIQF-042", + "0042-LIQF-035", + "0042-LIQF-049", + "0042-LIQF-047", + "0042-LIQF-039", + "0042-LIQF-040", + "0042-LIQF-043", + "0042-LIQF-044", + "0042-LIQF-045", + "0042-LIQF-046", + "0042-LIQF-053", + "0042-LIQF-054", + "0044-LIME-057", + "0044-LIME-058", + "0044-LIME-059", + "0044-LIME-013", + "0044-LIME-014", + "0044-LIME-015", + "0044-LIME-016", + "0044-LIME-018", + "0044-LIME-019", + "0044-LIME-020", + "0044-LIME-021", + "0044-LIME-030", + "0044-LIME-031", + "0044-LIME-049", + "0044-LIME-022", + "0044-LIME-023", + "0044-LIME-024", + "0044-LIME-025", + "0044-LIME-026", + "0044-LIME-027", + "0044-LIME-050", + "0044-LIME-051", + "0044-LIME-053", + "0044-LIME-060", + "0044-LIME-028", + "0044-LIME-032", + "0044-LIME-033", + "0044-LIME-034", + "0044-LIME-036", + "0044-LIME-062", + "0044-LIME-063", + "0044-LIME-065", + "0044-LIME-067", + "0044-LIME-069", + "0044-LIME-071", + "0044-LIME-073", + "0044-LIME-075", + "0044-LIME-077", + "0044-LIME-079", + "0044-LIME-081", + "0044-LIME-083", + "0044-LIME-084", + "0044-LIME-085", + "0044-LIME-086", + "0044-LIME-087", + "0044-LIME-088", + "0044-LIME-089", + "0044-LIME-090", + "0044-LIME-091", + "0044-LIME-092", + "0044-LIME-093", + "0044-LIME-094", + "0044-LIME-095", + "0044-LIME-096", + "0044-LIME-097", + "0044-LIME-098", + "0044-LIME-099", + "0044-LIME-100", + "0044-LIME-101", + "0044-LIME-102", + "0026-AUCT-016", + "0026-AUCT-017", + "0026-AUCT-018", + "0026-AUCT-019", + "0026-AUCT-020", + "0026-AUCT-021", + "0026-AUCT-022", + "0034-PROB-004", + "0042-LIQF-055" + ] + }, + "Referral program": { + "milestone": "deployment-2", + "acs": [ + "0083-RFPR-001", + "0083-RFPR-002", + "0083-RFPR-003", + "0083-RFPR-004", + "0083-RFPR-005", + "0083-RFPR-006", + "0083-RFPR-007", + "0083-RFPR-008", + "0083-RFPR-009", + "0083-RFPR-010", + "0083-RFPR-011", + "0083-RFPR-012", + "0083-RFPR-013", + "0083-RFPR-014", + "0083-RFPR-015", + "0083-RFPR-016", + "0083-RFPR-017", + "0083-RFPR-018", + "0083-RFPR-019", + "0083-RFPR-020", + "0083-RFPR-021", + "0083-RFPR-022", + "0083-RFPR-023", + "0083-RFPR-024", + "0083-RFPR-025", + "0083-RFPR-026", + "0083-RFPR-027", + "0083-RFPR-029", + "0083-RFPR-030", + "0083-RFPR-031", + "0083-RFPR-032", + "0083-RFPR-033", + "0083-RFPR-034", + "0083-RFPR-035", + "0083-RFPR-036", + "0083-RFPR-037", + "0083-RFPR-038", + "0083-RFPR-039", + "0083-RFPR-040", + "0083-RFPR-041", + "0083-RFPR-042", + "0083-RFPR-043", + "0083-RFPR-046", + "0083-RFPR-047", + "0083-RFPR-048", + "0083-RFPR-049", + "0083-RFPR-050", + "0083-RFPR-051", + "0084-VDPR-001", + "0084-VDPR-002", + "0084-VDPR-003", + "0084-VDPR-004", + "0084-VDPR-005", + "0084-VDPR-006", + "0084-VDPR-007", + "0084-VDPR-008", + "0084-VDPR-009", + "0084-VDPR-010", + "0084-VDPR-011", + "0084-VDPR-012", + "0084-VDPR-013", + "0084-VDPR-014", + "0084-VDPR-015", + "0084-VDPR-016", + "0084-VDPR-017", + "0029-FEES-023", + "0029-FEES-024", + "0029-FEES-025", + "0029-FEES-026", + "0029-FEES-027", + "0029-FEES-028", + "0029-FEES-029", + "0029-FEES-030", + "0029-FEES-031", + "0029-FEES-032", + "0029-FEES-033", + "0062-SPAM-026", + "0062-SPAM-027", + "0062-SPAM-028", + "0062-SPAM-029", + "0062-SPAM-030", + "0062-SPAM-031", + "0062-SPAM-032", + "0062-SPAM-033", + "0062-SPAM-034", + "0062-SPAM-035", + "0062-SPAM-036", + "0062-SPAM-037" + ] + }, +"Rewards": { + "milestone": "deployment-2", + "acs": [ + "0085-RVST-001", + "0085-RVST-002", + "0085-RVST-003", + "0085-RVST-004", + "0085-RVST-005", + "0085-RVST-006", + "0085-RVST-007", + "0085-RVST-008", + "0085-RVST-009", + "0085-RVST-010", + "0085-RVST-011", + "0085-RVST-012", + "0085-RVST-013", + "0085-RVST-014", + "0086-ASPR-001", + "0086-ASPR-002", + "0086-ASPR-003", + "0086-ASPR-004", + "0086-ASPR-005", + "0086-ASPR-006", + "0086-ASPR-007", + "0086-ASPR-008", + "0086-ASPR-009", + "0086-ASPR-010", + "0056-REWA-076", + "0056-REWA-077", + "0056-REWA-078", + "0056-REWA-079", + "0056-REWA-080", + "0056-REWA-081", + "0056-REWA-082", + "0056-REWA-083", + "0056-REWA-084", + "0056-REWA-085", + "0056-REWA-086", + "0056-REWA-087", + "0056-REWA-088", + "0056-REWA-089", + "0056-REWA-090", + "0056-REWA-091", + "0056-REWA-092", + "0056-REWA-093", + "0056-REWA-094" + ] + }, + "Team rewards": { + "milestone": "deployment-3", + "acs": [ + "0056-REWA-095", + "0056-REWA-096", + "0056-REWA-097", + "0056-REWA-098", + "0056-REWA-099", + "0056-REWA-100", + "0056-REWA-101", + "0056-REWA-102", + "0056-REWA-103", + "0056-REWA-104", + "0056-REWA-105", + "0056-REWA-106", + "0056-REWA-107", + "0056-REWA-108" + ] + }, + "Market governance": { + "milestone": "deployment-2", + "acs": [ + "0028-GOVE-064", + "0028-GOVE-069", + "0028-GOVE-070", + "0028-GOVE-072", + "0028-GOVE-108", + "0028-GOVE-110", + "0028-GOVE-113", + "0028-GOVE-114", + "0028-GOVE-115", + "0028-GOVE-116", + "0028-GOVE-117", + "0028-GOVE-118", + "0028-GOVE-135", + "0028-GOVE-136", + "0028-GOVE-137", + "0028-GOVE-138", + "0028-GOVE-139", + "0028-GOVE-111", + "0028-GOVE-150", + "0028-GOVE-151", + "0028-GOVE-152" + ] + }, + "Batch change proposals": { + "milestone": "deployment-3", + "acs": [ + "0028-GOVE-145", + "0028-GOVE-146", + "0028-GOVE-147", + "0028-GOVE-148", + "0028-GOVE-149" + ] + }, + "Spot": { + "milestone": "deployment-3", + "acs": [ + "0080-SPOT-001", + "0080-SPOT-002", + "0080-SPOT-003", + "0080-SPOT-004", + "0080-SPOT-006", + "0080-SPOT-007", + "0080-SPOT-009", + "0080-SPOT-010", + "0080-SPOT-012", + "0080-SPOT-013", + "0080-SPOT-015", + "0080-SPOT-016", + "0080-SPOT-017", + "0080-SPOT-018", + "0080-SPOT-019", + "0080-SPOT-020", + "0081-SUCM-004", + "0044-LIME-029", + "0044-LIME-043", + "0044-LIME-044", + "0044-LIME-045", + "0044-LIME-046", + "0044-LIME-047", + "0044-LIME-048", + "0044-LIME-054", + "0044-LIME-055", + "0044-LIME-056", + "0044-LIME-064", + "0044-LIME-066", + "0044-LIME-068", + "0044-LIME-070", + "0044-LIME-072", + "0044-LIME-074", + "0044-LIME-076", + "0044-LIME-078", + "0044-LIME-080", + "0044-LIME-082", + "0056-REWA-051", + "0056-REWA-052", + "0056-REWA-053", + "0056-REWA-054", + "0056-REWA-055", + "0056-REWA-056", + "0056-REWA-057", + "0056-REWA-058", + "0056-REWA-059", + "0056-REWA-060", + "0056-REWA-061", + "0056-REWA-062", + "0056-REWA-063", + "0056-REWA-064", + "0056-REWA-065", + "0056-REWA-066", + "0056-REWA-067", + "0056-REWA-068", + "0056-REWA-069", + "0056-REWA-070", + "0056-REWA-071", + "0056-REWA-072", + "0056-REWA-073", + "0056-REWA-074", + "0056-REWA-075", + "0073-LIMN-077", + "0073-LIMN-078", + "0073-LIMN-079", + "0073-LIMN-080", + "0073-LIMN-081", + "0073-LIMN-082", + "0073-LIMN-083", + "0073-LIMN-084", + "0073-LIMN-085", + "0073-LIMN-086", + "0073-LIMN-088", + "0073-LIMN-089", + "0037-OPEG-019", + "0008-TRAD-008", + "0004-AMND-030", + "0004-AMND-031", + "0004-AMND-034", + "0004-AMND-035", + "0004-AMND-036", + "0004-AMND-037", + "0004-AMND-038", + "0004-AMND-039", + "0004-AMND-040", + "0004-AMND-041", + "0004-AMND-042", + "0004-AMND-043", + "0004-AMND-044", + "0004-AMND-045", + "0004-AMND-046", + "0004-AMND-047", + "0004-AMND-048", + "0004-AMND-049", + "0004-AMND-050", + "0004-AMND-051", + "0004-AMND-052", + "0004-AMND-053", + "0004-AMND-054", + "0004-AMND-055", + "0011-MARA-018", + "0011-MARA-019", + "0011-MARA-020", + "0011-MARA-021", + "0011-MARA-022", + "0011-MARA-023", + "0011-MARA-024", + "0011-MARA-025", + "0011-MARA-026", + "0011-MARA-027", + "0011-MARA-028", + "0011-MARA-029", + "0011-MARA-030", + "0011-MARA-031", + "0011-MARA-032", + "0013-ACCT-024", + "0013-ACCT-025", + "0013-ACCT-030", + "0013-ACCT-031", + "0014-ORDT-081", + "0014-ORDT-082", + "0014-ORDT-083", + "0014-ORDT-084", + "0014-ORDT-085", + "0014-ORDT-086", + "0014-ORDT-087", + "0014-ORDT-088", + "0014-ORDT-089", + "0014-ORDT-090", + "0014-ORDT-091", + "0014-ORDT-092", + "0014-ORDT-093", + "0014-ORDT-094", + "0014-ORDT-095", + "0014-ORDT-096", + "0014-ORDT-097", + "0014-ORDT-098", + "0014-ORDT-099", + "0014-ORDT-100", + "0014-ORDT-101", + "0014-ORDT-102", + "0014-ORDT-103", + "0014-ORDT-104", + "0014-ORDT-105", + "0014-ORDT-106", + "0014-ORDT-107", + "0014-ORDT-108", + "0014-ORDT-109", + "0014-ORDT-110", + "0014-ORDT-111", + "0014-ORDT-112", + "0014-ORDT-113", + "0014-ORDT-114", + "0014-ORDT-115", + "0014-ORDT-116", + "0014-ORDT-117", + "0014-ORDT-118", + "0014-ORDT-119", + "0021-MDAT-013", + "0021-MDAT-014", + "0021-MDAT-015", + "0021-MDAT-016", + "0021-MDAT-017", + "0021-MDAT-018", + "0021-MDAT-019", + "0021-MDAT-020", + "0024-OSTA-030", + "0024-OSTA-031", + "0024-OSTA-032", + "0024-OSTA-033", + "0024-OSTA-034", + "0024-OSTA-035", + "0024-OSTA-036", + "0024-OSTA-037", + "0024-OSTA-038", + "0024-OSTA-039", + "0024-OSTA-040", + "0024-OSTA-041", + "0024-OSTA-042", + "0024-OSTA-043", + "0024-OSTA-044", + "0024-OSTA-045", + "0024-OSTA-046", + "0024-OSTA-047", + "0024-OSTA-048", + "0025-OCRE-004", + "0025-OCRE-005", + "0025-OCRE-006", + "0026-AUCT-023", + "0026-AUCT-024", + "0026-AUCT-025", + "0026-AUCT-026", + "0026-AUCT-027", + "0026-AUCT-028", + "0026-AUCT-029", + "0026-AUCT-031", + "0026-AUCT-032", + "0029-FEES-015", + "0029-FEES-016", + "0029-FEES-017", + "0029-FEES-018", + "0029-FEES-019", + "0029-FEES-020", + "0029-FEES-021", + "0029-FEES-022", + "0032-PRIM-022", + "0032-PRIM-023", + "0032-PRIM-024", + "0032-PRIM-025", + "0032-PRIM-026", + "0032-PRIM-027", + "0032-PRIM-028", + "0032-PRIM-029", + "0032-PRIM-030", + "0032-PRIM-031", + "0032-PRIM-032", + "0032-PRIM-033", + "0032-PRIM-034", + "0032-PRIM-035", + "0032-PRIM-036", + "0032-PRIM-037", + "0032-PRIM-038", + "0033-OCAN-011", + "0033-OCAN-012", + "0033-OCAN-013", + "0033-OCAN-014", + "0033-OCAN-015", + "0033-OCAN-016", + "0033-OCAN-017", + "0034-PROB-003", + "0034-PROB-008", + "0034-PROB-010", + "0039-MKTD-020", + "0039-MKTD-021", + "0039-MKTD-022", + "0039-MKTD-023", + "0039-MKTD-024", + "0039-MKTD-025", + "0039-MKTD-026", + "0039-MKTD-027", + "0039-MKTD-028", + "0039-MKTD-029", + "0039-MKTD-030", + "0039-MKTD-031", + "0039-MKTD-032", + "0039-MKTD-033", + "0043-MKTL-005", + "0043-MKTL-006", + "0043-MKTL-007", + "0043-MKTL-008", + "0049-TVAL-007", + "0049-TVAL-008", + "0049-TVAL-009", + "0049-TVAL-010", + "0049-TVAL-011", + "0049-TVAL-012", + "0051-PROD-004", + "0051-PROD-005", + "0051-PROD-006", + "0052-FPOS-003", + "0052-FPOS-004", + "0054-NETP-007", + "0054-NETP-008", + "0054-NETP-009", + "0054-NETP-010", + "0054-NETP-011", + "0057-TRAN-063", + "0065-FTCO-005", + "0065-FTCO-006", + "0065-FTCO-007", + "0065-FTCO-008", + "0066-VALW-004", + "0066-VALW-005", + "0068-MATC-060", + "0068-MATC-061", + "0068-MATC-062", + "0068-MATC-063", + "0068-MATC-064", + "0068-MATC-065", + "0068-MATC-066", + "0068-MATC-067", + "0068-MATC-068", + "0068-MATC-069", + "0068-MATC-070", + "0068-MATC-071", + "0068-MATC-072", + "0068-MATC-073", + "0068-MATC-074", + "0068-MATC-075", + "0068-MATC-076", + "0068-MATC-077", + "0068-MATC-078", + "0068-MATC-079", + "0068-MATC-080", + "0068-MATC-081", + "0068-MATC-082", + "0068-MATC-083", + "0068-MATC-084", + "0068-MATC-085", + "0068-MATC-086", + "0068-MATC-087", + "0068-MATC-088", + "0068-MATC-089", + "0068-MATC-090", + "0068-MATC-091", + "0068-MATC-092", + "0070-MKTD-008", + "0070-MKTD-009", + "0070-MKTD-010", + "0070-MKTD-011", + "0070-MKTD-012", + "0070-MKTD-013", + "0070-MKTD-014", + "0070-MKTD-015", + "0073-LIMN-087", + "0073-LIMN-090", + "0073-LIMN-091", + "0073-LIMN-092", + "0073-LIMN-093", + "0073-LIMN-094", + "0073-LIMN-095", + "0073-LIMN-096", + "0073-LIMN-097", + "0073-LIMN-098", + "0073-LIMN-099", + "0073-LIMN-100", + "0073-LIMN-101", + "0073-LIMN-102", + "0073-LIMN-103", + "0073-LIMN-104", + "0074-BTCH-011", + "0074-BTCH-012", + "0074-BTCH-015", + "0074-BTCH-016", + "0074-BTCH-019", + "0079-TGAP-006", + "0079-TGAP-007", + "0081-SUCM-033", + "0081-SUCM-034" + ] + }, + "Fixed Size Amendments": { + "milestone": "deployment-3", + "acs": [ + "0004-AMND-056", + "0004-AMND-057", + "0004-AMND-058" + ] + }, + "Unknown": { + "milestone": "unknown", + "acs": [] + }, + "Iceberg Orders": { + "milestone": "cosmic-carryover", + "acs": ["0014-ORDT-069"] + }, + "Successor Markets": { + "milestone": "cosmic-carryover", + "acs": ["0001-MKTF-008"] + }, + "Ethereum oracles": { + "milestone": "cosmic-carryover", + "acs": ["0082-ETHD-035", "0082-ETHD-041"] + }, + "Stop Orders": { + "milestone": "cosmic-carryover", + "acs": ["0079-TGAP-004", "0079-TGAP-005", "0014-ORDT-132", "0014-ORDT-133", "0014-ORDT-134", "0014-ORDT-135", "0014-ORDT-136"] + } +} diff --git a/protocol/features.json b/protocol/features.json index 94910d269..b5b6a4677 100644 --- a/protocol/features.json +++ b/protocol/features.json @@ -1,570 +1,490 @@ { - "Iceberg Orders": { - "milestone": "deployment-1", + "Perpetuals": { + "milestone": "palazzo", "acs": [ - "0014-ORDT-039", - "0014-ORDT-007", - "0014-ORDT-008", - "0014-ORDT-009", - "0014-ORDT-010", - "0014-ORDT-011", - "0014-ORDT-012", - "0014-ORDT-013", - "0014-ORDT-014", - "0014-ORDT-015", - "0014-ORDT-016", - "0014-ORDT-017", - "0014-ORDT-018", - "0014-ORDT-020", - "0014-ORDT-021", - "0014-ORDT-022", - "0014-ORDT-023", - "0014-ORDT-024", - "0014-ORDT-025", - "0014-ORDT-026", - "0014-ORDT-027", - "0014-ORDT-028", - "0014-ORDT-029", - "0014-ORDT-030", - "0014-ORDT-031", - "0014-ORDT-032", - "0014-ORDT-033", - "0014-ORDT-034", - "0014-ORDT-037", - "0014-ORDT-038", - "0014-ORDT-035", - "0014-ORDT-036", - "0014-ORDT-069", - "0014-ORDT-070" + "0014-ORDT-120", + "0014-ORDT-121", + "0014-ORDT-122", + "0014-ORDT-123", + "0082-ETHD-052", + "0053-PERP-024", + "0053-PERP-025", + "0053-PERP-026", + "0053-PERP-027", + "0053-PERP-029", + "0053-PERP-030", + "0053-PERP-031", + "0053-PERP-032", + "0053-PERP-040", + "0053-PERP-041", + "0053-PERP-042" ] }, - "Stop Orders": { - "milestone": "deployment-1", + "Team rewards": { + "milestone": "palazzo", "acs": [ - "0014-ORDT-041", - "0014-ORDT-042", - "0014-ORDT-043", - "0014-ORDT-044", - "0014-ORDT-045", - "0014-ORDT-046", - "0014-ORDT-047", - "0014-ORDT-048", - "0014-ORDT-049", - "0014-ORDT-050", - "0014-ORDT-051", - "0014-ORDT-052", - "0014-ORDT-053", - "0014-ORDT-054", - "0014-ORDT-055", - "0014-ORDT-056", - "0014-ORDT-058", - "0014-ORDT-059", - "0014-ORDT-060", - "0014-ORDT-061", - "0014-ORDT-062", - "0014-ORDT-063", - "0014-ORDT-064", - "0014-ORDT-065", - "0014-ORDT-066", - "0014-ORDT-067", - "0014-ORDT-068", - "0014-ORDT-071", - "0014-ORDT-072", - "0014-ORDT-073", - "0014-ORDT-074", - "0014-ORDT-075", - "0014-ORDT-076", - "0014-ORDT-077", - "0014-ORDT-078", - "0014-ORDT-079", - "0014-ORDT-080", - "0079-TGAP-004", - "0079-TGAP-005" + "0056-REWA-095", + "0056-REWA-096", + "0056-REWA-097", + "0056-REWA-098", + "0056-REWA-099", + "0056-REWA-100", + "0056-REWA-101", + "0056-REWA-102", + "0056-REWA-103", + "0056-REWA-104", + "0056-REWA-105", + "0056-REWA-106", + "0056-REWA-107", + "0056-REWA-108", + "0056-REWA-109", + "0056-REWA-110", + "0083-RFPR-052", + "0083-RFPR-059", + "0083-RFPR-060", + "0083-RFPR-061", + "0083-RFPR-062", + "0083-RFPR-063", + "0083-RFPR-064", + "0083-RFPR-065", + "0083-RFPR-066", + "0083-RFPR-067", + "0088-PPRF-001", + "0088-PPRF-002", + "0088-PPRF-003", + "0088-PPRF-004", + "0088-PPRF-005", + "0062-SPAM-033", + "0062-SPAM-035", + "0062-SPAM-036", + "0062-SPAM-037", + "0062-SPAM-038", + "0062-SPAM-039", + "0062-SPAM-040", + "0062-SPAM-041", + "0062-SPAM-042" ] }, - "Successor Markets": { - "milestone": "deployment-1", + "Fixed size amendments": { + "milestone": "palazzo", "acs": [ - "0001-MKTF-006", - "0001-MKTF-007", - "0001-MKTF-008", - "0001-MKTF-009", - "0001-MKTF-010", - "0028-GOVE-071", - "0028-GOVE-093", - "0042-LIQF-031", - "0042-LIQF-048", - "0042-LIQF-033", - "0042-LIQF-034", - "0081-SUCM-001", - "0081-SUCM-003", - "0081-SUCM-005", - "0081-SUCM-006", - "0081-SUCM-008", - "0081-SUCM-027", - "0081-SUCM-009", - "0081-SUCM-010", - "0081-SUCM-011", - "0081-SUCM-014", - "0081-SUCM-028", - "0081-SUCM-018", - "0081-SUCM-012", - "0081-SUCM-023", - "0081-SUCM-024", - "0081-SUCM-013", - "0081-SUCM-016", - "0081-SUCM-029", - "0081-SUCM-030", - "0081-SUCM-031", - "0081-SUCM-032", - "0081-SUCM-017", - "0081-SUCM-025", - "0081-SUCM-026", - "0081-SUCM-020", - "0081-SUCM-021", - "0081-SUCM-022", - "0081-SUCM-035" + "0004-AMND-056", + "0004-AMND-057", + "0004-AMND-058", + "0004-AMND-059" ] }, - "Transfers by governance": { - "milestone": "deployment-2", + "Margin isolation": { + "milestone": "palazzo", "acs": [ - "0028-GOVE-073", - "0028-GOVE-074", - "0028-GOVE-128", - "0028-GOVE-119", - "0028-GOVE-120", - "0028-GOVE-132", - "0028-GOVE-122", - "0028-GOVE-077", - "0028-GOVE-079", - "0028-GOVE-081", - "0028-GOVE-082", - "0028-GOVE-083", - "0028-GOVE-084", - "0028-GOVE-085", - "0028-GOVE-086", - "0028-GOVE-087", - "0028-GOVE-088", - "0028-GOVE-089", - "0028-GOVE-091", - "0028-GOVE-092", - "0028-GOVE-094", - "0028-GOVE-095", - "0028-GOVE-096", - "0028-GOVE-099", - "0028-GOVE-100", - "0028-GOVE-101", - "0028-GOVE-102", - "0028-GOVE-130", - "0028-GOVE-131", - "0028-GOVE-103", - "0028-GOVE-133", - "0028-GOVE-129", - "0028-GOVE-104", - "0028-GOVE-105", - "0028-GOVE-106", - "0028-GOVE-107", - "0028-GOVE-123", - "0028-GOVE-124", - "0028-GOVE-125", - "0028-GOVE-126", - "0028-GOVE-127", - "0028-GOVE-140", - "0028-GOVE-141", - "0028-GOVE-142", - "0028-GOVE-143", - "0028-GOVE-144", - "0028-GOVE-154", - "0028-GOVE-155", - "0028-GOVE-156", - "0028-GOVE-157", - "0028-GOVE-158", - "0055-TREA-011", - "0013-ACCT-032", - "0013-ACCT-026", - "0013-ACCT-027", - "0013-ACCT-028" + "0019-MCAL-031", + "0019-MCAL-032", + "0019-MCAL-033", + "0019-MCAL-034", + "0019-MCAL-035", + "0019-MCAL-036", + "0019-MCAL-037", + "0019-MCAL-038", + "0019-MCAL-039", + "0019-MCAL-040", + "0019-MCAL-049", + "0019-MCAL-050", + "0019-MCAL-051", + "0019-MCAL-052", + "0019-MCAL-053", + "0019-MCAL-054", + "0019-MCAL-055", + "0019-MCAL-056", + "0019-MCAL-057", + "0019-MCAL-058", + "0019-MCAL-059", + "0019-MCAL-060", + "0019-MCAL-061", + "0019-MCAL-062", + "0019-MCAL-064", + "0019-MCAL-065", + "0019-MCAL-066", + "0019-MCAL-067", + "0019-MCAL-068", + "0019-MCAL-070", + "0019-MCAL-071", + "0019-MCAL-072", + "0019-MCAL-073", + "0019-MCAL-074", + "0019-MCAL-075", + "0019-MCAL-078", + "0019-MCAL-100", + "0019-MCAL-101", + "0019-MCAL-102", + "0019-MCAL-103", + "0019-MCAL-104", + "0019-MCAL-105", + "0019-MCAL-106", + "0019-MCAL-107", + "0019-MCAL-108", + "0019-MCAL-109", + "0019-MCAL-110", + "0019-MCAL-112", + "0019-MCAL-113", + "0019-MCAL-114", + "0019-MCAL-115", + "0019-MCAL-116", + "0019-MCAL-117", + "0019-MCAL-119", + "0019-MCAL-120", + "0019-MCAL-121", + "0019-MCAL-122", + "0019-MCAL-123", + "0019-MCAL-124", + "0019-MCAL-125", + "0019-MCAL-126", + "0019-MCAL-127", + "0019-MCAL-128", + "0019-MCAL-129", + "0019-MCAL-130", + "0019-MCAL-131", + "0019-MCAL-132", + "0019-MCAL-133", + "0019-MCAL-134", + "0019-MCAL-135", + "0019-MCAL-136", + "0019-MCAL-137", + "0019-MCAL-138", + "0019-MCAL-139", + "0019-MCAL-140", + "0019-MCAL-141", + "0019-MCAL-142", + "0019-MCAL-143", + "0019-MCAL-144", + "0019-MCAL-145", + "0019-MCAL-146", + "0019-MCAL-147", + "0019-MCAL-148", + "0019-MCAL-149", + "0019-MCAL-150", + "0019-MCAL-151", + "0019-MCAL-152", + "0019-MCAL-153", + "0019-MCAL-160", + "0019-MCAL-161", + "0019-MCAL-162", + "0019-MCAL-163", + "0019-MCAL-164", + "0019-MCAL-165", + "0019-MCAL-166", + "0019-MCAL-167", + "0019-MCAL-168", + "0019-MCAL-169", + "0019-MCAL-172", + "0019-MCAL-173", + "0019-MCAL-174", + "0019-MCAL-175", + "0019-MCAL-176", + "0019-MCAL-200", + "0019-MCAL-201", + "0019-MCAL-202", + "0019-MCAL-203", + "0019-MCAL-204", + "0019-MCAL-205", + "0019-MCAL-206", + "0019-MCAL-207", + "0019-MCAL-220", + "0019-MCAL-221", + "0019-MCAL-222", + "0019-MCAL-223", + "0019-MCAL-224", + "0019-MCAL-225", + "0019-MCAL-226", + "0019-MCAL-227", + "0019-MCAL-228", + "0019-MCAL-229", + "0019-MCAL-230", + "0019-MCAL-231", + "0019-MCAL-232", + "0019-MCAL-233", + "0074-BTCH-020", + "0074-BTCH-021" ] }, - "Perpetuals": { - "milestone": "deployment-2", + "Quadratic slippage removal": { + "milestone": "palazzo", "acs": [ - "0001-MKTF-005", - "0001-MKTF-011", - "0001-MKTF-012", - "0053-PERP-001", - "0053-PERP-002", - "0053-PERP-003", - "0053-PERP-004", - "0053-PERP-005", - "0053-PERP-006", - "0053-PERP-007", - "0053-PERP-008", - "0053-PERP-009", - "0053-PERP-015", - "0053-PERP-016", - "0053-PERP-017", - "0053-PERP-018", - "0053-PERP-019", - "0053-PERP-020", - "0053-PERP-021", - "0053-PERP-022", - "0053-PERP-023", - "0043-MKTL-009", - "0051-PROD-007", - "0051-PROD-008", - "0051-PROD-009", - "0070-MKTD-017", - "0070-MKTD-018", - "0070-MKTD-019", - "0073-LIMN-105", - "0073-LIMN-106", - "0073-LIMN-107", - "0073-LIMN-108", - "0073-LIMN-109", - "0073-LIMN-110", - "0073-LIMN-111", - "0081-SUCM-002", - "0081-SUCM-015", - "0014-ORDT-120", - "0014-ORDT-121", - "0014-ORDT-122", - "0014-ORDT-123", - "0019-MCAL-019", - "0019-MCAL-020", - "0019-MCAL-021", - "0019-MCAL-022", - "0019-MCAL-023", - "0053-PERP-024" + "0019-MCAL-024", + "0019-MCAL-025", + "0019-MCAL-026", + "0019-MCAL-027", + "0019-MCAL-028", + "0019-MCAL-029", + "0019-MCAL-030" ] }, - "Ethereum Oracles": { - "milestone": "deployment-2", + "Modify liquidation mechanics": { + "milestone": "palazzo", "acs": [ - "0082-ETHD-001", - "0082-ETHD-002", - "0082-ETHD-003", - "0082-ETHD-005", - "0082-ETHD-006", - "0082-ETHD-007", - "0082-ETHD-010", - "0082-ETHD-011", - "0082-ETHD-012", - "0082-ETHD-013", - "0082-ETHD-014", - "0082-ETHD-015", - "0082-ETHD-016", - "0082-ETHD-017", - "0082-ETHD-018", - "0082-ETHD-019", - "0082-ETHD-020", - "0082-ETHD-021", - "0082-ETHD-022", - "0082-ETHD-023", - "0082-ETHD-024", - "0082-ETHD-025", - "0082-ETHD-028", - "0082-ETHD-029", - "0082-ETHD-030", - "0082-ETHD-034", - "0082-ETHD-035", - "0082-ETHD-038", - "0082-ETHD-039", - "0082-ETHD-040", - "0082-ETHD-041", - "0082-ETHD-042", - "0082-ETHD-043", - "0082-ETHD-044", - "0082-ETHD-045", - "0082-ETHD-047", - "0082-ETHD-048", - "0082-ETHD-049", - "0082-ETHD-050", - "0082-ETHD-051" + "0012-POSR-001", + "0012-POSR-005", + "0012-POSR-009", + "0012-POSR-010", + "0012-POSR-011", + "0012-POSR-012", + "0012-POSR-013", + "0012-POSR-014", + "0012-POSR-015", + "0012-POSR-016", + "0012-POSR-017", + "0012-POSR-018", + "0012-POSR-019", + "0012-POSR-020", + "0012-POSR-021", + "0012-POSR-022", + "0012-POSR-023", + "0012-POSR-024", + "0012-POSR-025", + "0012-POSR-026", + "0012-POSR-027", + "0012-POSR-028", + "0012-POSR-029", + "0003-MTMK-015", + "0003-MTMK-016", + "0003-MTMK-017", + "0053-PERP-037", + "0053-PERP-038", + "0053-PERP-039" ] }, - "SLA": { - "milestone": "deployment-2", + "Batch governance proposals": { + "milestone": "palazzo", "acs": [ - "0042-LIQF-032", - "0042-LIQF-050", - "0042-LIQF-051", - "0042-LIQF-052", - "0042-LIQF-037", - "0042-LIQF-038", - "0042-LIQF-041", - "0042-LIQF-042", - "0042-LIQF-035", - "0042-LIQF-049", - "0042-LIQF-047", - "0042-LIQF-039", - "0042-LIQF-040", - "0042-LIQF-043", - "0042-LIQF-044", - "0042-LIQF-045", - "0042-LIQF-046", - "0042-LIQF-053", - "0042-LIQF-054", - "0044-LIME-057", - "0044-LIME-058", - "0044-LIME-059", - "0044-LIME-013", - "0044-LIME-014", - "0044-LIME-015", - "0044-LIME-016", - "0044-LIME-018", - "0044-LIME-019", - "0044-LIME-020", - "0044-LIME-021", - "0044-LIME-030", - "0044-LIME-031", - "0044-LIME-049", - "0044-LIME-022", - "0044-LIME-023", - "0044-LIME-024", - "0044-LIME-025", - "0044-LIME-026", - "0044-LIME-027", - "0044-LIME-050", - "0044-LIME-051", - "0044-LIME-053", - "0044-LIME-060", - "0044-LIME-028", - "0044-LIME-032", - "0044-LIME-033", - "0044-LIME-034", - "0044-LIME-036", - "0044-LIME-062", - "0044-LIME-063", - "0044-LIME-065", - "0044-LIME-067", - "0044-LIME-069", - "0044-LIME-071", - "0044-LIME-073", - "0044-LIME-075", - "0044-LIME-077", - "0044-LIME-079", - "0044-LIME-081", - "0044-LIME-083", - "0044-LIME-084", - "0044-LIME-085", - "0044-LIME-086", - "0044-LIME-087", - "0044-LIME-088", - "0044-LIME-089", - "0044-LIME-090", - "0044-LIME-091", - "0044-LIME-092", - "0044-LIME-093", - "0044-LIME-094", - "0044-LIME-095", - "0044-LIME-096", - "0044-LIME-097", - "0044-LIME-098", - "0044-LIME-099", - "0044-LIME-100", - "0044-LIME-101", - "0044-LIME-102", - "0026-AUCT-016", - "0026-AUCT-017", - "0026-AUCT-018", - "0026-AUCT-019", - "0026-AUCT-020", - "0026-AUCT-021", - "0026-AUCT-022", - "0034-PROB-004", - "0042-LIQF-055" + "0028-GOVE-067", + "0028-GOVE-068", + "0028-GOVE-145", + "0028-GOVE-146", + "0028-GOVE-148", + "0028-GOVE-149", + "0028-GOVE-160", + "0028-GOVE-161", + "0028-GOVE-163", + "0028-GOVE-164", + "0028-GOVE-165" ] }, - "Referral program": { - "milestone": "deployment-2", + "Transfer fee cap": { + "milestone": "palazzo", "acs": [ - "0083-RFPR-001", - "0083-RFPR-002", - "0083-RFPR-003", - "0083-RFPR-004", - "0083-RFPR-005", - "0083-RFPR-006", - "0083-RFPR-007", - "0083-RFPR-008", - "0083-RFPR-009", - "0083-RFPR-010", - "0083-RFPR-011", - "0083-RFPR-012", - "0083-RFPR-013", - "0083-RFPR-014", - "0083-RFPR-015", - "0083-RFPR-016", - "0083-RFPR-017", - "0083-RFPR-018", - "0083-RFPR-019", - "0083-RFPR-020", - "0083-RFPR-021", - "0083-RFPR-022", - "0083-RFPR-023", - "0083-RFPR-024", - "0083-RFPR-025", - "0083-RFPR-026", - "0083-RFPR-027", - "0083-RFPR-029", - "0083-RFPR-030", - "0083-RFPR-031", - "0083-RFPR-032", - "0083-RFPR-033", - "0083-RFPR-034", - "0083-RFPR-035", - "0083-RFPR-036", - "0083-RFPR-037", - "0083-RFPR-038", - "0083-RFPR-039", - "0083-RFPR-040", - "0083-RFPR-041", - "0083-RFPR-042", - "0083-RFPR-043", - "0083-RFPR-046", - "0083-RFPR-047", - "0083-RFPR-048", - "0083-RFPR-049", - "0083-RFPR-050", - "0083-RFPR-051", - "0084-VDPR-001", - "0084-VDPR-002", - "0084-VDPR-003", - "0084-VDPR-004", - "0084-VDPR-005", - "0084-VDPR-006", - "0084-VDPR-007", - "0084-VDPR-008", - "0084-VDPR-009", - "0084-VDPR-010", - "0084-VDPR-011", - "0084-VDPR-012", - "0084-VDPR-013", - "0084-VDPR-014", - "0084-VDPR-015", - "0084-VDPR-016", - "0084-VDPR-017", - "0029-FEES-023", - "0029-FEES-024", - "0029-FEES-025", - "0029-FEES-026", - "0029-FEES-027", - "0029-FEES-028", - "0029-FEES-029", - "0029-FEES-030", - "0029-FEES-031", - "0029-FEES-032", - "0029-FEES-033", - "0062-SPAM-026", - "0062-SPAM-027", - "0062-SPAM-028", - "0062-SPAM-029", - "0062-SPAM-030", - "0062-SPAM-031", - "0062-SPAM-032", - "0062-SPAM-033", - "0062-SPAM-034", - "0062-SPAM-035", - "0062-SPAM-036", - "0062-SPAM-037" + "0057-TRAN-011", + "0057-TRAN-012", + "0057-TRAN-014", + "0057-TRAN-015", + "0057-TRAN-016", + "0057-TRAN-017", + "0057-TRAN-018", + "0057-TRAN-019", + "0057-TRAN-020", + "0057-TRAN-021", + "0057-TRAN-022", + "0057-TRAN-023", + "0057-TRAN-024", + "0057-TRAN-027", + "0057-TRAN-064", + "0057-TRAN-065" ] }, -"Rewards": { - "milestone": "deployment-2", + "Transfers from vesting": { + "milestone": "palazzo", "acs": [ - "0085-RVST-001", - "0085-RVST-002", - "0085-RVST-003", - "0085-RVST-004", - "0085-RVST-005", - "0085-RVST-006", - "0085-RVST-007", - "0085-RVST-008", - "0085-RVST-009", - "0085-RVST-010", - "0085-RVST-011", - "0085-RVST-012", - "0085-RVST-013", - "0085-RVST-014", - "0086-ASPR-001", - "0086-ASPR-002", - "0086-ASPR-003", - "0086-ASPR-004", - "0086-ASPR-005", - "0086-ASPR-006", - "0086-ASPR-007", - "0086-ASPR-008", - "0086-ASPR-009", - "0086-ASPR-010", - "0056-REWA-076", - "0056-REWA-077", - "0056-REWA-078", - "0056-REWA-079", - "0056-REWA-080", - "0056-REWA-081", - "0056-REWA-082", - "0056-REWA-083", - "0056-REWA-084", - "0056-REWA-085", - "0056-REWA-086", - "0056-REWA-087", - "0056-REWA-088", - "0056-REWA-089", - "0056-REWA-090", - "0056-REWA-091", - "0056-REWA-092", - "0056-REWA-093", - "0056-REWA-094" + "0057-TRAN-066", + "0057-TRAN-067", + "0057-TRAN-068", + "0057-TRAN-069" ] }, - "Team rewards": { - "milestone": "deployment-3", + "Liquidity fee setting": { + "milestone": "palazzo", "acs": [ - "0056-REWA-095", - "0056-REWA-096", - "0056-REWA-097", - "0056-REWA-098", - "0056-REWA-099", - "0056-REWA-100", - "0056-REWA-101", - "0056-REWA-102", - "0056-REWA-103", - "0056-REWA-104", - "0056-REWA-105", - "0056-REWA-106", - "0056-REWA-107", - "0056-REWA-108" + "0042-LIQF-056", + "0042-LIQF-057", + "0042-LIQF-058", + "0042-LIQF-059", + "0042-LIQF-060", + "0042-LIQF-061", + "0042-LIQF-062" + ] + }, + "Governance market name change": { + "milestone": "palazzo", + "acs": [ + "0028-GOVE-159", + "0028-GOVE-166" + ] + }, + "Insurance pools redistribution": { + "milestone": "palazzo", + "acs": [ + "0002-STTL-011", + "0002-STTL-012", + "0002-STTL-013", + "0013-ACCT-033", + "0043-MKTL-010" + ] + }, + "Position Linked Stop-Orders": { + "milestone": "palazzo", + "acs": [ + "0014-ORDT-127", + "0014-ORDT-128", + "0014-ORDT-129", + "0014-ORDT-130", + "0014-ORDT-131", + "0014-ORDT-137", + "0014-ORDT-138", + "0014-ORDT-139" + ] + }, + "Markprice updates": { + "milestone": "palazzo", + "acs": [ + "0009-MRKP-001", + "0009-MRKP-003", + "0009-MRKP-010", + "0009-MRKP-011", + "0009-MRKP-012", + "0009-MRKP-013", + "0009-MRKP-014", + "0009-MRKP-015", + "0009-MRKP-016", + "0009-MRKP-017", + "0009-MRKP-018", + "0009-MRKP-019", + "0009-MRKP-020", + "0009-MRKP-021", + "0009-MRKP-022", + "0009-MRKP-023", + "0009-MRKP-024", + "0009-MRKP-025", + "0009-MRKP-026", + "0009-MRKP-027", + "0009-MRKP-028", + "0009-MRKP-029", + "0009-MRKP-030", + "0009-MRKP-031", + "0009-MRKP-032", + "0053-PERP-033", + "0053-PERP-034", + "0009-MRKP-040", + "0009-MRKP-041", + "0009-MRKP-050", + "0009-MRKP-051", + "0009-MRKP-052", + "0009-MRKP-053", + "0009-MRKP-054", + "0009-MRKP-055", + "0009-MRKP-056", + "0009-MRKP-057", + "0009-MRKP-058", + "0009-MRKP-059", + "0009-MRKP-060", + "0009-MRKP-061", + "0009-MRKP-062", + "0009-MRKP-063", + "0009-MRKP-064", + "0009-MRKP-110", + "0009-MRKP-111", + "0009-MRKP-112", + "0009-MRKP-113", + "0009-MRKP-114", + "0009-MRKP-115", + "0009-MRKP-116", + "0009-MRKP-117", + "0009-MRKP-118", + "0009-MRKP-119", + "0009-MRKP-120", + "0009-MRKP-121", + "0009-MRKP-122", + "0009-MRKP-123", + "0009-MRKP-124", + "0009-MRKP-125", + "0009-MRKP-126", + "0009-MRKP-127", + "0009-MRKP-130", + "0009-MRKP-131", + "0009-MRKP-132", + "0009-MRKP-134", + "0009-MRKP-135", + "0053-PERP-044", + "0053-PERP-045", + "0053-PERP-046" + ] + }, + "Ethereum RPC and EVM based data sources": { + "milestone": "palazzo", + "acs": [ + "0087-EVMD-001", + "0087-EVMD-003", + "0087-EVMD-004", + "0087-EVMD-005", + "0087-EVMD-006", + "0087-EVMD-007", + "0087-EVMD-008", + "0087-EVMD-009", + "0087-EVMD-010", + "0087-EVMD-011", + "0087-EVMD-012", + "0087-EVMD-013", + "0087-EVMD-014", + "0087-EVMD-015", + "0087-EVMD-016", + "0087-EVMD-017", + "0087-EVMD-018", + "0087-EVMD-019", + "0087-EVMD-020", + "0087-EVMD-021", + "0087-EVMD-022", + "0087-EVMD-023", + "0087-EVMD-024", + "0087-EVMD-025", + "0087-EVMD-026", + "0087-EVMD-027", + "0087-EVMD-028", + "0087-EVMD-029", + "0087-EVMD-030", + "0087-EVMD-031", + "0087-EVMD-032", + "0087-EVMD-033", + "0087-EVMD-034", + "0087-EVMD-035", + "0087-EVMD-036", + "0087-EVMD-037", + "0087-EVMD-038", + "0087-EVMD-039", + "0087-EVMD-040", + "0087-EVMD-041", + "0087-EVMD-042", + "0087-EVMD-043", + "0087-EVMD-044" ] }, - "Market governance": { - "milestone": "deployment-2", + "Price estimate": { + "milestone": "palazzo", "acs": [ - "0028-GOVE-064", - "0028-GOVE-069", - "0028-GOVE-070", - "0028-GOVE-072", - "0028-GOVE-108", - "0028-GOVE-110", - "0028-GOVE-113", - "0028-GOVE-114", - "0028-GOVE-115", - "0028-GOVE-116", - "0028-GOVE-117", - "0028-GOVE-118", - "0028-GOVE-135", - "0028-GOVE-136", - "0028-GOVE-137", - "0028-GOVE-138", - "0028-GOVE-139", - "0028-GOVE-111", - "0028-GOVE-150", - "0028-GOVE-151", - "0028-GOVE-152" + "0012-NP-LIPE-001", + "0012-NP-LIPE-002", + "0012-NP-LIPE-003", + "0012-NP-LIPE-004", + "0012-NP-LIPE-005", + "0012-NP-LIPE-006", + "0012-NP-LIPE-007", + "0013-NP-POSE-001", + "0013-NP-POSE-002", + "0013-NP-POSE-004", + "0013-NP-POSE-005", + "0013-NP-POSE-006", + "0013-NP-POSE-007", + "0013-NP-POSE-008", + "0013-NP-POSE-009" ] }, "Unknown": { "milestone": "unknown", - "acs": [] + "acs": ["0028-GOVE-167"] } -} +} \ No newline at end of file diff --git a/wordlist.txt b/wordlist.txt index 7ca46810b..5a8e4d28d 100644 --- a/wordlist.txt +++ b/wordlist.txt @@ -1,6 +1,8 @@ +$VEGA ABCI ABCI++ ABI +ABIs actioned Allowlist allowlisted @@ -12,6 +14,7 @@ asynchrony atomicity Auth authorisable +avatarUrl AVL bip blockchain @@ -30,8 +33,6 @@ cdot ceil ceiled CFD -CSF -CSV checkpointing CLEF closedout @@ -45,16 +46,21 @@ composable config cosmovisor counterparty +CQRS +CreateReferralSet crypto cryptocurrencies cryptographic -CQRS +CSF +CSV cumulated customisable customised DAI +dApp datanode datapoint +datapoints datatypes datetime decentralised @@ -70,6 +76,7 @@ discoverability disincentivise DLT DPOS +DS durations earlyExitPenalty ECDSA @@ -78,18 +85,20 @@ EIP ELS encodings ENE +enum enums EOS ERC +ERC-1155 ERC-20 ERC-721 -ERC-1155 ETH ETH's ETHB Ethereum eventbus EVM +EVMs executables familiarise FBAs @@ -126,14 +135,15 @@ IBC incentivise incentivised incrementing +infty init initialisation initialise initialised initialising +int64 integrations intrablock -int64 IOC JSON keyholder @@ -141,7 +151,10 @@ keypair keypairs keystore KMS +ldots +leaderboards leftarrow +leq lifecycle linearisation linearised @@ -160,10 +173,10 @@ malus margined margining marketID -messager math mempool Merkle +messager MetaMask microstructure midprice @@ -190,6 +203,8 @@ PDP performant permissioned permissionless +PERP +perps PME PnL PoS @@ -201,10 +216,14 @@ protobuf Pseudocode pseudorandom pubkey +quant rata +rebased +recollateralise +ReferralSet reimplemented -repo renormalise +repo reponse repurpose resync @@ -215,19 +234,21 @@ Ropsten RPC runnable runtime -scalable scalability +scalable Scholes selectable sepolia serialisation SHA -sharded SHA3 +sharded siskas SLA Solana src +SSD +SSL stablecoin stablecoins stakers @@ -235,8 +256,6 @@ statebridge stateful statesync structs -SSD -SSL suboptimal subtype subtypes @@ -246,6 +265,7 @@ sybil sybils tau TBC +teamUrl Tendermint testnet testnets @@ -253,35 +273,36 @@ TIF TODO tokenholder tokenholders -tradable trackable +tradable Tron trustless TWAP tx TXs -quant UI uint -unmarshal unamended unassociated unbanned undelegate -undelegates undelegated +undelegates undelegating undelegation undelegations undeployed +underlyings unencrypted unitless +unmarshal unnominated unnormalised unstake unstaked unstakes unstaking +untriggered unvested url USD @@ -289,8 +310,8 @@ USDC USDT UTC validator -validators validator's +validators vega vegaprotocol vegatools @@ -303,19 +324,8 @@ VW VWAP walkthrough walkthroughs -wei wBTC +wei whitepaper Yubikey -$VEGA -DS -teamUrl -avatarUrl -dApp -leaderboards -ReferralSet -CreateReferralSet -enum -ldots -infty -leq +joinable \ No newline at end of file