Curve Lp Metrics
Bayesian Online Changepoint Detection models for predicting depegs on Curve pools.
Install / Use
npx skills add xenophonlabs/curve-lp-metricsInstalls into whichever agent you are using.
README
Curvemetrics
Welcome to Curvemetrics, a stablecoin and liquid staking derivative depeg detector for Curve's StableSwap pools. Curvemetrics consists of two primary components:
- Metrics - Our metrics are designed to capture leading indicators of potential depegs.
- Bayesian Online Changepoint Detection - Our BOCD models are trained on historical Curve data, and listen to changes in metrics data in real-time to detect potential changepoints (depegs).
You may listen to detected depegs or query our metrics by using our API. You may also listen to potential depegs by following and turning notification on for our Twitter bot. The theoretical underpinning of Curvemetrics is available in our corresponding research paper.
This codebase and research paper were developed by Xenophon Labs and sponsored by the Cuve Analytics team. If there are any questions, please reach out to thomas@xenophonlabs.com.
Overview
In our paper, we provided promising evidence for how quantitative metrics and detection algorithms can be constructed to keep liquidity providers (LPs) informed, in real-time, regarding potential stablecoin and liquid staking derivative depegs. The metrics and models we developed are available in this API. We first describe the API usage, then we overview each metric, how Bayesian Online Changepoint Detectors works, and the results of our study very briefly in this README. For further information, refer to our paper. Finally, we describe how to reconstruct our database and API in your own machine.

High-level Figma diagram of Curvemetrics Architecture
The BOCD models are currently deployed for the following metrics and pools:
POOL_METRICS = {"shannonsEntropy", "netSwapFlow", "300.Markout"}
MODELED_POOLS = [
"0xdc24316b9ae028f1497c275eb9192a3ea0f67022", # ETH/stETH
"0xbebc44782c7db0a1a60cb6fe97d0b483032ff1c7", # 3pool
"0xdcef968d416a41cdac0ed8702fac8128a64241a2", # FRAX/USDC
"0xa1f8a6807c402e4a15ef4eba36528a3fed24e577", # ETH/frxETH
]
API Usage
Our Flask API can be accessed with simple HTTP GET requests. For example, we may query the pool_metrics table for the 3pool, between January 1st 2023, and June 13th 2023 as follows:
curl "http://172.104.8.91/pool_metrics?&pool_id=0xbebc44782c7db0a1a60cb6fe97d0b483032ff1c7&metric=shannonsEntropy&start=1672531200&end=1686689907" > example.json
We first obtain the pool id, then convert the dates to UNIX timestamps, and query the pool_data endpoint. By default, this returns the token balances and output LP token supply for the pool at each block. As it stands, our API frontfills data, computes metrics, and runs inference on our BOCD models every hour on the hour. For an example using Python, refer to README.ipynb!
API Endpoints
Below is a summary of available API endpoints and their descriptions. Each endpoint corresponds to a Postgres table, where the pool_id and timestamps often correspond to the table's primary key.
| Endpoint | Description | Parameters |
| -------- | ----------- | ---------- |
| /pools | Get metadata for all pools | None |
| /tokens | Get metadata for all tokens | None |
| /block_timestamps | Get the timestamp for a specific block | block |
| /pool_data | Get pool token balances, LP token supply, etc.. | pool_id, start, end, cols (optional, default: ['inputTokenBalances', 'timestamp', 'outputTokenSupply']) |
| /swaps_data | Get swap data for a specific pool | pool_id, start, end |
| /lp_data | Get deposit & withdrawal data for a specific pool | pool_id, start, end |
| /snapshots | Get daily snapshot data for a specific pool | pool_id, start, end, cols (optional, default: ['timestamp', 'normalizedReserves', 'reserves', 'virtualPrice', 'lpPriceUSD', 'tvl', 'reservesUSD']) |
| /ohlcv | Get OHLCV data for a specific token | token_id, start, end |
| /pool_metrics | Get specific pool metrics | pool_id, metric, start, end, cols (optional, default: ['timestamp', 'value']) |
| /token_metrics | Get specific token metrics | token_id, metric, start, end, cols (optional, default: ['timestamp', 'value']) |
| /changepoints | Get changepoints for a specific pool or token and model | address, model, metric, start, end, cols (optional, default: ['timestamp']) |
| /takers | Get information about takers | None |
| /sharks | Get list of sharks | top (optional, default: 0.9) |
| /pool_X | Get the underlying signal for a specific pool metric (e.g. log diffs of Shannon's Entropy) at a specific frequency | pool_id, metric, start, end, freq (optional, default: timedelta(hours=1))|
| /token_X | Get the underlying signal for a specific token metric | token_id, metric, start, end, freq (optional, default: timedelta(hours=1))|
start and end are always UNIX timestamps, cols are always the strings of table columns (you can find them in curvemetrics/src/classes/entities/), model is either the baseline model or a bocd model (more info on baseline in our paper), and metric is one of:
shannonsEntropy300.MarkoutnetSwapFlow
top indicates the top percentage of takers you would like to consider as sharks. Finally, freq is a timedelta object indicating the sampling period you would like to see on the pool_X and token_X metrics (although it can also be a string such as 1h).
Notice that pools and tokens are meta tables: they provide basic information on the supported pools and tokens. The block_timestamps is simply a relation from UNIX timestamp <-> block. The pool_data, swaps_data, lp_data and snapshots tables are all gleaned from GraphQL subgraphs. The ohlcv table contains either CCXT or Chainlink pricing data on each token (as specified in config.json). The pool_metrics and token_metrics tables contain the metrics we compute, the changepoints table contains the changepoints computed by our models. The takers table contains the cumulative markout of all users that have swapped on the supported pools as defined in our paper, and the sharks endpoint filters for the top percentage of takers. Finally, the pool_X and token_X take a pool or token metric and convert it to the corresponding signal we use for our models. For example, for shannonsEntropy we compute pool_X = np.log1p(metric.pct_change()), meaning we take the log differences.
Listening to Changepoints
The easy way to listen to changepoints is by following our Twitter bot. To listen to changepoints programatically, you may query for the latest changepoint for your desired pool and metric. For example, you may query:
curl "http://172.104.8.91/changepoints?address=0xbebc44782c7db0a1a60cb6fe97d0b483032ff1c7&model=bocd&metric=shannonsEntropy&start=<start>&end=<end>" > example.json
to check if any changepoints occured on the 3pool according to our Entropy BOCD model between <start> and <end>.
Metrics
We have developed several metrics for detecting depegs for Curve's StableSwap pool. The metrics are summarized in the table below. We look to identify changes in market behavior that precede changes in token prices.
| Metric | Description | |-----------------------------|-------------------------------------------------------------------------| | Gini Coefficient and Shannon's Entropy | A measurement of the relative balances of a pool's tokens. | | Net Swap Flows | The net amount swapped into or out of a pool for a particular token. | | Net LP Flows | The net amount deposited into or withdrawn from a pool for a particular token. | | Price Volatility | The rolling log returns for a token's price. | | PIN | The probability of informed trading developed by Easley et al. | | Markouts | A short-term measurement of a trade's profits. | | Shark Trades | A classification of traders/LPs as "sharks" based on their past performance. |
Table: Summary of Studied Metrics.
BOCD
Changepoints are abrupt changes in the generative parameters of some sequence. Formally, denote a sequence of observations as $\vec{x}={x_i}, i \in [1, T]$ where observations are i.i.d. from some probability distribution $P$ with parameters $\eta$:
$$X \sim P(x_t | \eta)$$
Suppose our sequence $\vec{x}$ exhibits changes in its underlying distribution, meaning $\eta$ is not constant over time. Suppose there are $n$ changepoints occurring randomly within $[1, T]$. We may then partition our sequence into non-overlapping partitions $\rho = 1, 2, \ldots n$, each with parameters $\eta_{\rho}$. We further denote the contiguous set of observations between $a, b$ as $\vec{x}_{a:b}$.
In Bayesian online changepoint detection, our goal is to estimate when some observed datum $x_t$ is very unlikely to have been sampled from the current assumed parameters $\eta$. As proposed by Adams and MacKay in their original BOCD work, we do so by tracking a "run length" at each data point, denoted as $r_t$. The run length can be understood as the number of time steps since the last change point: with every new datum, $r_t$ either increases by 1, or it drops to $0$.
Our models emit a signal (i.e. a depeg detection) when a run length $r_t \neq r_{t-1}$. These signals are saved to the changepoints table (keyed by pool, model, and metric), and tweeted out by our Twitter alerting bot.
Results
We trained a BOCD model on each metric using the UST Wormhole pool as our training set (from January to June of 2022). We then tested it on 13 major StableSwap pools from Janu
Related Skills
node-connect
385.5kDiagnose OpenClaw Android, iOS, or macOS node pairing, QR/setup code, route, auth, and connection failures.
blender-python-addon
40.5kBlender Python add-on rules for operators, panels, properties, registration, testing, and API-safe scripting
flutter-development-guidelines-cursorrules-prompt-file
40.5kCursor rules for Flutter development with MVVM architecture, Riverpod state management, Material widgets, and Dart style guidelines.
commit-push-pr
140.7kCommit, push, and open a PR
