Overview
Access comprehensive market data through the Indexer Client. This guide covers querying markets, orderbooks, funding rates, and trading statistics.Markets
List all perpetual markets and their properties
Orderbooks
View live bids and asks for any market
Statistics
Access volume, price changes, and funding rates
Querying Markets
Get All Perpetual Markets
Copy
import { CompositeClient } from "@oraichain/lfg-client-js";
async function getAllMarkets(client: CompositeClient) {
const response = await client.indexerClient.markets.getPerpetualMarkets();
console.log("Available Markets:");
for (const [marketId, market] of Object.entries(response.markets)) {
console.log(`\n${market.ticker} (${marketId}):`);
console.log(` Status: ${market.status}`);
console.log(` Price: $${market.oraclePrice}`);
console.log(` 24h Change: ${market.priceChange24H}%`);
console.log(` 24h Volume: $${market.volume24H}`);
console.log(` Open Interest: $${market.openInterest}`);
console.log(` Next Funding Rate: ${market.nextFundingRate}%`);
}
return response.markets;
}
Market Data Structure
Unique identifier for the CLOB (Central Limit Order Book) pair
Market ticker symbol (e.g., “ETH-USD”, “BTC-USD”)
Market status:
"ACTIVE", "PAUSED", "CANCEL_ONLY", or "POST_ONLY"Current oracle price in quote currency (USD)
24-hour price change percentage
24-hour trading volume in USD
Number of trades in the last 24 hours
Next funding rate as a percentage
Initial margin requirement (e.g., “0.05” = 5% = 20x leverage)
Maintenance margin requirement (e.g., “0.03” = 3%)
Total open interest in USD
Get Specific Market
Copy
async function getMarket(client: CompositeClient, marketId: string) {
const markets = await client.indexerClient.markets.getPerpetualMarkets();
const market = markets.markets[marketId];
if (!market) {
throw new Error(`Market ${marketId} not found`);
}
return market;
}
// Get ETH-USD market
const ethMarket = await getMarket(client, "ETH-USD");
console.log(`ETH Price: $${ethMarket.oraclePrice}`);
Filter Active Markets
Copy
function getActiveMarkets(markets: any) {
return Object.entries(markets)
.filter(([_, market]: [string, any]) => market.status === "ACTIVE")
.map(([marketId, market]) => ({ marketId, ...market }));
}
const markets = await client.indexerClient.markets.getPerpetualMarkets();
const activeMarkets = getActiveMarkets(markets.markets);
console.log(`Found ${activeMarkets.length} active markets`);
Market Statistics
Get Market Rankings
Copy
async function getMarketRankings(client: CompositeClient) {
const response = await client.indexerClient.markets.getPerpetualMarkets();
const markets = Object.entries(response.markets).map(
([id, data]: [string, any]) => ({
marketId: id,
ticker: data.ticker,
volume24H: parseFloat(data.volume24H),
trades24H: data.trades24H,
priceChange24H: parseFloat(data.priceChange24H),
openInterest: parseFloat(data.openInterest),
})
);
// Sort by 24h volume
const byVolume = [...markets].sort((a, b) => b.volume24H - a.volume24H);
console.log("Top 10 Markets by Volume:");
byVolume.slice(0, 10).forEach((market, index) => {
console.log(
`${index + 1}. ${market.ticker}: $${market.volume24H.toFixed(0)}`
);
});
// Sort by price change
const byPriceChange = [...markets].sort(
(a, b) => b.priceChange24H - a.priceChange24H
);
console.log("\nTop Gainers:");
byPriceChange.slice(0, 5).forEach((market) => {
console.log(`${market.ticker}: +${market.priceChange24H.toFixed(2)}%`);
});
console.log("\nTop Losers:");
byPriceChange
.slice(-5)
.reverse()
.forEach((market) => {
console.log(`${market.ticker}: ${market.priceChange24H.toFixed(2)}%`);
});
return { byVolume, byPriceChange };
}
Calculate Maximum Leverage
Copy
function calculateMaxLeverage(market: any): number {
const initialMarginFraction = parseFloat(market.initialMarginFraction);
return 1 / initialMarginFraction;
}
const markets = await client.indexerClient.markets.getPerpetualMarkets();
const ethMarket = markets.markets["ETH-USD"];
const maxLeverage = calculateMaxLeverage(ethMarket);
console.log(`ETH-USD Max Leverage: ${maxLeverage}x`);
Orderbook Data
Get Market Orderbook
Copy
async function getOrderbook(client: CompositeClient, marketId: string) {
const orderbook =
await client.indexerClient.markets.getPerpetualMarketOrderbook(marketId);
console.log(`${marketId} Orderbook:`);
// Display top 10 asks (sell orders)
console.log("\nAsks (Sell Orders):");
orderbook.asks.slice(0, 10).forEach((ask: any) => {
console.log(
` $${parseFloat(ask.price).toFixed(2)} - ${parseFloat(ask.size).toFixed(
4
)}`
);
});
// Display top 10 bids (buy orders)
console.log("\nBids (Buy Orders):");
orderbook.bids.slice(0, 10).forEach((bid: any) => {
console.log(
` $${parseFloat(bid.price).toFixed(2)} - ${parseFloat(bid.size).toFixed(
4
)}`
);
});
return orderbook;
}
await getOrderbook(client, "ETH-USD");
Orderbook Structure
Copy
interface OrderbookLevel {
price: string; // Price level
size: string; // Total size at this price
}
interface Orderbook {
bids: OrderbookLevel[]; // Buy orders (descending by price)
asks: OrderbookLevel[]; // Sell orders (ascending by price)
}
Calculate Spread
Copy
function calculateSpread(orderbook: any): number {
if (orderbook.bids.length === 0 || orderbook.asks.length === 0) {
return 0;
}
const bestBid = parseFloat(orderbook.bids[0].price);
const bestAsk = parseFloat(orderbook.asks[0].price);
const spread = bestAsk - bestBid;
const spreadPercent = (spread / bestBid) * 100;
console.log(`Best Bid: $${bestBid.toFixed(2)}`);
console.log(`Best Ask: $${bestAsk.toFixed(2)}`);
console.log(`Spread: $${spread.toFixed(2)} (${spreadPercent.toFixed(3)}%)`);
return spread;
}
const orderbook =
await client.indexerClient.markets.getPerpetualMarketOrderbook("ETH-USD");
calculateSpread(orderbook);
Calculate Market Depth
Copy
function calculateMarketDepth(orderbook: any, depthPercent: number = 1): any {
const bestBid = parseFloat(orderbook.bids[0]?.price || 0);
const bestAsk = parseFloat(orderbook.asks[0]?.price || 0);
// Calculate depth for bids
let bidDepth = 0;
const bidPriceFloor = bestBid * (1 - depthPercent / 100);
for (const bid of orderbook.bids) {
const price = parseFloat(bid.price);
if (price < bidPriceFloor) break;
bidDepth += parseFloat(bid.size) * price;
}
// Calculate depth for asks
let askDepth = 0;
const askPriceCeil = bestAsk * (1 + depthPercent / 100);
for (const ask of orderbook.asks) {
const price = parseFloat(ask.price);
if (price > askPriceCeil) break;
askDepth += parseFloat(ask.size) * price;
}
console.log(`Market Depth (${depthPercent}%):`);
console.log(` Bid Depth: $${bidDepth.toFixed(2)}`);
console.log(` Ask Depth: $${askDepth.toFixed(2)}`);
console.log(` Total Depth: $${(bidDepth + askDepth).toFixed(2)}`);
return { bidDepth, askDepth, totalDepth: bidDepth + askDepth };
}
const depth = calculateMarketDepth(orderbook, 1); // 1% depth
Funding Rates
Understanding Funding Rates
Funding rates are periodic payments between longs and shorts to keep perpetual prices close to spot:- Positive funding rate: Longs pay shorts (perp price > spot)
- Negative funding rate: Shorts pay longs (perp price < spot)
Copy
function analyzeFundingRate(market: any) {
const fundingRate = parseFloat(market.nextFundingRate);
const fundingRatePercent = fundingRate * 100;
console.log(
`${market.ticker} Funding Rate: ${fundingRatePercent.toFixed(4)}%`
);
if (fundingRate > 0) {
console.log("→ Longs pay shorts (bullish sentiment)");
console.log(` Annualized: ${(fundingRate * 365 * 3 * 100).toFixed(2)}%`); // 3 times per day
} else if (fundingRate < 0) {
console.log("→ Shorts pay longs (bearish sentiment)");
console.log(` Annualized: ${(fundingRate * 365 * 3 * 100).toFixed(2)}%`);
} else {
console.log("→ No funding payment (balanced)");
}
// Calculate funding cost for a position
const positionSize = 10000; // $10k position
const fundingCost = positionSize * fundingRate;
console.log(`Funding cost for $10k position: $${fundingCost.toFixed(2)}`);
}
const markets = await client.indexerClient.markets.getPerpetualMarkets();
analyzeFundingRate(markets.markets["ETH-USD"]);
Find Best Funding Opportunities
Copy
async function findFundingOpportunities(client: CompositeClient) {
const response = await client.indexerClient.markets.getPerpetualMarkets();
const fundingRates = Object.entries(response.markets).map(
([id, market]: [string, any]) => ({
marketId: id,
ticker: market.ticker,
fundingRate: parseFloat(market.nextFundingRate),
volume24H: parseFloat(market.volume24H),
})
);
// Sort by funding rate
fundingRates.sort((a, b) => b.fundingRate - a.fundingRate);
console.log("Highest Positive Funding (Longs pay shorts):");
fundingRates.slice(0, 5).forEach((market) => {
const annualized = market.fundingRate * 365 * 3 * 100; // 3x daily
console.log(
` ${market.ticker}: ${(market.fundingRate * 100).toFixed(
4
)}% (${annualized.toFixed(2)}% APR)`
);
});
console.log("\nHighest Negative Funding (Shorts pay longs):");
fundingRates
.slice(-5)
.reverse()
.forEach((market) => {
const annualized = market.fundingRate * 365 * 3 * 100;
console.log(
` ${market.ticker}: ${(market.fundingRate * 100).toFixed(
4
)}% (${annualized.toFixed(2)}% APR)`
);
});
}
Trading Signals
Price Change Analysis
Copy
async function analyzePriceMovements(client: CompositeClient) {
const response = await client.indexerClient.markets.getPerpetualMarkets();
const movements = Object.entries(response.markets)
.map(([id, market]: [string, any]) => ({
ticker: market.ticker,
priceChange: parseFloat(market.priceChange24H),
volume: parseFloat(market.volume24H),
trades: market.trades24H,
openInterest: parseFloat(market.openInterest),
}))
.filter((m) => m.volume > 100000) // Min $100k volume
.sort((a, b) => Math.abs(b.priceChange) - Math.abs(a.priceChange));
console.log("Largest Price Movements (24h):");
movements.slice(0, 10).forEach((market, index) => {
const direction = market.priceChange > 0 ? "📈" : "📉";
console.log(
`${index + 1}. ${direction} ${market.ticker}: ${
market.priceChange > 0 ? "+" : ""
}${market.priceChange.toFixed(2)}%`
);
console.log(
` Volume: $${market.volume.toFixed(0)}, Trades: ${market.trades}`
);
});
}
Volume Analysis
Copy
async function analyzeVolumeSpikes(client: CompositeClient) {
const response = await client.indexerClient.markets.getPerpetualMarkets();
const volumeData = Object.values(response.markets).map((market: any) => ({
ticker: market.ticker,
volume24H: parseFloat(market.volume24H),
trades24H: market.trades24H,
avgTradeSize: parseFloat(market.volume24H) / (market.trades24H || 1),
openInterest: parseFloat(market.openInterest),
volumeToOI:
parseFloat(market.volume24H) / (parseFloat(market.openInterest) || 1),
}));
// Sort by volume/OI ratio (indicates activity level)
volumeData.sort((a, b) => b.volumeToOI - a.volumeToOI);
console.log("Most Active Markets (Volume/OI Ratio):");
volumeData.slice(0, 10).forEach((market, index) => {
console.log(`${index + 1}. ${market.ticker}:`);
console.log(` Volume: $${market.volume24H.toFixed(0)}`);
console.log(` Open Interest: $${market.openInterest.toFixed(0)}`);
console.log(` Ratio: ${market.volumeToOI.toFixed(2)}x`);
});
}
Market Monitoring
Real-time Market Monitor
Copy
class MarketMonitor {
private client: CompositeClient;
private markets: string[];
private intervalId?: NodeJS.Timeout;
constructor(client: CompositeClient, markets: string[]) {
this.client = client;
this.markets = markets;
}
async checkMarkets() {
console.log(`\n[${new Date().toISOString()}] Market Update:`);
const response =
await this.client.indexerClient.markets.getPerpetualMarkets();
for (const marketId of this.markets) {
const market = response.markets[marketId];
if (!market) continue;
console.log(`\n${market.ticker}:`);
console.log(` Price: $${market.oraclePrice}`);
console.log(` 24h Change: ${market.priceChange24H}%`);
console.log(` Volume: $${market.volume24H}`);
console.log(` Funding: ${parseFloat(market.nextFundingRate) * 100}%`);
// Get orderbook
const orderbook =
await this.client.indexerClient.markets.getPerpetualMarketOrderbook(
marketId
);
const bestBid = parseFloat(orderbook.bids[0]?.price || "0");
const bestAsk = parseFloat(orderbook.asks[0]?.price || "0");
const spread = ((bestAsk - bestBid) / bestBid) * 100;
console.log(` Best Bid: $${bestBid.toFixed(2)}`);
console.log(` Best Ask: $${bestAsk.toFixed(2)}`);
console.log(` Spread: ${spread.toFixed(3)}%`);
}
}
startMonitoring(intervalSeconds: number = 60) {
console.log(
`Starting market monitoring every ${intervalSeconds} seconds...`
);
this.intervalId = setInterval(async () => {
try {
await this.checkMarkets();
} catch (error) {
console.error("Error checking markets:", error);
}
}, intervalSeconds * 1000);
// Initial check
this.checkMarkets();
}
stopMonitoring() {
if (this.intervalId) {
clearInterval(this.intervalId);
console.log("Market monitoring stopped");
}
}
}
// Usage
const monitor = new MarketMonitor(client, ["ETH-USD", "BTC-USD", "SOL-USD"]);
monitor.startMonitoring(60); // Update every minute
Market Data Utilities
Price Ticker
Copy
class PriceTicker {
private client: CompositeClient;
private prices: Map<string, number> = new Map();
constructor(client: CompositeClient) {
this.client = client;
}
async updatePrices() {
const response =
await this.client.indexerClient.markets.getPerpetualMarkets();
for (const [marketId, market] of Object.entries(response.markets)) {
this.prices.set(marketId, parseFloat((market as any).oraclePrice));
}
}
getPrice(marketId: string): number | undefined {
return this.prices.get(marketId);
}
getAllPrices(): Map<string, number> {
return new Map(this.prices);
}
async startAutoUpdate(intervalMs: number = 5000) {
await this.updatePrices(); // Initial update
setInterval(async () => {
try {
await this.updatePrices();
} catch (error) {
console.error("Error updating prices:", error);
}
}, intervalMs);
}
}
// Usage
const ticker = new PriceTicker(client);
await ticker.startAutoUpdate(5000); // Update every 5 seconds
// Get specific price
const ethPrice = ticker.getPrice("ETH-USD");
console.log(`ETH: $${ethPrice}`);
Best Practices
Cache Market Data
Cache Market Data
Cache market data to reduce API calls:
Copy
class MarketDataCache {
private cache: Map<string, { data: any; timestamp: number }> = new Map();
private ttl: number = 60000; // 60 seconds
set(key: string, data: any) {
this.cache.set(key, { data, timestamp: Date.now() });
}
get(key: string): any | null {
const entry = this.cache.get(key);
if (!entry) return null;
if (Date.now() - entry.timestamp > this.ttl) {
this.cache.delete(key);
return null;
}
return entry.data;
}
}
Handle Market Status
Handle Market Status
Check market status before trading:
Copy
function canTrade(market: any): boolean {
return market.status === "ACTIVE";
}
function canPlaceOrders(market: any): boolean {
return ["ACTIVE", "POST_ONLY"].includes(market.status);
}
Monitor Orderbook Liquidity
Monitor Orderbook Liquidity
Assess liquidity before placing large orders:
Copy
function hasEnoughLiquidity(
orderbook: any,
side: "BUY" | "SELL",
size: number,
priceSlippage: number = 0.01 // 1%
): boolean {
const levels = side === "BUY" ? orderbook.asks : orderbook.bids;
const bestPrice = parseFloat(levels[0]?.price || 0);
const maxPrice = side === "BUY"
? bestPrice * (1 + priceSlippage)
: bestPrice * (1 - priceSlippage);
let availableSize = 0;
for (const level of levels) {
const price = parseFloat(level.price);
if ((side === "BUY" && price > maxPrice) ||
(side === "SELL" && price < maxPrice)) {
break;
}
availableSize += parseFloat(level.size);
if (availableSize >= size) return true;
}
return false;
}