Struct Price

Source
pub struct Price {
    pub price: i64,
    pub conf: u64,
    pub expo: i32,
    pub publish_time: UnixTimestamp,
}
Expand description

A price with a degree of uncertainty at a certain time, represented as a price +- a confidence interval.

Please refer to the documentation at https://docs.pyth.network/consumers/best-practices for using this price safely.

The confidence interval roughly corresponds to the standard error of a normal distribution. Both the price and confidence are stored in a fixed-point numeric representation, x * 10^expo, where expo is the exponent. For example:

use pyth_sdk::Price;
Price { price: 12345, conf: 267, expo: -2, publish_time: 100 }; // represents 123.45 +- 2.67 published at UnixTimestamp 100
Price { price: 123, conf: 1, expo: 2,  publish_time: 100 }; // represents 12300 +- 100 published at UnixTimestamp 100

Price supports a limited set of mathematical operations. All of these operations will propagate any uncertainty in the arguments into the result. However, the uncertainty in the result may overestimate the true uncertainty (by at most a factor of sqrt(2)) due to computational limitations. Furthermore, all of these operations may return None if their result cannot be represented within the numeric representation (e.g., the exponent is so small that the price does not fit into an i64). Users of these methods should (1) select their exponents to avoid this problem, and (2) handle the None case gracefully.

Fields§

§price: i64

Price.

§conf: u64

Confidence interval.

§expo: i32

Exponent.

§publish_time: UnixTimestamp

Publish time.

Implementations§

Source§

impl Price

Source

pub fn get_price_in_quote( &self, quote: &Price, result_expo: i32, ) -> Option<Price>

Get the current price of this account in a different quote currency.

If this account represents the price of the product X/Z, and quote represents the price of the product Y/Z, this method returns the price of X/Y. Use this method to get the price of e.g., mSOL/SOL from the mSOL/USD and SOL/USD accounts.

result_expo determines the exponent of the result, i.e., the number of digits below the decimal point. This method returns None if either the price or confidence are too large to be represented with the requested exponent.

Example:

let btc_usd: Price = ...;
let eth_usd: Price = ...;
// -8 is the desired exponent for the result
let btc_eth: Price = btc_usd.get_price_in_quote(&eth_usd, -8);
println!("BTC/ETH price: ({} +- {}) x 10^{}", price.price, price.conf, price.expo);
Source

pub fn get_collateral_valuation_price( &self, deposits: u64, deposits_endpoint: u64, rate_discount_initial: u64, rate_discount_final: u64, discount_exponent: i32, ) -> Option<Price>

Get the valuation of a collateral position according to:

  1. the net amount currently deposited (across the protocol)
  2. the deposits endpoint for the affine combination (across the protocol)
  3. the initial (at 0 deposits) and final (at the deposits endpoint) valuation discount rates

We use a linear interpolation between the the initial and final discount rates, scaled by the proportion of the deposits endpoint that has been deposited. This essentially assumes a linear liquidity cumulative density function, which has been shown to be a reasonable assumption for many crypto tokens in literature. For more detail on this: https://pythnetwork.medium.com/improving-lending-protocols-with-liquidity-oracles-fd1ea4f96f37

If the assumptions of the liquidity curve hold true, we are obtaining a lower bound for the net price at which one can sell the quantity of token specified by deposits in the open markets. We value collateral according to the total deposits in the protocol due to the present intractability of assessing collateral at risk by price range.

Args deposits: u64, quantity of token deposited in the protocol deposits_endpoint: u64, deposits right endpoint for the affine combination rate_discount_initial: u64, initial discounted rate at 0 deposits (units given by discount_exponent) rate_discount_final: u64, final discounted rate at deposits_endpoint deposits (units given by discount_exponent) discount_exponent: u64, the exponent to apply to the discounts above (e.g. if discount_final is 10 but meant to express 0.1/10%, exponent would be -2) note that if discount_initial is bigger than 100% per the discount exponent scale, then the initial valuation of the collateral will be higher than the oracle price

Source

pub fn get_borrow_valuation_price( &self, borrows: u64, borrows_endpoint: u64, rate_premium_initial: u64, rate_premium_final: u64, premium_exponent: i32, ) -> Option<Price>

Get the valuation of a borrow position according to:

  1. the net amount currently borrowed (across the protocol)
  2. the borrowed endpoint for the affine combination (across the protocol)
  3. the initial (at 0 borrows) and final (at the borrow endpoint) valuation premiums

We use a linear interpolation between the the initial and final premiums, scaled by the proportion of the borrows endpoint that has been borrowed out. This essentially assumes a linear liquidity cumulative density function, which has been shown to be a reasonable assumption for many crypto tokens in literature. For more detail on this: https://pythnetwork.medium.com/improving-lending-protocols-with-liquidity-oracles-fd1ea4f96f37

If the assumptions of the liquidity curve hold true, we are obtaining an upper bound for the net price at which one can buy the quantity of token specified by borrows in the open markets. We value the borrows according to the total borrows out of the protocol due to the present intractability of assessing collateral at risk and repayment likelihood by price range.

Args borrows: u64, quantity of token borrowed from the protocol borrows_endpoint: u64, borrows right endpoint for the affine combination rate_premium_initial: u64, initial premium at 0 borrows (units given by premium_exponent) rate_premium_final: u64, final premium at borrows_endpoint borrows (units given by premium_exponent) premium_exponent: u64, the exponent to apply to the premiums above (e.g. if premium_final is 50 but meant to express 0.05/5%, exponent would be -3) note that if premium_initial is less than 100% per the premium exponent scale, then the initial valuation of the borrow will be lower than the oracle price

Source

pub fn affine_combination( x1: i64, y1: Price, x2: i64, y2: Price, x_query: i64, pre_add_expo: i32, ) -> Option<Price>

affine_combination performs an affine combination of two prices located at x coordinates x1 and x2, for query x coordinate x_query Takes in 2 points and a 3rd “query” x coordinate, to compute the value at x_query Effectively draws a line between the 2 points and then proceeds to interpolate/extrapolate to find the value at the query coordinate according to that line

affine_combination gives you the Price, scaled to a specified exponent, closest to y2 * ((xq-x1)/(x2-x1)) + y1 * ((x2-x3)/(x2-x1)) If the numerators and denominators of the fractions there are both representable within 8 digits of precision and the fraction itself is also representable within 8 digits of precision, there is no loss due to taking the fractions. If the prices are normalized, then there is no loss in taking the products via mul. Otherwise, the prices will be converted to a form representable within 8 digits of precision. The scaling to the specified expo pre_add_expo introduces a max error of 2*10^pre_add_expo. If pre_add_expo is small enough relative to the products, then there is no loss due to scaling. If the fractions are expressable within 8 digits of precision, the ys are normalized, and the exponent is sufficiently small, then you get an exact result. Otherwise, your error is bounded as given below.

Args x1: i64, the x coordinate of the first point y1: Price, the y coordinate of the first point, represented as a Price struct x2: i64, the x coordinate of the second point, must be greater than x1 y2: Price, the y coordinate of the second point, represented as a Price struct x_query: i64, the query x coordinate, at which we wish to impute a y value pre_add_expo: i32, the exponent to scale to, before final addition; essentially the final precision you want

Logic imputed y value = y2 * ((xq-x1)/(x2-x1)) + y1 * ((x2-x3)/(x2-x1))

  1. compute A = xq-x1
  2. compute B = x2-xq
  3. compute C = x2-x1
  4. compute D = A/C
  5. compute E = B/C
  6. compute F = y2 * D
  7. compute G = y1 * E
  8. compute H = F + G

Bounds due to precision loss x = 10^(PD_EXPO+2) fraction (due to normalization & division) incurs max loss of x Thus, max loss here: Err(D), Err(E) <= x If y1, y2 already normalized, no additional error. O/w, Err(y1), Err(y2) with normalization <= x Err(F), Err(G) <= (1+x)^2 - 1 (in fractional terms) ~= 2x Err(H) <= 22x = 4x, when PD_EXPO = -9 ==> Err(H) <= 410^-7

Scaling this back has error bounded by the expo (10^pre_add_expo). This is because reverting a potentially finer expo to a coarser grid has the potential to be off by the order of the atomic unit of the coarser grid. This scaling error combines with the previous error additively: Err <= 4x + 2*10^pre_add_expo But if pre_add_expo is reasonably small (<= -9), then other term will dominate

Note that if the ys are unnormalized due to the confidence but not the price, the normalization could zero out the price fields. Based on this, it is recommended that input prices are normalized, or at least do not contain huge discrepancies between price and confidence.

Source

pub fn price_basket( amounts: &[(Price, i64, i32)], result_expo: i32, ) -> Option<Price>

Get the price of a basket of currencies.

Each entry in amounts is of the form (price, qty, qty_expo), and the result is the sum of price * qty * 10^qty_expo. The result is returned with exponent result_expo.

An example use case for this function is to get the value of an LP token.

Example:

let btc_usd: Price = ...;
let eth_usd: Price = ...;
// Quantity of each asset in fixed-point a * 10^e.
// This represents 0.1 BTC and .05 ETH.
// -8 is desired exponent for result
let basket_price: Price = Price::price_basket(&[
    (btc_usd, 10, -2),
    (eth_usd, 5, -2)
  ], -8);
println!("0.1 BTC and 0.05 ETH are worth: ({} +- {}) x 10^{} USD",
         basket_price.price, basket_price.conf, basket_price.expo);
Source

pub fn div(&self, other: &Price) -> Option<Price>

Divide this price by other while propagating the uncertainty in both prices into the result.

This method will automatically select a reasonable exponent for the result. If both self and other are normalized, the exponent is self.expo + PD_EXPO - other.expo (i.e., the fraction has PD_EXPO digits of additional precision). If they are not normalized, this method will normalize them, resulting in an unpredictable result exponent. If the result is used in a context that requires a specific exponent, please call scale_to_exponent on it.

Source

pub fn add(&self, other: &Price) -> Option<Price>

Add other to this, propagating uncertainty in both prices.

Requires both Prices to have the same exponent – use scale_to_exponent on the arguments if necessary.

TODO: could generalize this method to support different exponents.

Source

pub fn cmul(&self, c: i64, e: i32) -> Option<Price>

Multiply this Price by a constant c * 10^e.

Source

pub fn mul(&self, other: &Price) -> Option<Price>

Multiply this Price by other, propagating any uncertainty.

Source

pub fn normalize(&self) -> Option<Price>

Get a copy of this struct where the price and confidence have been normalized to be between MIN_PD_V_I64 and MAX_PD_V_I64.

Source

pub fn scale_to_exponent(&self, target_expo: i32) -> Option<Price>

Scale this price/confidence so that its exponent is target_expo.

Return None if this number is outside the range of numbers representable in target_expo, which will happen if target_expo is too small.

Warning: if target_expo is significantly larger than the current exponent, this function will return 0 +- 0.

Trait Implementations§

Source§

impl BorshDeserialize for Price

Source§

fn deserialize_reader<R: Read>(reader: &mut R) -> Result<Self, Error>

Source§

fn deserialize(buf: &mut &[u8]) -> Result<Self, Error>

Deserializes this instance from a given slice of bytes. Updates the buffer to point at the remaining bytes.
Source§

fn try_from_slice(v: &[u8]) -> Result<Self, Error>

Deserialize this instance from a slice of bytes.
Source§

fn try_from_reader<R>(reader: &mut R) -> Result<Self, Error>
where R: Read,

Source§

impl BorshSerialize for Price

Source§

fn serialize<W: Write>(&self, writer: &mut W) -> Result<(), Error>

Source§

fn try_to_vec(&self) -> Result<Vec<u8>, Error>

Serialize this instance into a vector of bytes.
Source§

impl Clone for Price

Source§

fn clone(&self) -> Price

Returns a copy of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Price

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for Price

Source§

fn default() -> Price

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for Price

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl JsonSchema for Price

Source§

fn schema_name() -> String

The name of the generated JSON Schema. Read more
Source§

fn schema_id() -> Cow<'static, str>

Returns a string that uniquely identifies the schema produced by this type. Read more
Source§

fn json_schema(generator: &mut SchemaGenerator) -> Schema

Generates a JSON Schema for this type. Read more
Source§

fn is_referenceable() -> bool

Whether JSON Schemas generated for this type should be re-used where possible using the $ref keyword. Read more
Source§

impl PartialEq for Price

Source§

fn eq(&self, other: &Price) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Serialize for Price

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl Copy for Price

Source§

impl Eq for Price

Source§

impl StructuralPartialEq for Price

Auto Trait Implementations§

§

impl Freeze for Price

§

impl RefUnwindSafe for Price

§

impl Send for Price

§

impl Sync for Price

§

impl Unpin for Price

§

impl UnwindSafe for Price

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,