Liquidity Monitoring
Learn how to monitor liquidity pools in real-time, identify imbalanced pools, and track liquidity health across the Hoops Finance ecosystem. This guide shows you how to build tools for liquidity analysis and pool monitoring.
Overview
This page covers:
- Getting real-time pool reserves and liquidity data
- Identifying imbalanced or highly active pools
- Monitoring liquidity changes over time
- Building liquidity health dashboards
- Detecting arbitrage opportunities
Pool Reserves and Liquidity
Get Pool Reserves
- Python
- JavaScript
- TypeScript
- cURL
- Go
- Rust
- Java
- Shell
import requests
import json
def get_pair_liquidity(pair_contract):
"""Fetch liquidity data for a specific pair"""
url = f"https://api.hoops.finance/pairs/{pair_contract}/liquidity"
response = requests.get(url)
response.raise_for_status()
return response.json()
def get_pair_details(pair_contract):
"""Fetch detailed pair information including reserves"""
url = f"https://api.hoops.finance/pairs/{pair_contract}"
response = requests.get(url)
response.raise_for_status()
return response.json()
# Usage
pair_address = "CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU"
# Get liquidity data
liquidity_data = get_pair_liquidity(pair_address)
pair_details = get_pair_details(pair_address)
print(f"Pair: {pair_details['token0Symbol']}/{pair_details['token1Symbol']}")
print(f"Token0 Reserves: {pair_details['reserve0']}")
print(f"Token1 Reserves: {pair_details['reserve1']}")
print(f"Total Supply: {pair_details['totalSupply']}")
print(f"Current Price: {pair_details['token0Price']} {pair_details['token0Symbol']} per {pair_details['token1Symbol']}")
async function getPairLiquidity(pairContract) {
try {
const response = await fetch(`https://api.hoops.finance/pairs/${pairContract}/liquidity`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
} catch (error) {
console.error('Error fetching pair liquidity:', error.message);
throw error;
}
}
async function getPairDetails(pairContract) {
try {
const response = await fetch(`https://api.hoops.finance/pairs/${pairContract}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
} catch (error) {
console.error('Error fetching pair details:', error.message);
throw error;
}
}
// Usage
const pairAddress = "CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU";
Promise.all([
getPairLiquidity(pairAddress),
getPairDetails(pairAddress)
]).then(([liquidityData, pairDetails]) => {
console.log(`Pair: ${pairDetails.token0Symbol}/${pairDetails.token1Symbol}`);
console.log(`Token0 Reserves: ${pairDetails.reserve0}`);
console.log(`Token1 Reserves: ${pairDetails.reserve1}`);
console.log(`Total Supply: ${pairDetails.totalSupply}`);
console.log(`Current Price: ${pairDetails.token0Price} ${pairDetails.token0Symbol} per ${pairDetails.token1Symbol}`);
});
interface PairDetails {
token0Symbol: string;
token1Symbol: string;
reserve0: string;
reserve1: string;
totalSupply: string;
token0Price: string;
token1Price: string;
tvl: string;
volume24h: string;
}
async function getPairLiquidity(pairContract: string) {
try {
const response = await fetch(`https://api.hoops.finance/pairs/${pairContract}/liquidity`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
} catch (error) {
console.error('Error fetching pair liquidity:', error);
throw error;
}
}
async function getPairDetails(pairContract: string): Promise<PairDetails> {
try {
const response = await fetch(`https://api.hoops.finance/pairs/${pairContract}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
} catch (error) {
console.error('Error fetching pair details:', error);
throw error;
}
}
// Usage
const pairAddress = "CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU";
Promise.all([
getPairLiquidity(pairAddress),
getPairDetails(pairAddress)
]).then(([liquidityData, pairDetails]) => {
console.log(`Pair: ${pairDetails.token0Symbol}/${pairDetails.token1Symbol}`);
console.log(`Token0 Reserves: ${pairDetails.reserve0}`);
console.log(`Token1 Reserves: ${pairDetails.reserve1}`);
console.log(`Total Supply: ${pairDetails.totalSupply}`);
console.log(`Current Price: ${pairDetails.token0Price} ${pairDetails.token0Symbol} per ${pairDetails.token1Symbol}`);
});
# Get pair details
PAIR_ADDRESS="CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU"
# Get pair details
curl -s "https://api.hoops.finance/pairs/${PAIR_ADDRESS}" | jq '.'
# Get pair liquidity
curl -s "https://api.hoops.finance/pairs/${PAIR_ADDRESS}/liquidity" | jq '.'
# Extract specific values
TOKEN0_SYMBOL=$(curl -s "https://api.hoops.finance/pairs/${PAIR_ADDRESS}" | jq -r '.token0Symbol')
TOKEN1_SYMBOL=$(curl -s "https://api.hoops.finance/pairs/${PAIR_ADDRESS}" | jq -r '.token1Symbol')
RESERVE0=$(curl -s "https://api.hoops.finance/pairs/${PAIR_ADDRESS}" | jq -r '.reserve0')
RESERVE1=$(curl -s "https://api.hoops.finance/pairs/${PAIR_ADDRESS}" | jq -r '.reserve1')
PRICE=$(curl -s "https://api.hoops.finance/pairs/${PAIR_ADDRESS}" | jq -r '.token0Price')
echo "Pair: ${TOKEN0_SYMBOL}/${TOKEN1_SYMBOL}"
echo "Token0 Reserves: ${RESERVE0}"
echo "Token1 Reserves: ${RESERVE1}"
echo "Current Price: ${PRICE} ${TOKEN0_SYMBOL} per ${TOKEN1_SYMBOL}"
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
type PairDetails struct {
Token0Symbol string `json:"token0Symbol"`
Token1Symbol string `json:"token1Symbol"`
Reserve0 string `json:"reserve0"`
Reserve1 string `json:"reserve1"`
TotalSupply string `json:"totalSupply"`
Token0Price string `json:"token0Price"`
Token1Price string `json:"token1Price"`
TVL string `json:"tvl"`
Volume24h string `json:"volume24h"`
}
func getPairLiquidity(pairContract string) ([]byte, error) {
url := fmt.Sprintf("https://api.hoops.finance/pairs/%s/liquidity", pairContract)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return ioutil.ReadAll(resp.Body)
}
func getPairDetails(pairContract string) (*PairDetails, error) {
url := fmt.Sprintf("https://api.hoops.finance/pairs/%s", pairContract)
resp, err := http.Get(url)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
var pairDetails PairDetails
err = json.Unmarshal(body, &pairDetails)
return &pairDetails, err
}
func main() {
pairAddress := "CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU"
// Get pair details
pairDetails, err := getPairDetails(pairAddress)
if err != nil {
panic(err)
}
fmt.Printf("Pair: %s/%s\n", pairDetails.Token0Symbol, pairDetails.Token1Symbol)
fmt.Printf("Token0 Reserves: %s\n", pairDetails.Reserve0)
fmt.Printf("Token1 Reserves: %s\n", pairDetails.Reserve1)
fmt.Printf("Total Supply: %s\n", pairDetails.TotalSupply)
fmt.Printf("Current Price: %s %s per %s\n",
pairDetails.Token0Price, pairDetails.Token0Symbol, pairDetails.Token1Symbol)
}
use reqwest;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
struct PairDetails {
token0_symbol: String,
token1_symbol: String,
reserve0: String,
reserve1: String,
total_supply: String,
token0_price: String,
token1_price: String,
tvl: String,
volume24h: String,
}
async fn get_pair_liquidity(pair_contract: &str) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
let url = format!("https://api.hoops.finance/pairs/{}/liquidity", pair_contract);
let response = reqwest::get(&url).await?;
let data: serde_json::Value = response.json().await?;
Ok(data)
}
async fn get_pair_details(pair_contract: &str) -> Result<PairDetails, Box<dyn std::error::Error>> {
let url = format!("https://api.hoops.finance/pairs/{}", pair_contract);
let response = reqwest::get(&url).await?;
let pair_details: PairDetails = response.json().await?;
Ok(pair_details)
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let pair_address = "CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU";
let pair_details = get_pair_details(pair_address).await?;
println!("Pair: {}/{}", pair_details.token0_symbol, pair_details.token1_symbol);
println!("Token0 Reserves: {}", pair_details.reserve0);
println!("Token1 Reserves: {}", pair_details.reserve1);
println!("Total Supply: {}", pair_details.total_supply);
println!("Current Price: {} {} per {}",
pair_details.token0_price, pair_details.token0_symbol, pair_details.token1_symbol);
Ok(())
}
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
public class PairDetails {
@JsonProperty("token0Symbol")
private String token0Symbol;
@JsonProperty("token1Symbol")
private String token1Symbol;
private String reserve0;
private String reserve1;
private String totalSupply;
@JsonProperty("token0Price")
private String token0Price;
@JsonProperty("token1Price")
private String token1Price;
private String tvl;
@JsonProperty("volume24h")
private String volume24h;
// Getters and setters
public String getToken0Symbol() { return token0Symbol; }
public void setToken0Symbol(String token0Symbol) { this.token0Symbol = token0Symbol; }
public String getToken1Symbol() { return token1Symbol; }
public void setToken1Symbol(String token1Symbol) { this.token1Symbol = token1Symbol; }
public String getReserve0() { return reserve0; }
public void setReserve0(String reserve0) { this.reserve0 = reserve0; }
public String getReserve1() { return reserve1; }
public void setReserve1(String reserve1) { this.reserve1 = reserve1; }
public String getTotalSupply() { return totalSupply; }
public void setTotalSupply(String totalSupply) { this.totalSupply = totalSupply; }
public String getToken0Price() { return token0Price; }
public void setToken0Price(String token0Price) { this.token0Price = token0Price; }
public String getToken1Price() { return token1Price; }
public void setToken1Price(String token1Price) { this.token1Price = token1Price; }
public String getTvl() { return tvl; }
public void setTvl(String tvl) { this.tvl = tvl; }
public String getVolume24h() { return volume24h; }
public void setVolume24h(String volume24h) { this.volume24h = volume24h; }
}
public class HoopsApiClient {
private static final String BASE_URL = "https://api.hoops.finance";
private final HttpClient client = HttpClient.newHttpClient();
private final ObjectMapper mapper = new ObjectMapper();
public String getPairLiquidity(String pairContract) throws Exception {
String url = String.format("%s/pairs/%s/liquidity", BASE_URL, pairContract);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
return response.body();
}
public PairDetails getPairDetails(String pairContract) throws Exception {
String url = String.format("%s/pairs/%s", BASE_URL, pairContract);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
return mapper.readValue(response.body(), PairDetails.class);
}
public static void main(String[] args) throws Exception {
HoopsApiClient client = new HoopsApiClient();
String pairAddress = "CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU";
PairDetails pairDetails = client.getPairDetails(pairAddress);
System.out.printf("Pair: %s/%s%n", pairDetails.getToken0Symbol(), pairDetails.getToken1Symbol());
System.out.printf("Token0 Reserves: %s%n", pairDetails.getReserve0());
System.out.printf("Token1 Reserves: %s%n", pairDetails.getReserve1());
System.out.printf("Total Supply: %s%n", pairDetails.getTotalSupply());
System.out.printf("Current Price: %s %s per %s%n",
pairDetails.getToken0Price(), pairDetails.getToken0Symbol(), pairDetails.getToken1Symbol());
}
}
#!/bin/bash
BASE_URL="https://api.hoops.finance"
get_pair_liquidity() {
local pair_contract=$1
curl -s "${BASE_URL}/pairs/${pair_contract}/liquidity" | jq '.'
}
get_pair_details() {
local pair_contract=$1
curl -s "${BASE_URL}/pairs/${pair_contract}" | jq '.'
}
# Usage
PAIR_ADDRESS="CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU"
# Get pair details
pair_details=$(get_pair_details "$PAIR_ADDRESS")
# Extract values
token0_symbol=$(echo "$pair_details" | jq -r '.token0Symbol')
token1_symbol=$(echo "$pair_details" | jq -r '.token1Symbol')
reserve0=$(echo "$pair_details" | jq -r '.reserve0')
reserve1=$(echo "$pair_details" | jq -r '.reserve1')
price=$(echo "$pair_details" | jq -r '.token0Price')
echo "Pair: ${token0_symbol}/${token1_symbol}"
echo "Token0 Reserves: ${reserve0}"
echo "Token1 Reserves: ${reserve1}"
echo "Current Price: ${price} ${token0_symbol} per ${token1_symbol}"
# Get liquidity data
get_pair_liquidity "$PAIR_ADDRESS"
Use Case
Great for: Real-time trading interfaces, price feeds, and liquidity monitoring dashboards
Response Fields Explained
Field | Description | Example |
---|---|---|
reserve0 | Token0 reserves in pool | 1000000000000000000000 |
reserve1 | Token1 reserves in pool | 500000000000000000000 |
totalSupply | Total LP token supply | 707106781186547524 |
token0Price | Current price of token0 in token1 | 0.5 |
token1Price | Current price of token1 in token0 | 2.0 |
tvl | Total Value Locked in USD | 1500000.00 |
Identifying Imbalanced Pools
Pool Balance Analysis
def analyze_pool_balance(pair_contract):
"""Analyze pool balance and identify potential issues"""
pair_details = get_pair_details(pair_contract)
# Calculate balance ratio
reserve0 = float(pair_details['reserve0'])
reserve1 = float(pair_details['reserve1'])
if reserve0 == 0 or reserve1 == 0:
return {
'status': 'empty_pool',
'message': 'Pool has zero reserves'
}
# Calculate balance ratio (should be close to 1 for balanced pools)
balance_ratio = reserve0 / reserve1
price_ratio = float(pair_details['token0Price'])
# Determine if pool is imbalanced
imbalance_threshold = 0.1 # 10% threshold
is_imbalanced = abs(balance_ratio - price_ratio) / price_ratio > imbalance_threshold
return {
'pair': f"{pair_details['token0Symbol']}/{pair_details['token1Symbol']}",
'reserve0': reserve0,
'reserve1': reserve1,
'balance_ratio': balance_ratio,
'price_ratio': price_ratio,
'is_imbalanced': is_imbalanced,
'imbalance_percentage': abs(balance_ratio - price_ratio) / price_ratio * 100,
'tvl': float(pair_details['tvl']),
'volume24h': float(pair_details['volume24h'])
}
def find_imbalanced_pools(threshold=0.1):
"""Find all imbalanced pools above threshold"""
all_pairs = requests.get("https://api.hoops.finance/pairs").json()
imbalanced_pools = []
for pair in all_pairs['pairs']:
analysis = analyze_pool_balance(pair['pairContract'])
if analysis.get('is_imbalanced', False):
imbalanced_pools.append(analysis)
# Sort by imbalance percentage
imbalanced_pools.sort(key=lambda x: x['imbalance_percentage'], reverse=True)
return imbalanced_pools
# Usage
pair_address = "CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU"
analysis = analyze_pool_balance(pair_address)
print(f"Pool: {analysis['pair']}")
print(f"Balance Ratio: {analysis['balance_ratio']:.4f}")
print(f"Price Ratio: {analysis['price_ratio']:.4f}")
print(f"Imbalance: {analysis['imbalance_percentage']:.2f}%")
print(f"Is Imbalanced: {analysis['is_imbalanced']}")
print(f"TVL: ${analysis['tvl']:,.2f}")
print(f"24h Volume: ${analysis['volume24h']:,.2f}")
# Find all imbalanced pools
print("\n=== MOST IMBALANCED POOLS ===")
imbalanced = find_imbalanced_pools(threshold=0.05) # 5% threshold
for pool in imbalanced[:5]:
print(f"{pool['pair']}: {pool['imbalance_percentage']:.2f}% imbalance")
async function analyzePoolBalance(pairContract) {
try {
const pairDetails = await getPairDetails(pairContract);
// Calculate balance ratio
const reserve0 = parseFloat(pairDetails.reserve0);
const reserve1 = parseFloat(pairDetails.reserve1);
if (reserve0 === 0 || reserve1 === 0) {
return {
status: 'empty_pool',
message: 'Pool has zero reserves'
};
}
// Calculate balance ratio
const balanceRatio = reserve0 / reserve1;
const priceRatio = parseFloat(pairDetails.token0Price);
// Determine if pool is imbalanced
const imbalanceThreshold = 0.1; // 10% threshold
const isImbalanced = Math.abs(balanceRatio - priceRatio) / priceRatio > imbalanceThreshold;
return {
pair: `${pairDetails.token0Symbol}/${pairDetails.token1Symbol}`,
reserve0: reserve0,
reserve1: reserve1,
balanceRatio: balanceRatio,
priceRatio: priceRatio,
isImbalanced: isImbalanced,
imbalancePercentage: Math.abs(balanceRatio - priceRatio) / priceRatio * 100,
tvl: parseFloat(pairDetails.tvl),
volume24h: parseFloat(pairDetails.volume24h)
};
} catch (error) {
console.error('Error analyzing pool balance:', error.message);
throw error;
}
}
async function findImbalancedPools(threshold = 0.1) {
try {
const response = await fetch('https://api.hoops.finance/pairs');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const allPairs = await response.json();
const imbalancedPools = [];
for (const pair of allPairs.pairs) {
try {
const analysis = await analyzePoolBalance(pair.pairContract);
if (analysis.isImbalanced) {
imbalancedPools.push(analysis);
}
} catch (error) {
console.warn(`Error analyzing pair ${pair.pairContract}:`, error.message);
}
}
// Sort by imbalance percentage
imbalancedPools.sort((a, b) => b.imbalancePercentage - a.imbalancePercentage);
return imbalancedPools;
} catch (error) {
console.error('Error finding imbalanced pools:', error.message);
throw error;
}
}
// Usage
const pairAddress = "CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU";
analyzePoolBalance(pairAddress).then(analysis => {
console.log(`Pool: ${analysis.pair}`);
console.log(`Balance Ratio: ${analysis.balanceRatio.toFixed(4)}`);
console.log(`Price Ratio: ${analysis.priceRatio.toFixed(4)}`);
console.log(`Imbalance: ${analysis.imbalancePercentage.toFixed(2)}%`);
console.log(`Is Imbalanced: ${analysis.isImbalanced}`);
console.log(`TVL: $${analysis.tvl.toLocaleString()}`);
console.log(`24h Volume: $${analysis.volume24h.toLocaleString()}`);
});
// Find all imbalanced pools
findImbalancedPools(0.05).then(imbalanced => {
console.log('\n=== MOST IMBALANCED POOLS ===');
imbalanced.slice(0, 5).forEach(pool => {
console.log(`${pool.pair}: ${pool.imbalancePercentage.toFixed(2)}% imbalance`);
});
});
Use Case
Great for: Arbitrage detection, rebalancing tools, and risk assessment
High Activity Pool Detection
Identify Active Pools
def find_high_activity_pools(volume_threshold=10000, tvl_threshold=100000):
"""Find pools with high trading activity"""
all_pairs = requests.get("https://api.hoops.finance/pairs").json()
active_pools = []
for pair in all_pairs['pairs']:
volume24h = float(pair.get('volume24h', 0))
tvl = float(pair.get('tvl', 0))
if volume24h >= volume_threshold and tvl >= tvl_threshold:
active_pools.append({
'pair': f"{pair['token0Symbol']}/{pair['token1Symbol']}",
'pairContract': pair['pairContract'],
'volume24h': volume24h,
'tvl': tvl,
'volume_tvl_ratio': volume24h / tvl if tvl > 0 else 0,
'fee': pair.get('fee', 0)
})
# Sort by volume/TVL ratio (activity indicator)
active_pools.sort(key=lambda x: x['volume_tvl_ratio'], reverse=True)
return active_pools
def get_pool_health_metrics(pair_contract):
"""Get comprehensive health metrics for a pool"""
pair_details = get_pair_details(pair_contract)
# Calculate various health indicators
tvl = float(pair_details['tvl'])
volume24h = float(pair_details['volume24h'])
reserve0 = float(pair_details['reserve0'])
reserve1 = float(pair_details['reserve1'])
# Health metrics
volume_tvl_ratio = volume24h / tvl if tvl > 0 else 0
liquidity_depth = min(reserve0, reserve1) # Minimum reserve
price_impact = 1 / liquidity_depth if liquidity_depth > 0 else float('inf')
return {
'pair': f"{pair_details['token0Symbol']}/{pair_details['token1Symbol']}",
'tvl': tvl,
'volume24h': volume24h,
'volume_tvl_ratio': volume_tvl_ratio,
'liquidity_depth': liquidity_depth,
'price_impact': price_impact,
'fee': pair_details.get('fee', 0),
'health_score': calculate_health_score(tvl, volume24h, liquidity_depth)
}
def calculate_health_score(tvl, volume24h, liquidity_depth):
"""Calculate a simple health score (0-100)"""
# Normalize values (these thresholds can be adjusted)
tvl_score = min(tvl / 1000000, 1) * 40 # Max 40 points for TVL
volume_score = min(volume24h / 100000, 1) * 30 # Max 30 points for volume
liquidity_score = min(liquidity_depth / 100000, 1) * 30 # Max 30 points for liquidity
return tvl_score + volume_score + liquidity_score
# Usage
print("=== HIGH ACTIVITY POOLS ===")
active_pools = find_high_activity_pools(volume_threshold=5000, tvl_threshold=50000)
for pool in active_pools[:10]:
print(f"{pool['pair']}: ${pool['volume24h']:,.0f} volume, "
f"${pool['tvl']:,.0f} TVL, "
f"{pool['volume_tvl_ratio']:.2f} ratio")
print("\n=== POOL HEALTH ANALYSIS ===")
pair_address = "CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU"
health = get_pool_health_metrics(pair_address)
print(f"Pool: {health['pair']}")
print(f"Health Score: {health['health_score']:.1f}/100")
print(f"Volume/TVL Ratio: {health['volume_tvl_ratio']:.2f}")
print(f"Liquidity Depth: {health['liquidity_depth']:,.0f}")
print(f"Price Impact: {health['price_impact']:.6f}")
async function findHighActivityPools(volumeThreshold = 10000, tvlThreshold = 100000) {
try {
const response = await fetch('https://api.hoops.finance/pairs');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const allPairs = await response.json();
const activePools = [];
allPairs.pairs.forEach(pair => {
const volume24h = parseFloat(pair.volume24h || 0);
const tvl = parseFloat(pair.tvl || 0);
if (volume24h >= volumeThreshold && tvl >= tvlThreshold) {
activePools.push({
pair: `${pair.token0Symbol}/${pair.token1Symbol}`,
pairContract: pair.pairContract,
volume24h: volume24h,
tvl: tvl,
volumeTvlRatio: volume24h / tvl,
fee: pair.fee || 0
});
}
});
// Sort by volume/TVL ratio
activePools.sort((a, b) => b.volumeTvlRatio - a.volumeTvlRatio);
return activePools;
} catch (error) {
console.error('Error finding high activity pools:', error.message);
throw error;
}
}
async function getPoolHealthMetrics(pairContract) {
try {
const pairDetails = await getPairDetails(pairContract);
const tvl = parseFloat(pairDetails.tvl);
const volume24h = parseFloat(pairDetails.volume24h);
const reserve0 = parseFloat(pairDetails.reserve0);
const reserve1 = parseFloat(pairDetails.reserve1);
const volumeTvlRatio = volume24h / tvl;
const liquidityDepth = Math.min(reserve0, reserve1);
const priceImpact = 1 / liquidityDepth;
return {
pair: `${pairDetails.token0Symbol}/${pairDetails.token1Symbol}`,
tvl: tvl,
volume24h: volume24h,
volumeTvlRatio: volumeTvlRatio,
liquidityDepth: liquidityDepth,
priceImpact: priceImpact,
fee: pairDetails.fee || 0,
healthScore: calculateHealthScore(tvl, volume24h, liquidityDepth)
};
} catch (error) {
console.error('Error getting pool health metrics:', error.message);
throw error;
}
}
function calculateHealthScore(tvl, volume24h, liquidityDepth) {
const tvlScore = Math.min(tvl / 1000000, 1) * 40;
const volumeScore = Math.min(volume24h / 100000, 1) * 30;
const liquidityScore = Math.min(liquidityDepth / 100000, 1) * 30;
return tvlScore + volumeScore + liquidityScore;
}
// Usage
console.log('=== HIGH ACTIVITY POOLS ===');
findHighActivityPools(5000, 50000).then(activePools => {
activePools.slice(0, 10).forEach(pool => {
console.log(`${pool.pair}: $${pool.volume24h.toLocaleString()} volume, $${pool.tvl.toLocaleString()} TVL, ${pool.volumeTvlRatio.toFixed(2)} ratio`);
});
});
console.log('\n=== POOL HEALTH ANALYSIS ===');
const pairAddress = "CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU";
getPoolHealthMetrics(pairAddress).then(health => {
console.log(`Pool: ${health.pair}`);
console.log(`Health Score: ${health.healthScore.toFixed(1)}/100`);
console.log(`Volume/TVL Ratio: ${health.volumeTvlRatio.toFixed(2)}`);
console.log(`Liquidity Depth: ${health.liquidityDepth.toLocaleString()}`);
console.log(`Price Impact: ${health.priceImpact.toFixed(6)}`);
});
Use Case
Great for: Trading strategy development, risk management, and portfolio optimization
Building a Liquidity Dashboard
Complete Liquidity Monitoring System
import requests
import time
from datetime import datetime
import json
class LiquidityMonitor:
def __init__(self):
self.base_url = "https://api.hoops.finance"
self.monitored_pools = []
def add_pool_to_monitor(self, pair_contract, name=None):
"""Add a pool to monitoring list"""
if name is None:
pair_details = get_pair_details(pair_contract)
name = f"{pair_details['token0Symbol']}/{pair_details['token1Symbol']}"
self.monitored_pools.append({
'contract': pair_contract,
'name': name,
'last_check': None,
'history': []
})
def get_pool_snapshot(self, pair_contract):
"""Get current snapshot of pool state"""
pair_details = get_pair_details(pair_contract)
return {
'timestamp': datetime.now().isoformat(),
'reserve0': float(pair_details['reserve0']),
'reserve1': float(pair_details['reserve1']),
'tvl': float(pair_details['tvl']),
'volume24h': float(pair_details['volume24h']),
'price': float(pair_details['token0Price']),
'fee': pair_details.get('fee', 0)
}
def monitor_pools(self, interval=60):
"""Monitor all pools at specified interval (seconds)"""
print(f"Starting liquidity monitoring for {len(self.monitored_pools)} pools...")
print(f"Update interval: {interval} seconds")
try:
while True:
for pool in self.monitored_pools:
try:
snapshot = self.get_pool_snapshot(pool['contract'])
pool['history'].append(snapshot)
pool['last_check'] = datetime.now()
# Keep only last 100 snapshots
if len(pool['history']) > 100:
pool['history'] = pool['history'][-100:]
# Check for significant changes
self.check_for_alerts(pool, snapshot)
except Exception as e:
print(f"Error monitoring {pool['name']}: {e}")
time.sleep(interval)
except KeyboardInterrupt:
print("\nMonitoring stopped by user")
def check_for_alerts(self, pool, snapshot):
"""Check for significant changes and alert"""
if len(pool['history']) < 2:
return
previous = pool['history'][-2]
# Calculate changes
tvl_change = (snapshot['tvl'] - previous['tvl']) / previous['tvl'] * 100
price_change = (snapshot['price'] - previous['price']) / previous['price'] * 100
# Alert thresholds
if abs(tvl_change) > 5: # 5% TVL change
print(f"ALERT: {pool['name']} TVL changed by {tvl_change:.2f}%")
if abs(price_change) > 2: # 2% price change
print(f"ALERT: {pool['name']} price changed by {price_change:.2f}%")
def generate_report(self):
"""Generate monitoring report"""
print("\n=== LIQUIDITY MONITORING REPORT ===")
print(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Monitored Pools: {len(self.monitored_pools)}")
for pool in self.monitored_pools:
if pool['history']:
latest = pool['history'][-1]
print(f"\n{pool['name']}:")
print(f" Current TVL: ${latest['tvl']:,.2f}")
print(f" 24h Volume: ${latest['volume24h']:,.2f}")
print(f" Current Price: {latest['price']:.6f}")
print(f" Last Update: {pool['last_check']}")
def save_history(self, filename='liquidity_history.json'):
"""Save monitoring history to file"""
data = {
'timestamp': datetime.now().isoformat(),
'pools': self.monitored_pools
}
with open(filename, 'w') as f:
json.dump(data, f, indent=2)
print(f"History saved to {filename}")
# Usage
monitor = LiquidityMonitor()
# Add pools to monitor
monitor.add_pool_to_monitor("CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU")
monitor.add_pool_to_monitor("CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU")
# Generate initial report
monitor.generate_report()
# Start monitoring (uncomment to run)
# monitor.monitor_pools(interval=30) # Check every 30 seconds
class LiquidityMonitor {
constructor() {
this.baseUrl = 'https://api.hoops.finance';
this.monitoredPools = [];
}
addPoolToMonitor(pairContract, name = null) {
return getPairDetails(pairContract).then(pairDetails => {
const poolName = name || `${pairDetails.token0Symbol}/${pairDetails.token1Symbol}`;
this.monitoredPools.push({
contract: pairContract,
name: poolName,
lastCheck: null,
history: []
});
console.log(`Added ${poolName} to monitoring`);
});
}
async getPoolSnapshot(pairContract) {
const pairDetails = await getPairDetails(pairContract);
return {
timestamp: new Date().toISOString(),
reserve0: parseFloat(pairDetails.reserve0),
reserve1: parseFloat(pairDetails.reserve1),
tvl: parseFloat(pairDetails.tvl),
volume24h: parseFloat(pairDetails.volume24h),
price: parseFloat(pairDetails.token0Price),
fee: pairDetails.fee || 0
};
}
async monitorPools(interval = 60000) { // Default 1 minute
console.log(`Starting liquidity monitoring for ${this.monitoredPools.length} pools...`);
console.log(`Update interval: ${interval / 1000} seconds`);
const monitorInterval = setInterval(async () => {
for (const pool of this.monitoredPools) {
try {
const snapshot = await this.getPoolSnapshot(pool.contract);
pool.history.push(snapshot);
pool.lastCheck = new Date();
// Keep only last 100 snapshots
if (pool.history.length > 100) {
pool.history = pool.history.slice(-100);
}
// Check for alerts
this.checkForAlerts(pool, snapshot);
} catch (error) {
console.error(`Error monitoring ${pool.name}:`, error.message);
}
}
}, interval);
return monitorInterval;
}
checkForAlerts(pool, snapshot) {
if (pool.history.length < 2) return;
const previous = pool.history[pool.history.length - 2];
// Calculate changes
const tvlChange = (snapshot.tvl - previous.tvl) / previous.tvl * 100;
const priceChange = (snapshot.price - previous.price) / previous.price * 100;
// Alert thresholds
if (Math.abs(tvlChange) > 5) {
console.log(`ALERT: ${pool.name} TVL changed by ${tvlChange.toFixed(2)}%`);
}
if (Math.abs(priceChange) > 2) {
console.log(`ALERT: ${pool.name} price changed by ${priceChange.toFixed(2)}%`);
}
}
generateReport() {
console.log('\n=== LIQUIDITY MONITORING REPORT ===');
console.log(`Generated: ${new Date().toLocaleString()}`);
console.log(`Monitored Pools: ${this.monitoredPools.length}`);
this.monitoredPools.forEach(pool => {
if (pool.history.length > 0) {
const latest = pool.history[pool.history.length - 1];
console.log(`\n${pool.name}:`);
console.log(` Current TVL: $${latest.tvl.toLocaleString()}`);
console.log(` 24h Volume: $${latest.volume24h.toLocaleString()}`);
console.log(` Current Price: ${latest.price.toFixed(6)}`);
console.log(` Last Update: ${pool.lastCheck}`);
}
});
}
saveHistory(filename = 'liquidity_history.json') {
const data = {
timestamp: new Date().toISOString(),
pools: this.monitoredPools
};
// In Node.js, you'd use fs.writeFileSync
console.log(`History would be saved to ${filename}`);
console.log('Data:', JSON.stringify(data, null, 2));
}
}
// Usage
const monitor = new LiquidityMonitor();
// Add pools to monitor
monitor.addPoolToMonitor("CAB6MICC2WKRT372U3FRPKGGVB5R3FDJSMWJL5XK6R3XWMJUCA4RJMXU");
// Generate initial report
setTimeout(() => {
monitor.generateReport();
}, 1000);
// Start monitoring (uncomment to run)
// monitor.monitorPools(30000); // Check every 30 seconds
Arbitrage Opportunity Detection
Simple Arbitrage Scanner
def scan_for_arbitrage_opportunities():
"""Scan all pools for potential arbitrage opportunities"""
all_pairs = requests.get("https://api.hoops.finance/pairs").json()
opportunities = []
# Group pairs by token combinations
token_pairs = {}
for pair in all_pairs['pairs']:
token0 = pair['token0Symbol']
token1 = pair['token1Symbol']
# Create sorted key for consistent grouping
key = tuple(sorted([token0, token1]))
if key not in token_pairs:
token_pairs[key] = []
token_pairs[key].append(pair)
# Check for price differences in same token pairs
for token_combo, pairs in token_pairs.items():
if len(pairs) > 1:
prices = []
for pair in pairs:
price = float(pair['token0Price'])
prices.append({
'pair': f"{pair['token0Symbol']}/{pair['token1Symbol']}",
'price': price,
'tvl': float(pair['tvl']),
'volume24h': float(pair['volume24h']),
'contract': pair['pairContract']
})
# Find min and max prices
min_price = min(prices, key=lambda x: x['price'])
max_price = max(prices, key=lambda x: x['price'])
# Calculate spread
spread = (max_price['price'] - min_price['price']) / min_price['price'] * 100
if spread > 1: # 1% minimum spread
opportunities.append({
'tokens': token_combo,
'spread': spread,
'min_price': min_price,
'max_price': max_price,
'pairs': prices
})
# Sort by spread
opportunities.sort(key=lambda x: x['spread'], reverse=True)
return opportunities
# Usage
print("=== ARBITRAGE OPPORTUNITIES ===")
opportunities = scan_for_arbitrage_opportunities()
for opp in opportunities[:5]:
print(f"\n{opp['tokens'][0]}/{opp['tokens'][1]} - {opp['spread']:.2f}% spread")
print(f" Min: {opp['min_price']['pair']} @ {opp['min_price']['price']:.6f}")
print(f" Max: {opp['max_price']['pair']} @ {opp['max_price']['price']:.6f}")
print(f" Min TVL: ${opp['min_price']['tvl']:,.0f}")
print(f" Max TVL: ${opp['max_price']['tvl']:,.0f}")
Next Steps
- Combine with Analytics Dashboard to track liquidity trends
- Use Token Data to display token information in liquidity views
- Integrate with Wallet Tracking for LP position monitoring
Rate Limiting
Remember to implement proper rate limiting in production applications. The API allows 120 requests per minute.
Monitoring Best Practices
- Use appropriate intervals for monitoring (30-60 seconds for real-time, 5-15 minutes for trends)
- Implement alert thresholds based on your use case
- Store historical data for trend analysis
- Consider using WebSocket connections for real-time updates when available