# v9.2.1 Update

## Summary

* 50% of the spread and price impact is now charged on trade opening and closing instead of 100% on trade opening
* Existing trades don't pay any spread or impact on close (since they already paid in full on open), and they keep paying 100% spread when partial adding
* Spread and price impact on close use the reverse direction as opening spread (makes price lower for longs and higher for shorts)
* Price impact on close uses the opposite side depth and opposite side price impact OI (= cumulative vol now)
* Price impact OI windows now use new cumulative volume logic (much shorter windows duration possible because more accurate, opening a long / closing a short uses/adds to long windows, opening a short / closing a long uses/adds to short windows, new cumulative factor which will be set to 1 to start with but can be used to adjust weight of price impact OI in price impact formula), we never remove any OI from windows anymore (logic much simpler, no longer need to move trade OI to current window for partials)
* Closing price impact higher for short-term trades (depending on protection factor blocks duration and protection factor multiplier per pair), only when net PnL is positive
* Liquidation price now takes into account the closing spread (not the price impact) for trades opened after v9.2 and uses new logic for the liquidation pnl % threshold (closer pnl % threshold as leverage increases, more aggressive for higher leverages similarly to MMR)
* Existing trades aren't affected by the new liquidation threshold logic, liquidation parameters for trades are stored on opening so they can't change for an open trade even when a group's liquidation parameters are updated, however a trade's liquidation params are refreshed to current value when position size is increased using partials
* New stop loss max distance is `liqPnlThresholdP - 10%` (eg. -70% PnL when trade liq pnl % threshold is -80%)
* Added max slippage on close (market close, partial close, tp/sl) mechanism: by default 1% for all opened trades, can be adjusted after trade is open
* Extracted cancel reason logic for limit/stop/tp/sl/liq callbacks into getters, will be used by EA-lookbacks to check ability to execute at past chain state
* LIQ triggers no longer turned into SL triggers when SL is closer otherwise user could set very low max closing slippage and would never be able to be liquidated if price went past his SL and LIQ price, trigger bots will prioritize SL over LIQ when possible

## Full Changelog

### core/facets/GNSPairsStorage.sol (update)

* Added `initializeGroupLiquidationParams`, `setGroupLiquidationParams`, `getGroupLiquidationParams`, and `getPairLiquidationParams`

### core/facets/GNSPriceAggregator.sol (update)

* `getPrice` now accepts `_tradeId` instead of `_trader`

### core/facets/GNSPriceImpact.sol (update)

* Added `initializePairFactors`, `setProtectionCloseFactors`, `setProtectionCloseFactorBlocks`, `setCumulativeFactors`, `getPairFactors`
* Removed `removePriceImpactOpenInterest`, `getTradeLastWindowOiUsd`, `getTradePriceImpactInfo`
* `addPriceImpactOpenInterest` now accepts `_open` argument
* `getTradePriceImpact` now accepts `_isPnlPositive`, `_open`, `_lastPosIncreaseBlock`, and `_contractsVersion` arguments

### core/facets/GNSTradingCallbacks.sol (update)

* Added `validateTriggerOpenOrderCallback` and `validateTriggerCloseOrderCallback` getters

### core/facets/GNSTradingInteractions.sol (update)

* New `updateMaxClosingSlippageP` function
* `closeTradeMarket` and `decreasePositionSize` now accept `_expectedPrice` argument (used for max slippage check in callbacks)

### core/facets/GNSTradingStorage.sol (update)

* New `updateTradeMaxClosingSlippageP` function
* `updateTradePosition` now accepts `_isPartialIncrease` argument
* Added `getTradeLiquidationParams`, `getTradesLiquidationParams`, `getAllTradesLiquidationParams`, and `getCurrentContractsVersion`

### interfaces/IGeneralErrors.sol (update)

* Added `WrongOrderType()` error

### interfaces/libraries/IPairsStorageUtils.sol (update)

* Added new function definitions
* Added `GroupLiquidationParamsUpdated` event
* Added `MaxLiqSpreadPTooHigh`, `WrongLiqParamsThresholds`, `WrongLiqParamsLeverages`, `StartLiqThresholdTooHigh`, `EndLiqThresholdTooLow`, `StartLeverageTooLow`, `EndLeverageTooHigh`

### interfaces/libraries/IPriceAggregatorUtils.sol (update)

* Adjusted `getPrice` function definition
* Updated `PriceRequested` event: now emits `tradeId` instead of `trader`

### interfaces/libraries/IPriceImpactUtils.sol (update)

* Added new function definitions
* Removed deprecated function definitions
* Adjusted `addPriceImpactOpenInterest` and `getTradePriceImpact` functions definitions
* Added `ProtectionCloseFactorUpdated`, `ProtectionCloseFactorBlocksUpdated`, and `CumulativeFactorUpdated` events
* Removed `isPartial` from `PriceImpactOpenInterestAdded` event
* Removed `PriceImpactOpenInterestRemoved` event

### interfaces/libraries/ITradingCallbacksUtils.sol (update)

* Added `validateTriggerOpenOrderCallback` and `validateTriggerCloseOrderCallback` functions definitions

### interfaces/libraries/ITradingCommonUtils.sol (update)

* Added `TradePriceImpactInput` struct to avoid stack too deep in `getTradeClosingPriceImpact`

### interfaces/libraries/ITradingInteractionsUtils.sol (update)

* Added `updateMaxClosingSlippageP` function definition
* Adjusted `closeTradeMarket` and `decreasePositionSize` functions definitions
* Removed `WrongOrderType()` error

### interfaces/libraries/ITradingStorageUtils.sol (update)

* Added new function definitions
* Adjusted `updateTradePosition` function definition
* `TradeStored` event now also emits the trade liquidation params
* `TradePositionUpdated` now emits `isPartialIncrease`
* New `TradeMaxClosingSlippagePUpdated` event

### interfaces/libraries/IUpdatePositionSizeUtils.sol (update)

* Added `long` and `collateralPriceUsd` properties to `PositionSizeIncreaseExecuted` and `PositionSizeDecreaseExecuted` events

### interfaces/types/IBorrowingFees.sol (update)

* `LiqPriceInput` struct has new `liquidationParams` property

### interfaces/types/IPairsStorage.sol (update)

* Added `groupLiquidationParams` mapping and corresponding `GroupLiquidationParams` struct
* `__gap` length decreased from 41 to 40

### interfaces/types/IPriceImpact.sol (update)

* Added `pairFactors` mapping
* `__gap` length decreased from 46 to 45
* Added `_open` property to `OiWindowUpdate` struct
* New `PairFactors` struct

### interfaces/types/ITradingStorage.sol (update)

* Added `tradeLiquidationParams` mapping
* `__gap` length decreased from 39 to 38
* New `ContractsVersion` enum
* `TradeInfo` struct now has `contractsVersion` and `lastPosIncreaseBlock` properties, and `__placeholder` was reduced from `uint48` to `uint8`

### interfaces/types/IUpdatePositionSize.sol (update)

* Added `expectedPrice` property to `DecreasePositionSizeInput` struct
* Added `priceAfterImpact` property to `DecreasePositionSizeValues` struct

### libraries/ArrayGetters.sol (update)

* Added `getTradesLiquidationParams` and `getAllTradesLiquidationParams` getters

### libraries/BorrowingFeesUtils.sol (update)

* `getTradeLiquidationPrice`: calculate `closingFeesCollateral` using new `TradingCommonUtils.getTotalClosingFeesCollateral` helper, pass input liquidation params, trade contracts version, and pair spread % to `_getTradeLiquidationPrice`
* `_getTradeLiquidationPrice`: new `_liquidationParams`, `_contractsVersion`, and `_pairSpreadP` arguments, `_collateralPrecisionDelta` is now uint256 (saves one var to avoid stack too deep), calculate `liqPnlThresholdP` using new `TradingCommonUtils.getLiqPnlThresholdP` helper, in `collateralLiqNegativePnlInt` calculation divide by `1e10` because of new liq threshold precision, if trade opened after v9.2 make liquidation price distance lower by closing spread (= 1/2 pair spread %, capped at `maxLiqSpreadP`)

### libraries/ConstantsUtils.sol (update)

* Replaced `MAX_SL_P` by `SL_LIQ_BUFFER_P=10%`, renamed `LIQ_THRESHOLD_P` to `LEGACY_LIQ_THRESHOLD_P`, added `MIN_LIQ_THRESHOLD_P=50%`/`MAX_LIQ_SPREAD_P=0.05%`/`DEFAULT_MAX_CLOSING_SLIPPAGE_P=1%`

### libraries/PairsStorageUtils.sol (update)

* New `initializeGroupLiquidationParams` function: initializes group liquidation params for all existing groups
* New `setGroupLiquidationParams` function: used to update a group's liquidation params (only affects trades opened after the change)
* New `getGroupLiquidationParams` and `getPairLiquidationParams` getters

### libraries/PriceAggregatorUtils.sol (update)

* `getPrice`: now accepts `_tradeId` argument instead of `_trader`, passes `trader`/`index`/`orderType` data to `linkRequest`

### libraries/PriceImpactUtils.sol (update)

* `MAX_WINDOWS_DURATION` lowered from 30 days to 10 minutes, `MIN_WINDOWS_DURATION` lowered from 10 minutes to 1 minute
* New `initializePairFactors` function that initializes pairs factors
* New `setProtectionCloseFactors` to set the protection close factors for a set of pairs
* New `setProtectionCloseFactorBlocks` to set the protection close factor duration in blocks for a set of pairs
* New `setCumulativeFactors` to set the cumulative factors for a set of pairs
* `addPriceImpactOpenInterest`: Removed partials logic that moved previous trade OI to current window if not expired, changed logic to decide if adds to long or short part of window (opening a long / closing a short adds to long window, opening a short / closing a long adds to short window), removed `lastWindowOiUsd` update (deprecated)
* Removed `removePriceImpactOpenInterest` and `getTradeLastWindowOiUsd`
* `getTradePriceImpact`: new `_isPnlPositive`/`_open`/`_lastPosIncreaseBlock`/`_contractsVersion` arguments, now loads `pairFactors` for pair, uses opposite side depth when `_open = false`, uses opposite side price impact OI when `_open = false`, passes `_open`, `protectionCloseFactor` (only when `_isPnlPositive` is true, `_open` is false, `protectionCloseFactor` isn't zero, and protection close factor didn't expire, otherwise passes `1e10`), `cumulativeFactor` (when not zero, otherwise passes `1e10`), and `_contractsVersion` to `_getTradePriceImpact`
* Removed `getTradePriceImpactInfo` (deprecated)
* New `getPairFactors` getter
* `_getTradePriceImpact`: new `_open`, `_protectionCloseFactor`, `_cumulativeFactor`, `_contractsVersion` arguments, returns early 0 price impact if trade opened before v9.2 and `_open = false` (no closing price impact for trades opened before v9.2 since they paid it fully on open), multiplies `_startOpenInterestUsd` by `_cumulativeFactor`, multiplies by `_protectionCloseFactor` instead of `1e10` in `priceImpactP` calculation and divides result by 2 if trade opened after v9.2 (charge half price impact if trade opened after v9.2 since they paid half on open already, for trades opened before v9.2 charges full opening price impact), sets `_long` to `!_long` if `_open` is false to invert the price impact direction on close

### libraries/TradingCallbacksUtils.sol (update)

* `closeTradeMarketCallback`: use new `_getTradeInfo` getter, Add `TradingCommonUtils.getTradeClosingPriceImpact` call, added max slippage cancel reason check (uses default max slippage % if `tradeInfo.maxSlippageP` not set), use `priceAfterImpact` (= market price + closing spread and price impact) instead of `_a.price` to calculate `v.profitP`, emit `priceAfterImpact` and `priceImpactP` in event
* `executeTriggerOpenOrderCallback`: moved all cancel reason and spread/price impact logic to `validateTriggerOpenOrderCallback`
* `executeTriggerCloseOrderCallback`: moved all cancel reason and spread/price impact logic to `validateTriggerCloseOrderCallback`, emit `priceImpactP` in `LimitExecuted` event
* `validateTriggerOpenOrderCallback`: new getter that contains the extracted cancel reason logic of `executeTriggerOpenOrderCallback`
* `validateTriggerCloseOrderCallback`: new getter that contains the extracted cancel reason logic of `executeTriggerCloseOrderCallback`, for SLs and TPs (not liquidations) set `v.executionPrice` to `TradingCommonUtils.getTradeClosingPriceImpact` result to take into account closing spread and impact, added max slippage cancel reason check for everything but liquidations (uses default max slippage % if `tradeInfo.maxSlippageP` not set)
* `_openTradePrep`: no logic change, just use new `TradingCommonUtils.getTradeOpeningPriceImpact` helper to calculate opening price impact
* New `_getTradeInfo` getter

### libraries/TradingCommonUtils.sol (update)

* `getMarketExecutionPrice`: new `_open`/`_contractsVersion` arguments, returns early with 0 spread if `_open = false` and `_contractsVersion = before v9.2`, divides spread by 2 if `_contractsVersion >= v9.2`, inverts spread direction when `_open` = false
* `getTradeValuePure`: combined `_borrowingFeeCollateral` and `_closingFeeCollateral` into `_feesCollateral` to avoid stack too deep, added `_liqPnlThresholdP` argument, `collateralLiqThreshold` is now `uint256` and uses `_liqPnlThresholdP` instead of `ConstantsUtils.LIQ_THRESHOLD_P` and is adjusted for `1e10` precision, added `int256` conversion for `collateralLiqThreshold` in return statement
* `getLiqPnlThresholdP`: new pure function that returns the corresponding liquidation PnL threshold percentage to use depending on a trade's liquidation params and leverage; uses new logic (linearly decreasing liq pnl threshold as leverage increases, capped between two values)
* `getTradeLiquidationPrice`: pass the stored trade's liquidation params
* `getTradeValueCollateral`: pass `borrowingFeesCollateral + _closingFeesCollateral` to `getTradeValuePure` instead of passing them separately, and also pass `getTradeLiqPnlThresholdP(_trade)`
* `getTradeOpeningPriceImpact`: New view helper to calculate the price impact % and price after spread + price impact for trade openings / partial adds
* `getTradeClosingPriceImpact`: New view helper that returns the closing price impact %, price after closing spread and impact, and net trade value used to determine whether net pnl is positive or not; returns early 0% price impact for trades opened before v9.2, otherwise calculates net trade value (taking into account pnl, spread, price impact, closing fees, and borrowing fees) without protection factor and compares it to the initial trade collateral to determine the `_isPnlPositive` value passed to `getTradePriceImpact` which determines the final returned `priceImpactP` and `priceAfterImpact` values
* `getTradeLiqPnlThresholdP`: new getter that returns the value of `getLiqPnlThresholdP` by passing the trade's stored liquidation params and its leverage
* `getTotalClosingFeesCollateral`: new getter that returns a trade's closing fee based on its `_collateralIndex`/`_pairIndex`/`_positionSizeCollateral`, used in `BorrowingFeesUtils.getTradeLiquidationPrice`/`TradingCommonUtils.getTradeClosingPriceImpact`/`DecreasePositionSizeUtils.validateRequest`
* Renamed `addOiCollateral` to `updateOi`, now accepts `_open` argument and passes it to `handleTradeBorrowingCallback` and `addPriceImpactOpenInterest`
* Renamed `addTradeOiCollateral` to `updateOiTrade`, now accepts `_open` argument and passes it to `updateOi`
* Removed `removeOiCollateral` and `removeTradeOiCollateral`
* `handleOiDelta` now uses `updateOi`, passes `_open = true` when `_newPositionSizeCollateral > existingPositionSizeCollateral` and `_open = false` when `_newPositionSizeCollateral < existingPositionSizeCollateral`

### libraries/TradingInteractionsUtils.sol (update)

* New `updateMaxClosingSlippageP` function that simply calls `GNSTradingStorage.updateTradeMaxClosingSlippageP`
* `closeTradeMarket`: now accepts `_expectedPrice`, reverts if `_expectedPrice` is 0, sets `pendingOrder.trade.openPrice` to `_expectedPrice`, passes trade id to `getPrice`
* `decreasePositionSize`: now accepts `_expectedPrice`, passes it to `requestDecreasePositionSize`
* `triggerOrder`: no longer turn LIQ orders into SL orders when SL is closer, uses `TradingCommonUtils.getTradeOpeningPriceImpact` to calculate `priceImpactP`
* `_openTrade`: uses `TradingCommonUtils.getTradeOpeningPriceImpact` to calculate `priceImpactP`, passes trade id to `getPrice`
* `_getPriceTriggerOrder`: passes trade id to `getPrice`

### libraries/TradingStorageUtils.sol (update)

* `storeTrade`: Fetches the corresponding pair's liquidation params and stores them into the new `tradeLiquidationParams` mapping, limits the trade SL distance using the new `_limitTradeSlDistance` helper, sets `_tradeInfo.contractsVersion` to the current version using `getCurrentContractsVersion()`, sets `_tradeInfo.lastPosIncreaseBlock` to current block, no longer sets `_tradeInfo.lastOiUpdateTs = 0` because corresponding logic was deprecated, calls `updateOiTrade` with `_open = true` instead of `addTradeOiCollateral`, emits the trade's liquidation params
* New `updateTradeMaxClosingSlippageP` setter
* `updateTradePosition`/`updateOpenOrderDetails`/`updateTradeSl`: limit the trade's SL distance using the new `_limitTradeSlDistance` helper instead of `_limitSlDistance`
* `updateTradePosition`: new `_isPartialIncrease` argument, sets the trade's stored liquidation params to the current pair liquidation params when `_isPartialIncrease = true` and sets `i.lastPosIncreaseBlock` to current block, emits `_isPartialIncrease` in `TradePositionUpdated` event
* `closeTrade`: calls `updateOiTrade` with `_open = false` instead of `removeTradeOiCollateral`
* `getTradeLiquidationParams`: new getter to return the new `tradeLiquidationParams` mapping values
* `getCurrentContractsVersion`: new getter to return the current contracts version (will need to be updated at each significant update)
* `_limitSlDistance`: accepts new `_liqPnlThresholdP` argument, calculates `minSlP` as `_liqPnlThresholdP - ConstantsUtils.SL_LIQ_BUFFER_P`, uses calculated `minSlP` instead of `ConstantsUtils.MAX_SL_P`, adjustments done for `1e10` precision of `minSlP`
* `_limitTradeSlDistance`: new view wrapper accepting `_trade` and `_newSl` arguments; returns a trade's capped SL distance based on its open price, leverage, new stop loss value, whether it's a long or a short, and based on its liquidation pnl % threshold fetched using `TradingCommonUtils.getTradeLiqPnlThresholdP(_trade)`

### libraries/updateLeverage/UpdateLeverageLifecycles.sol (update)

* `_initiateRequest`: small adjustment to avoid stack too deep, now passes trade id to `getPrice`
* `_prepareCallbackValues`: pass stored trade liquidation params in `values.liqPrice` calculation
* `_handleCallback`: pass `_isPartialIncrease = false` to `updateTradePosition`

### libraries/updatePositionSize/DecreasePositionSizeUtils.sol (update)

* `validateRequest`: calculate `closingFeesCollateral` using new `TradingCommonUtils.getTotalClosingFeesCollateral` helper, revert if `input.expectedPrice` is 0
* `prepareCallbackValues`: added `values.priceAfterImpact` calculation using new `TradingCommonUtils.getTradeClosingPriceImpact` helper, `values.existingPnlCollateral` now uses `uint64(values.priceAfterImpact)` instead of `_answer.price`
* `validateCallback`: now accepts `_pendingOrder` (to fetch expected price), added max slippage calculations and corresponding cancel reason check (uses default max slippage % if `tradeInfo.maxSlippageP` not set)
* `updateTradeSuccess`: pass `_isPartialIncrease = false` to `updateTradePosition`

### libraries/updatePositionSize/IncreasePositionSizeUtils.sol (update)

* `prepareCallbackValues`: Uses `TradingCommonUtils.getTradeOpeningPriceImpact` to calculate `values.priceAfterImpact`, passes current pair liquidation params to `getTradeLiquidationPrice` for `values.newLiqPrice` calculation (already checks using new liquidation params since trade liquidation params will be refreshed on success callback)
* `updateTradeSuccess`: pass `_isPartialIncrease = true` to `updateTradePosition`

### libraries/updatePositionSize/UpdatePositionSizeLifecycles.sol (update)

* `requestDecreasePositionSize`: passes `_input.expectedPrice` to `_initiateRequest`
* `_initiateRequest`: passes trade id to `getPrice`
* `executeIncreasePositionSizeMarket`/`executeDecreasePositionSizeMarket`: pass trade long and current collateral/usd price to event
* `executeDecreasePositionSizeMarket`: pass `_order` to `DecreasePositionSizeUtils.validateCallback`
