diff --git a/.bumpversion.cfg b/.bumpversion.cfg index a7a6b6fa..7f358936 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.7.37 +current_version = 0.7.38 commit = True tag = True tag_name = {new_version} diff --git a/CHANGELOG.md b/CHANGELOG.md index b917a1d8..b9d1d1c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -194,4 +194,8 @@ Add support for prelisting oracles ## [0.7.37] - 2024-3-11 -Fix bug where prelisting & switchboard oracles weren't properly decoded for cached drift client subscriptions \ No newline at end of file +Fix bug where prelisting & switchboard oracles weren't properly decoded for cached drift client subscriptions + +## [0.7.38] - 2024-3-11 + +Add max confidence interval multiplier to oracle validity calculations \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index 84b5a269..0907ed40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "driftpy" -version = "0.7.37" +version = "0.7.38" description = "A Python client for the Drift DEX" authors = ["x19 ", "bigz ", "frank "] license = "MIT" diff --git a/src/driftpy/__init__.py b/src/driftpy/__init__.py index 428e6c96..bbe5ee36 100644 --- a/src/driftpy/__init__.py +++ b/src/driftpy/__init__.py @@ -1 +1 @@ -__version__ = "0.7.37" +__version__ = "0.7.38" diff --git a/src/driftpy/math/oracles.py b/src/driftpy/math/oracles.py index cfca7598..54c5f104 100644 --- a/src/driftpy/math/oracles.py +++ b/src/driftpy/math/oracles.py @@ -1,5 +1,12 @@ from driftpy.constants.numeric_constants import BID_ASK_SPREAD_PRECISION, FIVE_MINUTE -from driftpy.types import AMM, HistoricalOracleData, OracleGuardRails, OraclePriceData +from driftpy.types import ( + AMM, + ContractTier, + HistoricalOracleData, + OracleGuardRails, + OraclePriceData, + PerpMarketAccount, +) def calculate_live_oracle_twap( @@ -77,13 +84,15 @@ def get_new_oracle_conf_pct( def is_oracle_valid( - amm: AMM, + market: PerpMarketAccount, oracle_price_data: OraclePriceData, oracle_guard_rails: OracleGuardRails, slot: int, ) -> bool: is_oracle_price_non_positive = oracle_price_data.price <= 0 + amm = market.amm + lhs = ( oracle_price_data.price // (max(1, amm.historical_oracle_data.last_oracle_price_twap)) @@ -95,10 +104,15 @@ def is_oracle_valid( is_oracle_price_too_volatile = lhs or rhs + max_confidence_multiplier = get_max_confidence_interval_multiplier(market) + is_confidence_too_large = ( (max(1, oracle_price_data.confidence) * BID_ASK_SPREAD_PRECISION) // oracle_price_data.price - ) > oracle_guard_rails.validity.confidence_interval_max_size + ) > ( + oracle_guard_rails.validity.confidence_interval_max_size + * max_confidence_multiplier + ) is_oracle_stale = ( slot - oracle_price_data.slot @@ -111,3 +125,17 @@ def is_oracle_valid( or is_oracle_price_too_volatile or is_confidence_too_large ) + + +def get_max_confidence_interval_multiplier(market: PerpMarketAccount) -> int: + match market.contract_tier: + case ContractTier.A(): + return 1 + case ContractTier.B(): + return 1 + case ContractTier.C(): + return 2 + case ContractTier.Speculative(): + return 10 + case ContractTier.Isolated(): + return 50 diff --git a/tests/math/spreads.py b/tests/math/spreads.py index 94ab6dfc..8027845b 100644 --- a/tests/math/spreads.py +++ b/tests/math/spreads.py @@ -702,35 +702,37 @@ async def test_live_update_functions(): ) # good oracle - assert is_oracle_valid(amm, oracle_price_data, oracle_guard_rails, slot + 5) + assert is_oracle_valid(perp_market, oracle_price_data, oracle_guard_rails, slot + 5) # too confident oracle_price_data.confidence = int(13.553 * PRICE_PRECISION * 0.021) - assert not is_oracle_valid(amm, oracle_price_data, oracle_guard_rails, slot) + assert not is_oracle_valid(perp_market, oracle_price_data, oracle_guard_rails, slot) # not enough data points oracle_price_data.confidence = 1 oracle_price_data.has_sufficient_number_of_data_points = False - assert not is_oracle_valid(amm, oracle_price_data, oracle_guard_rails, slot) + assert not is_oracle_valid(perp_market, oracle_price_data, oracle_guard_rails, slot) # negative oracle price oracle_price_data.has_sufficient_number_of_data_points = True oracle_price_data.price = -1 * PRICE_PRECISION - assert not is_oracle_valid(amm, oracle_price_data, oracle_guard_rails, slot) + assert not is_oracle_valid(perp_market, oracle_price_data, oracle_guard_rails, slot) # too delayed for amm oracle_price_data.price = int(13.553 * PRICE_PRECISION) - assert not is_oracle_valid(amm, oracle_price_data, oracle_guard_rails, slot + 100) + assert not is_oracle_valid( + perp_market, oracle_price_data, oracle_guard_rails, slot + 100 + ) # oracle slot is stale (should be valid) oracle_price_data.slot += 100 - assert is_oracle_valid(amm, oracle_price_data, oracle_guard_rails, slot) + assert is_oracle_valid(perp_market, oracle_price_data, oracle_guard_rails, slot) # too volatile (>5x higher) oracle_price_data.slot = slot + 5 oracle_price_data.price = int(113.553 * PRICE_PRECISION) - assert not is_oracle_valid(amm, oracle_price_data, oracle_guard_rails, slot) + assert not is_oracle_valid(perp_market, oracle_price_data, oracle_guard_rails, slot) # too volatile (>5x lower) oracle_price_data.price = int(0.553 * PRICE_PRECISION) - assert not is_oracle_valid(amm, oracle_price_data, oracle_guard_rails, slot) + assert not is_oracle_valid(perp_market, oracle_price_data, oracle_guard_rails, slot)