PortfolioFetcher Setup
PortfolioFetcher provides access to your profile, positions, trade history, and accumulated points. Authentication is required.
import { HttpClient, PortfolioFetcher } from '@limitless-exchange/sdk';
const httpClient = new HttpClient({
baseURL: 'https://api.limitless.exchange',
apiKey: process.env.LIMITLESS_API_KEY,
});
const portfolio = new PortfolioFetcher(httpClient);
Current Profile
Call getProfile() without an address to fetch the authenticated caller’s private profile via GET /profiles/me. This avoids passing or persisting the wallet address when the client is already authenticated.
const currentProfile = await portfolio.getProfile();
console.log(currentProfile.id); // numeric profile ID
console.log(currentProfile.account); // authenticated wallet address
console.log(currentProfile.rank?.feeRateBps); // fee rate used for signing
To keep the existing address-based lookup, pass an address:
const profileByAddress = await portfolio.getProfile('0xYOUR_WALLET_ADDRESS');
getProfile() without an address calls GET /profiles/me; getProfile(address) calls GET /profiles/:account. Both return the private authenticated profile shape and require authentication.
All Positions
Fetch all positions across CLOB and AMM markets in a single call:
const positions = await portfolio.getPositions();
console.log('CLOB positions:', positions.clob.length);
console.log('AMM positions:', positions.amm.length);
console.log('Accumulative points:', positions.accumulativePoints);
Response Structure
interface PositionsResponse {
clob: ClobPosition[];
amm: AmmPosition[];
accumulativePoints: number;
}
CLOB Positions
Retrieve only your CLOB market positions:
const clobPositions = await portfolio.getCLOBPositions();
for (const position of clobPositions) {
console.log(`Market: ${position.market.slug}`);
console.log(` YES — size: ${position.positions.yes.size}, PnL: ${position.positions.yes.unrealizedPnl}`);
console.log(` NO — size: ${position.positions.no.size}, PnL: ${position.positions.no.unrealizedPnl}`);
}
CLOB Position Structure
interface ClobPosition {
market: {
slug: string;
title: string;
};
positions: {
yes: {
size: number;
collateral: number;
unrealizedPnl: number;
};
no: {
size: number;
collateral: number;
unrealizedPnl: number;
};
};
}
| Field | Description |
|---|
size | Number of shares held |
collateral | USDC value of collateral locked |
unrealizedPnl | Unrealized profit/loss in USDC based on current orderbook prices |
AMM Positions
Retrieve only your AMM market positions:
const ammPositions = await portfolio.getAMMPositions();
for (const position of ammPositions) {
console.log(`Market: ${position.market.slug}`);
console.log(` YES — size: ${position.positions.yes.size}`);
console.log(` NO — size: ${position.positions.no.size}`);
}
Trade History
Fetch paginated trade history:
const history = await portfolio.getUserHistory();
for (const entry of history.data) {
console.log(entry.type, entry.market.slug, entry.amount, entry.timestamp);
}
// Next page: pass the returned cursor
if (history.nextCursor) {
const page2 = await portfolio.getUserHistory(history.nextCursor, 20);
}
Parameters
| Parameter | Type | Default | Description |
|---|
cursor | string | — | Opaque cursor from the previous response’s nextCursor. Omit for the first page. |
limit | number | 20 | Number of entries per page |
Combining Positions with Market Data
For a richer view, combine position data with live market information:
import { MarketFetcher } from '@limitless-exchange/sdk';
const marketFetcher = new MarketFetcher(httpClient);
const positions = await portfolio.getCLOBPositions();
for (const position of positions) {
const market = await marketFetcher.getMarket(position.market.slug);
const orderbook = await marketFetcher.getOrderBook(position.market.slug);
const bestBid = orderbook.bids[0]?.price ?? 0;
const bestAsk = orderbook.asks[0]?.price ?? 1;
const midPrice = (bestBid + bestAsk) / 2;
console.log(`${market.title}`);
console.log(` Mid price: ${midPrice.toFixed(4)}`);
console.log(` YES: ${position.positions.yes.size} shares, PnL: ${position.positions.yes.unrealizedPnl}`);
console.log(` NO: ${position.positions.no.size} shares, PnL: ${position.positions.no.unrealizedPnl}`);
}
For real-time PnL tracking, combine PortfolioFetcher with WebSocket orderbook updates rather than repeatedly polling getOrderBook(). See the WebSocket Streaming guide.
Error Handling
Portfolio endpoints require a valid API key. If the key is missing or invalid, the SDK throws an APIError with status 401.
import { APIError } from '@limitless-exchange/sdk';
try {
const positions = await portfolio.getPositions();
} catch (error) {
if (error instanceof APIError) {
if (error.status === 401) {
console.error('Authentication failed. Check your LIMITLESS_API_KEY.');
} else {
console.error(`API error ${error.status}: ${error.message}`);
}
} else {
throw error;
}
}
See the Error Handling and Retry guide for retry strategies and common error codes.