description |
---|
Best Practices for using Pyth Price Feeds |
This page provides some technical details about Pyth price feeds that are necessary to use them safely and correctly. Please read this page before using Pyth price feeds in your application.
Each Pyth Network price feed is stored in a Solana account and is uniquely identified by its corresponding Solana account key. Price feeds have different ids in mainnet than in testnet or devnet. The full list of price feeds available on Solana is listed on the pyth.network website under the "mainnet-beta" tag. The price feed ids page lists the id of each available price feed. To use a price feed on-chain, look up its id using this page, then store the feed id in your program. You should then pass in the corresponding Solana account to any instruction that requires the current price and validate in your program that the account key matches the stored price feed id.
Price feeds represent numbers in a fixed-point format. The same exponent is used for both the price and confidence interval. The integer representation of these values can be computed by multiplying by 10^exponent
. As an example, imagine Pyth reported the following values for AAPL/USD:
Field | Value |
---|---|
exponent |
-5 |
conf |
1500 |
price |
12276250 |
The confidence interval is 1500 * 10^(-5) = $0.015
, and the price is 12276250 * 10^(-5) = $122.7625
.
Sometimes, Pyth will not be able to provide a current price for a product. This situation can happen for various reasons. For example, US equity markets only trade during certain hours, and outside those hours, it's not clear what an equity's price is. Alternatively, a network outage (at the internet level, blockchain level, or at multiple data providers) may prevent the protocol from producing new price updates. (Such outages are unlikely, but integrators should still be prepared for the possibility.) In such cases, Pyth may return a stale price for the product.
Integrators should be careful to avoid accidentally using a stale price.
The SDKs guard against this failure mode by incorporating a staleness check by default.
Querying the current price will fail if too much time has elapsed since the last update.
The SDKs expose this failure condition in an idiomatic way: for example, the Rust SDK may return None
, and our Solidity SDK may revert the transaction.
The SDK provides a sane default for the staleness threshold, but users may configure it to suit their use case.
Pyth price feeds follow the traditional market hours of each asset classes and will be available at the following hours:
Asset Class | Opening Hours | Exceptions |
---|---|---|
Crypto | 24/7 | No market close |
US Equities | Every weekday from 9.30AM ET to 4PM ET | Markets are closed on weekends, US Holidays, and during extraordinary events |
FX | From Sunday 5PM ET to Friday 5PM ET | Trading continues during most US holidays |
Metals | From Sunday 5PM ET to Friday 5PM ET | Spot gold and silver trading also follow CME holiday closures |
Developers integrating Pyth Network price feeds should account for the difference in latency between on-chain oracles and off-chain sources (e.g. centralized exchanges). Although Pyth Network is designed with low latency in mind, no on-chain oracle can match the latency of an off-chain source due to the added overhead for consensus and security. The threat model for integrating protocols should assume that adversaries see price changes a short time before the protocol does. In this threat model, protocol designers should avoid situations where a Pyth price update must race against an adversary's transaction. Adversaries are highly likely to win these races, as they have a head start, and sophisticated adversaries can additionally optimize their network latencies or pay miners for priority blockspace.
This situation is analogous to market making in traditional finance. Market makers place resting orders on exchanges with the hope of earning the bid/ask spread. When the “true price” moves, these market makers get picked off by adverse “smart flow” that is faster than they are. The smart flow is balanced by two-way flow, that is, people wanting to trade for other reasons besides a price change.
This analogy suggests two simple solutions to races:
- Configure protocol parameters to balance the losses from smart flow against the gains from two-way flow. Market makers in traditional finance implement this approach by offering a bid/ask spread and limited liquidity. The limited liquidity caps the losses to smart flow, while still earning profits from the two-way flow. A successful market maker tunes the spread and offered liquidity to limit adverse selection from smart traders while still interacting with two-way flow.
- Give the protocol a "last look" to decide which transactions to accept. In traditional finance, some exchanges give market makers a chance to walk back a trade offer after someone else has requested it. Protocols can implement this technique by splitting transactions into two parts: a request and a fulfillment. In the first transaction, the user requests to perform an action. In the second transaction, the protocol chooses whether or not to fulfill the user's request; this step can be implemented as a permissionless operation. The protocol can require a short delay between the two transactions, and the user's request gets fulfilled at the Pyth price as of the second transaction. This technique gives the protocol extra time to observe price changes, giving it a head start in the latency race.
At every point in time, Pyth publishes both a price and a confidence interval for each product. For example, Pyth may publish the current price of bitcoin as $50000 ± $10. Pyth publishes a confidence interval because, in real markets, there is no one single price for a product. For example, at any given time, bitcoin trades at different prices at different venues around the world. While these prices are typically similar, they can diverge for a number of reasons, such as when a cryptocurrency exchange blocks withdrawals on an asset. If this happens, prices diverge because arbitrageurs can no longer bring prices across exchanges into line. Alternatively, prices on different venues can differ simply because an asset is highly volatile at a particular point in time. At such times, bid/ask spreads tend to be wider, and trades on different markets at around the same time tend to occur at a wider range of prices.
Pyth represents these possibly-different prices by giving its users a probability distribution over price instead of just a single price. Pyth models the price according to a Laplace distribution centered on the Pyth aggregate price with a standard deviation equal to the confidence interval (the scale parameter b of the Laplace distribution is equal to the standard deviation divided by the square root of 2). The Laplace distribution contains ~95% of the probability mass within ~2.12 standard deviations (~3 times the scale parameter). If markets are behaving normally, then the confidence interval will be tight -- typically much less than 1% of the price -- and the Laplace distribution will be highly peaked. However, at unusual times, the confidence interval can widen out dramatically.
When consuming Pyth prices, we recommend using the confidence interval to protect your users from these unusual market conditions. The simplest way to do so is to use Pyth's confidence interval to compute a range in which the true price (probably) lies. You obtain this range by adding and subtracting a multiple of the confidence interval to the Pyth price; the bigger the multiple, the more likely the price lies within that range. We recommend considering a multiple of 2.12, which as mentioned above gives you a 95% probability that the true price is within the range (assuming Laplace distribution estimates are correct). Then, select the most conservative price within that range for every action. In other words, your protocol should minimize state changes during times of large price uncertainty.
This principle is common sense. Imagine that you are lending money to a friend, and your friend pledges a bitcoin as collateral. Also imagine that Pyth says the bitcoin price is $50000 +- $1000. (Note that $1000 is an unusually large confidence interval for bitcoin; the confidence interval is typically ~$50 dollars). You therefore calculate that the true price is between $47880 and $52120 using the multiply by 3 rule from above. When originating the loan, you would value the bitcoin at $47880. The lower price is conservative in this instance because it limits the amount of borrowing that is possible while the price is uncertain. On the other hand, once the loan has been issued, you would value the bitcoin at $52120. The higher price is conservative, as it prevents you from liquidating your friend purely due to increased price uncertainty.
The same principle would apply if you wrote a derivative contract. If someone wants to open a derivative contract with you, you would value their collateral at the lower price. However, if you were deciding whether someone's margin limits were violated, you would value their collateral at the higher price. If a contract needs to be settled at a price, you could take approaches such as the following:
- Using Pyth's exponential moving average price, which represents estimates of the average price of the asset over a specified time period (e.g., over the past 1 hour). The exponential moving average price is computed such that it lessens the influence of prices with wide confidence intervals. You may find more details in ema-price-aggregation.md.
- Using the aggregate price, which is Pyth's best estimate of the price at a single point in time. The quality of this estimate depends on the width of the confidence interval at settlement time and on occasion, it may be imprecise. However, it is the best you can do with Pyth data if you need a single price at that exact point in time.
- Defining the contract to depend on confidence. For example, you could create an option that refunds the option premium to the buyer (so both sides of the transaction are even) if the strike price is within the confidence interval at settlement time. You could also create a contract that delayed settlement until the confidence interval was sufficiently small. If you choose this second option, you should ensure that your contract is guaranteed to eventually settle even if the confidence interval never narrows.