from compass_api_sdk import CompassAPI, models
with CompassAPI(
api_key_auth="<YOUR_API_KEY_HERE>",
) as compass_api:
res = compass_api.risk_yield.risk_yield_risk_scenarios(pool={
"pool_id": 1,
}, range={
"type": "symmetric_pct",
"width_pct": "30",
}, deposit={
"type": "usd",
"value_usd": "1000",
}, chain=models.RiskScenariosRequestChain.ROBINHOOD, horizon_days=30, scenarios=[
{
"name": "meme halves",
"ratio_move_pct": "-50",
},
{
"name": "meme doubles",
"ratio_move_pct": "100",
},
{
"name": "stock falls 10%",
"quote_usd_move_pct": "-10",
},
], monte_carlo=True, n_paths=2000, seed=0)
# Handle response
print(res)import { CompassApiSDK } from "@compass-labs/api-sdk";
const compassApiSDK = new CompassApiSDK({
apiKeyAuth: "<YOUR_API_KEY_HERE>",
});
async function run() {
const result = await compassApiSDK.riskYield.riskYieldRiskScenarios({
chain: "robinhood",
pool: {
poolId: 1,
},
range: {
type: "symmetric_pct",
widthPct: "30",
},
deposit: {
type: "usd",
valueUsd: "1000",
},
horizonDays: 30,
scenarios: [
{
name: "meme halves",
ratioMovePct: "-50",
},
{
name: "meme doubles",
ratioMovePct: "100",
},
{
name: "stock falls 10%",
quoteUsdMovePct: "-10",
},
],
});
console.log(result);
}
run();curl --request POST \
--url https://api.compasslabs.ai/v2/risk_yield/risk/scenarios \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"chain": "robinhood",
"pool": {
"pool_id": 1
},
"range": {
"type": "symmetric_pct",
"width_pct": "30"
},
"deposit": {
"type": "usd",
"value_usd": "1000"
},
"horizon_days": 30,
"scenarios": [
{
"name": "meme halves",
"ratio_move_pct": "-50"
},
{
"name": "meme doubles",
"ratio_move_pct": "100"
},
{
"name": "stock falls 10%",
"quote_usd_move_pct": "-10"
}
]
}
'const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
chain: 'robinhood',
pool: {pool_id: 1},
range: {type: 'symmetric_pct', width_pct: '30'},
deposit: {type: 'usd', value_usd: '1000'},
horizon_days: 30,
scenarios: [
{name: 'meme halves', ratio_move_pct: '-50'},
{name: 'meme doubles', ratio_move_pct: '100'},
{name: 'stock falls 10%', quote_usd_move_pct: '-10'}
]
})
};
fetch('https://api.compasslabs.ai/v2/risk_yield/risk/scenarios', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.compasslabs.ai/v2/risk_yield/risk/scenarios",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'chain' => 'robinhood',
'pool' => [
'pool_id' => 1
],
'range' => [
'type' => 'symmetric_pct',
'width_pct' => '30'
],
'deposit' => [
'type' => 'usd',
'value_usd' => '1000'
],
'horizon_days' => 30,
'scenarios' => [
[
'name' => 'meme halves',
'ratio_move_pct' => '-50'
],
[
'name' => 'meme doubles',
'ratio_move_pct' => '100'
],
[
'name' => 'stock falls 10%',
'quote_usd_move_pct' => '-10'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.compasslabs.ai/v2/risk_yield/risk/scenarios"
payload := strings.NewReader("{\n \"chain\": \"robinhood\",\n \"pool\": {\n \"pool_id\": 1\n },\n \"range\": {\n \"type\": \"symmetric_pct\",\n \"width_pct\": \"30\"\n },\n \"deposit\": {\n \"type\": \"usd\",\n \"value_usd\": \"1000\"\n },\n \"horizon_days\": 30,\n \"scenarios\": [\n {\n \"name\": \"meme halves\",\n \"ratio_move_pct\": \"-50\"\n },\n {\n \"name\": \"meme doubles\",\n \"ratio_move_pct\": \"100\"\n },\n {\n \"name\": \"stock falls 10%\",\n \"quote_usd_move_pct\": \"-10\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.compasslabs.ai/v2/risk_yield/risk/scenarios")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"chain\": \"robinhood\",\n \"pool\": {\n \"pool_id\": 1\n },\n \"range\": {\n \"type\": \"symmetric_pct\",\n \"width_pct\": \"30\"\n },\n \"deposit\": {\n \"type\": \"usd\",\n \"value_usd\": \"1000\"\n },\n \"horizon_days\": 30,\n \"scenarios\": [\n {\n \"name\": \"meme halves\",\n \"ratio_move_pct\": \"-50\"\n },\n {\n \"name\": \"meme doubles\",\n \"ratio_move_pct\": \"100\"\n },\n {\n \"name\": \"stock falls 10%\",\n \"quote_usd_move_pct\": \"-10\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.compasslabs.ai/v2/risk_yield/risk/scenarios")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"chain\": \"robinhood\",\n \"pool\": {\n \"pool_id\": 1\n },\n \"range\": {\n \"type\": \"symmetric_pct\",\n \"width_pct\": \"30\"\n },\n \"deposit\": {\n \"type\": \"usd\",\n \"value_usd\": \"1000\"\n },\n \"horizon_days\": 30,\n \"scenarios\": [\n {\n \"name\": \"meme halves\",\n \"ratio_move_pct\": \"-50\"\n },\n {\n \"name\": \"meme doubles\",\n \"ratio_move_pct\": \"100\"\n },\n {\n \"name\": \"stock falls 10%\",\n \"quote_usd_move_pct\": \"-10\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"range": {
"tick_lower": 123,
"tick_upper": 123,
"price_lower": "<string>",
"price_upper": "<string>",
"width_lower_pct": "<string>",
"width_upper_pct": "<string>",
"is_full_range": true
},
"assumptions": {
"entry_price": "<string>",
"deposit_value_usd": "<string>",
"horizon_days": 123,
"price0_usd": "<string>",
"price1_usd": "<string>",
"price_source0": "<string>",
"price_source1": "<string>",
"prices_updated_at": "2023-11-07T05:31:56Z",
"annual_vol_pct": "<string>",
"vol_source": "measured",
"fee_apr_pct": "<string>",
"fee_source": "measured",
"fees_per_day_usd_pool": "<string>",
"share_of_active_liquidity_pct": "<string>",
"lp_earns_fees": true
},
"scenarios": [
{
"name": "<string>",
"price_ratio": "<string>",
"end_price": "<string>",
"in_range_at_end": true,
"position_value_usd": "<string>",
"hold_value_usd": "<string>",
"il_usd": "<string>",
"il_pct": "<string>",
"hold_pnl_usd": "<string>",
"expected_fees_usd": "<string>",
"net_pnl_usd": "<string>",
"breakeven_fee_apr_pct": "<string>",
"days_of_fees_to_recover_il": "<string>"
}
],
"pool_id": 123,
"probabilistic": {
"method": "<string>",
"paths": 123,
"p_exit_within_horizon_pct": "<string>",
"p_out_at_horizon_pct": "<string>",
"expected_time_in_range_pct": "<string>",
"expected_il_usd": "<string>",
"expected_net_pnl_usd": "<string>",
"prob_net_loss_pct": "<string>",
"il_pct_percentiles": {},
"il_usd_percentiles": {},
"net_pnl_usd_percentiles": {}
},
"warnings": [
"<string>"
]
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Model impermanent loss
What this position would do if the price moved — and how likely that is.
A fee APR on a memecoin pair means nothing without the loss it is paid against, which is what this measures.
Each scenario takes two independent moves, and keeping them apart is the
point. ratio_move_pct is how far the pair’s ratio travels, and that is
what causes impermanent loss. quote_usd_move_pct is how far the stock
moves in dollars, which scales what the position is worth without touching
the ratio at all — a stock-only move produces an impermanent loss of exactly
zero. “NVDA drops 10%” and “the memecoin drops 10% against NVDA” are
different questions, and one number cannot ask both.
The two most useful columns are breakeven_fee_apr_pct — what this position
would have to earn for the loss to be worth taking, which you can compare
against the pool’s actual fee APR — and days_of_fees_to_recover_il, which
is null when the pool pays liquidity providers nothing, because no amount of
time fixes that.
probabilistic runs the pair’s own measured volatility through a driftless
simulation. Driftless deliberately: nobody knows which way a memecoin goes,
and a model that assumed one would be predicting returns rather than
measuring risk. It is absent when the pair has no measured volatility —
which a pool with a few hours of history does not — because a distribution
built on zero would report that nothing can happen.
from compass_api_sdk import CompassAPI, models
with CompassAPI(
api_key_auth="<YOUR_API_KEY_HERE>",
) as compass_api:
res = compass_api.risk_yield.risk_yield_risk_scenarios(pool={
"pool_id": 1,
}, range={
"type": "symmetric_pct",
"width_pct": "30",
}, deposit={
"type": "usd",
"value_usd": "1000",
}, chain=models.RiskScenariosRequestChain.ROBINHOOD, horizon_days=30, scenarios=[
{
"name": "meme halves",
"ratio_move_pct": "-50",
},
{
"name": "meme doubles",
"ratio_move_pct": "100",
},
{
"name": "stock falls 10%",
"quote_usd_move_pct": "-10",
},
], monte_carlo=True, n_paths=2000, seed=0)
# Handle response
print(res)import { CompassApiSDK } from "@compass-labs/api-sdk";
const compassApiSDK = new CompassApiSDK({
apiKeyAuth: "<YOUR_API_KEY_HERE>",
});
async function run() {
const result = await compassApiSDK.riskYield.riskYieldRiskScenarios({
chain: "robinhood",
pool: {
poolId: 1,
},
range: {
type: "symmetric_pct",
widthPct: "30",
},
deposit: {
type: "usd",
valueUsd: "1000",
},
horizonDays: 30,
scenarios: [
{
name: "meme halves",
ratioMovePct: "-50",
},
{
name: "meme doubles",
ratioMovePct: "100",
},
{
name: "stock falls 10%",
quoteUsdMovePct: "-10",
},
],
});
console.log(result);
}
run();curl --request POST \
--url https://api.compasslabs.ai/v2/risk_yield/risk/scenarios \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"chain": "robinhood",
"pool": {
"pool_id": 1
},
"range": {
"type": "symmetric_pct",
"width_pct": "30"
},
"deposit": {
"type": "usd",
"value_usd": "1000"
},
"horizon_days": 30,
"scenarios": [
{
"name": "meme halves",
"ratio_move_pct": "-50"
},
{
"name": "meme doubles",
"ratio_move_pct": "100"
},
{
"name": "stock falls 10%",
"quote_usd_move_pct": "-10"
}
]
}
'const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
chain: 'robinhood',
pool: {pool_id: 1},
range: {type: 'symmetric_pct', width_pct: '30'},
deposit: {type: 'usd', value_usd: '1000'},
horizon_days: 30,
scenarios: [
{name: 'meme halves', ratio_move_pct: '-50'},
{name: 'meme doubles', ratio_move_pct: '100'},
{name: 'stock falls 10%', quote_usd_move_pct: '-10'}
]
})
};
fetch('https://api.compasslabs.ai/v2/risk_yield/risk/scenarios', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.compasslabs.ai/v2/risk_yield/risk/scenarios",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'chain' => 'robinhood',
'pool' => [
'pool_id' => 1
],
'range' => [
'type' => 'symmetric_pct',
'width_pct' => '30'
],
'deposit' => [
'type' => 'usd',
'value_usd' => '1000'
],
'horizon_days' => 30,
'scenarios' => [
[
'name' => 'meme halves',
'ratio_move_pct' => '-50'
],
[
'name' => 'meme doubles',
'ratio_move_pct' => '100'
],
[
'name' => 'stock falls 10%',
'quote_usd_move_pct' => '-10'
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.compasslabs.ai/v2/risk_yield/risk/scenarios"
payload := strings.NewReader("{\n \"chain\": \"robinhood\",\n \"pool\": {\n \"pool_id\": 1\n },\n \"range\": {\n \"type\": \"symmetric_pct\",\n \"width_pct\": \"30\"\n },\n \"deposit\": {\n \"type\": \"usd\",\n \"value_usd\": \"1000\"\n },\n \"horizon_days\": 30,\n \"scenarios\": [\n {\n \"name\": \"meme halves\",\n \"ratio_move_pct\": \"-50\"\n },\n {\n \"name\": \"meme doubles\",\n \"ratio_move_pct\": \"100\"\n },\n {\n \"name\": \"stock falls 10%\",\n \"quote_usd_move_pct\": \"-10\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.compasslabs.ai/v2/risk_yield/risk/scenarios")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"chain\": \"robinhood\",\n \"pool\": {\n \"pool_id\": 1\n },\n \"range\": {\n \"type\": \"symmetric_pct\",\n \"width_pct\": \"30\"\n },\n \"deposit\": {\n \"type\": \"usd\",\n \"value_usd\": \"1000\"\n },\n \"horizon_days\": 30,\n \"scenarios\": [\n {\n \"name\": \"meme halves\",\n \"ratio_move_pct\": \"-50\"\n },\n {\n \"name\": \"meme doubles\",\n \"ratio_move_pct\": \"100\"\n },\n {\n \"name\": \"stock falls 10%\",\n \"quote_usd_move_pct\": \"-10\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.compasslabs.ai/v2/risk_yield/risk/scenarios")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"chain\": \"robinhood\",\n \"pool\": {\n \"pool_id\": 1\n },\n \"range\": {\n \"type\": \"symmetric_pct\",\n \"width_pct\": \"30\"\n },\n \"deposit\": {\n \"type\": \"usd\",\n \"value_usd\": \"1000\"\n },\n \"horizon_days\": 30,\n \"scenarios\": [\n {\n \"name\": \"meme halves\",\n \"ratio_move_pct\": \"-50\"\n },\n {\n \"name\": \"meme doubles\",\n \"ratio_move_pct\": \"100\"\n },\n {\n \"name\": \"stock falls 10%\",\n \"quote_usd_move_pct\": \"-10\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"range": {
"tick_lower": 123,
"tick_upper": 123,
"price_lower": "<string>",
"price_upper": "<string>",
"width_lower_pct": "<string>",
"width_upper_pct": "<string>",
"is_full_range": true
},
"assumptions": {
"entry_price": "<string>",
"deposit_value_usd": "<string>",
"horizon_days": 123,
"price0_usd": "<string>",
"price1_usd": "<string>",
"price_source0": "<string>",
"price_source1": "<string>",
"prices_updated_at": "2023-11-07T05:31:56Z",
"annual_vol_pct": "<string>",
"vol_source": "measured",
"fee_apr_pct": "<string>",
"fee_source": "measured",
"fees_per_day_usd_pool": "<string>",
"share_of_active_liquidity_pct": "<string>",
"lp_earns_fees": true
},
"scenarios": [
{
"name": "<string>",
"price_ratio": "<string>",
"end_price": "<string>",
"in_range_at_end": true,
"position_value_usd": "<string>",
"hold_value_usd": "<string>",
"il_usd": "<string>",
"il_pct": "<string>",
"hold_pnl_usd": "<string>",
"expected_fees_usd": "<string>",
"net_pnl_usd": "<string>",
"breakeven_fee_apr_pct": "<string>",
"days_of_fees_to_recover_il": "<string>"
}
],
"pool_id": 123,
"probabilistic": {
"method": "<string>",
"paths": 123,
"p_exit_within_horizon_pct": "<string>",
"p_out_at_horizon_pct": "<string>",
"expected_time_in_range_pct": "<string>",
"expected_il_usd": "<string>",
"expected_net_pnl_usd": "<string>",
"prob_net_loss_pct": "<string>",
"il_pct_percentiles": {},
"il_usd_percentiles": {},
"net_pnl_usd_percentiles": {}
},
"warnings": [
"<string>"
]
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>"
}
]
}Body
Ask what a position would do under a set of hypotheticals.
Which pool, by id or by its key.
A pool id is stable and is what the list endpoints return. The long form exists so a caller who knows the pair can address a pool without a lookup — on a chain minting twenty thousand pools a day, a client should not have to search for one it just created.
Show child attributes
Show child attributes
{ "pool_id": 1 }
A range centred on the current price, given as a percentage either side.
The usual way to ask for a position: narrow earns more fees per dollar while the price stays inside it, and stops earning entirely when it leaves.
- SymmetricRange
- TickRange
- PriceRange
- FullRange
Show child attributes
Show child attributes
{
"type": "symmetric_pct",
"width_pct": "30"
}
Explicit amounts of each token.
- BothSidesDeposit
- SingleSidedDeposit
- UsdDeposit
Show child attributes
Show child attributes
{ "type": "usd" }
robinhood "robinhood"
1 <= x <= 36530
Leave empty for a default spread of moves.
50Show child attributes
Show child attributes
[
{
"name": "meme halves",
"ratio_move_pct": "-50"
},
{
"name": "meme doubles",
"ratio_move_pct": "100"
},
{
"name": "stock falls 10%",
"quote_usd_move_pct": "-10"
}
]
Assume this fee rate instead of the pool's measured one.
x >= 0Assume this annualized volatility instead of the pair's measured one. Needed for a pool too new to have measured any.
x > 0Also estimate how likely each outcome is, not just its size.
100 <= x <= 10000Fixed so the same request gives the same answer.
Response
Successful Response
A range after the API has snapped it to the pool's tick spacing.
Returned because the range you asked for is rarely the range you get: ticks are discrete, and a 30% band becomes whichever usable ticks bracket it.
Show child attributes
Show child attributes
What the answers rest on.
Returned in full because every number below is only as good as these, and a caller who disagrees with one can override it and ask again.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Absent when the pair's volatility cannot be measured — a pool with a few hours of history has no measurable volatility, and a distribution built on zero would report that nothing can happen.
Show child attributes
Show child attributes
Was this page helpful?