Skip to main content
Glama

get_arb_compare

Compare active arbitrage positions side by side to analyze ROI, funding income, price PnL, and annualized returns for each position.

Instructions

Compare active arbitrage positions side by side — ROI, funding income, price PnL, and annualized returns for each position

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
periodNoFunding lookup period: 7d, 30d, 90d, or allall

Implementation Reference

  • The handler implementation for the get_arb_compare tool, which compares active arbitrage positions.
      "get_arb_compare",
      "Compare active arbitrage positions side by side — ROI, funding income, price PnL, and annualized returns for each position",
      {
        period: z.string().default("all").describe("Funding lookup period: 7d, 30d, 90d, or all"),
      },
      async ({ period }) => {
        try {
          const arbState = loadArbState();
          const positions = arbState?.positions ?? [];
          if (positions.length === 0) {
            return { content: [{ type: "text", text: ok({ positions: [], message: "No active arb positions" }) }] };
          }
    
          const periodMatch = period === "all" ? null : period.match(/^(\d+)(d|w|m)$/);
          const sinceMs = periodMatch
            ? Date.now() - parseInt(periodMatch[1]) * ({ d: 86400000, w: 604800000, m: 2592000000 }[periodMatch[2]] ?? 86400000)
            : 0;
    
          const exchangeSet = new Set<string>();
          for (const pos of positions) { exchangeSet.add(pos.longExchange); exchangeSet.add(pos.shortExchange); }
    
          const fundingByExSym = new Map<string, number>();
          const currentPrices = new Map<string, number>();
    
          await Promise.allSettled([...exchangeSet].map(async (ex) => {
            try {
              const adapter = await getOrCreateAdapter(ex);
              const [funding, livePos] = await Promise.all([adapter.getFundingPayments(200), adapter.getPositions()]);
              for (const f of funding) {
                if (sinceMs && f.time < sinceMs) continue;
                const key = `${ex}:${f.symbol}`;
                fundingByExSym.set(key, (fundingByExSym.get(key) ?? 0) + Number(f.payment));
              }
              for (const p of livePos) currentPrices.set(`${ex}:${p.symbol}`, Number(p.markPrice));
            } catch { /* skip */ }
          }));
    
          const rows = positions.map(pos => {
            const daysHeld = Math.max(1, Math.round((Date.now() - new Date(pos.entryTime).getTime()) / 86400000));
            const funding = (fundingByExSym.get(`${pos.longExchange}:${pos.symbol}`) ?? 0) + (fundingByExSym.get(`${pos.shortExchange}:${pos.symbol}`) ?? 0);
            const longPrice = currentPrices.get(`${pos.longExchange}:${pos.symbol}`) ?? pos.entryLongPrice;
            const shortPrice = currentPrices.get(`${pos.shortExchange}:${pos.symbol}`) ?? pos.entryShortPrice;
            const pricePnl = (longPrice - pos.entryLongPrice) * pos.longSize + (pos.entryShortPrice - shortPrice) * pos.shortSize;
            const totalPnl = pricePnl + funding;
            const notional = pos.entryLongPrice * pos.longSize + pos.entryShortPrice * pos.shortSize;
            const roi = notional > 0 ? (totalPnl / notional) * 100 : 0;
            const fundingRoi = notional > 0 ? (funding / notional) * 100 : 0;
            return { symbol: pos.symbol, mode: pos.mode ?? "perp-perp", longExchange: pos.longExchange, shortExchange: pos.shortExchange, daysHeld, funding, pricePnl, totalPnl, roi, annualizedRoi: daysHeld > 0 ? (fundingRoi / daysHeld) * 365 : 0 };
          }).sort((a, b) => b.totalPnl - a.totalPnl);
    
          const totals = { funding: rows.reduce((s, r) => s + r.funding, 0), pricePnl: rows.reduce((s, r) => s + r.pricePnl, 0), totalPnl: rows.reduce((s, r) => s + r.totalPnl, 0) };
    
          return { content: [{ type: "text", text: ok({ period, positionCount: rows.length, positions: rows, totals }) }] };
        } catch (e) {
          return { content: [{ type: "text", text: err(e instanceof Error ? e.message : String(e)) }], isError: true };
        }
      },
    );

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hypurrquant/perp-cli'

If you have feedback or need assistance with the MCP directory API, please join our Discord server