Thanks to visit codestin.com
Credit goes to GitHub.com

Skip to content

gtg7784/dr-manhattan-ts

Repository files navigation

dr-manhattan

CCXT-style unified API for prediction markets in TypeScript.

TypeScript port of guzus/dr-manhattan (Python)

Node.js TypeScript License

Supported Exchanges

Exchange REST WebSocket Chain
Polymarket âś… âś… Polygon
Limitless âś… âś… Base
Opinion ✅ ❌ BNB
Kalshi ✅ ❌ -
Predict.fun ✅ ❌ BNB

Installation

npm install @alango/dr-manhattan
# or
pnpm add @alango/dr-manhattan
# or
yarn add @alango/dr-manhattan

Quick Start

import { createExchange, listExchanges, MarketUtils } from '@alango/dr-manhattan';

// List available exchanges
console.log(listExchanges()); // ['polymarket', 'limitless', 'opinion', 'kalshi', 'predictfun']

// Create exchange instance (no auth required for public data)
const polymarket = createExchange('polymarket');

// Fetch markets
const markets = await polymarket.fetchMarkets({ limit: 10 });

for (const market of markets) {
  console.log(`${market.question}`);
  console.log(`  Volume: $${market.volume.toLocaleString()}`);
  console.log(`  Binary: ${MarketUtils.isBinary(market)}`);
  console.log(`  Spread: ${MarketUtils.spread(market)?.toFixed(4)}`);
}

Authentication

Polymarket

import { Polymarket } from '@alango/dr-manhattan';

const polymarket = new Polymarket({
  privateKey: process.env.PRIVATE_KEY,
  funder: process.env.FUNDER_ADDRESS, // optional
  chainId: 137, // Polygon (default)
});

// Create order
const order = await polymarket.createOrder({
  marketId: 'market-condition-id',
  outcome: 'Yes',
  side: OrderSide.BUY,
  price: 0.65,
  size: 100,
  tokenId: 'outcome-token-id',
});

// Fetch balance
const balance = await polymarket.fetchBalance();
console.log(`USDC: ${balance.USDC}`);

Limitless

import { Limitless } from '@alango/dr-manhattan';

const limitless = new Limitless({
  privateKey: process.env.PRIVATE_KEY,
});

// Authentication happens automatically via EIP-191/EIP-712 signing
const positions = await limitless.fetchPositions();

Opinion

import { Opinion } from '@alango/dr-manhattan';

const opinion = new Opinion({
  apiKey: process.env.OPINION_API_KEY,
  privateKey: process.env.PRIVATE_KEY,
  multiSigAddr: process.env.MULTI_SIG_ADDR,
});

Kalshi

import { Kalshi } from '@alango/dr-manhattan';

// With RSA private key file
const kalshi = new Kalshi({
  apiKeyId: process.env.KALSHI_API_KEY_ID,
  privateKeyPath: '/path/to/kalshi_private_key.pem',
});

// Or with PEM content directly
const kalshi = new Kalshi({
  apiKeyId: process.env.KALSHI_API_KEY_ID,
  privateKeyPem: process.env.KALSHI_PRIVATE_KEY_PEM,
});

// Demo environment
const kalshiDemo = new Kalshi({
  apiKeyId: process.env.KALSHI_API_KEY_ID,
  privateKeyPath: '/path/to/private_key.pem',
  demo: true,
});

// Fetch markets (no auth required)
const markets = await kalshi.fetchMarkets({ limit: 10 });

// Create order (auth required)
const order = await kalshi.createOrder({
  marketId: 'INXD-24DEC31-B5000',
  outcome: 'Yes',
  side: OrderSide.BUY,
  price: 0.55,
  size: 10,
});

Predict.fun

import { PredictFun } from '@alango/dr-manhattan';

// Mainnet
const predictfun = new PredictFun({
  apiKey: process.env.PREDICTFUN_API_KEY,
  privateKey: process.env.PRIVATE_KEY,
});

// Testnet
const predictfunTestnet = new PredictFun({
  apiKey: process.env.PREDICTFUN_API_KEY,
  privateKey: process.env.PRIVATE_KEY,
  testnet: true,
});

// Fetch markets (no auth required for public data)
const markets = await predictfun.fetchMarkets({ limit: 10 });

// Get orderbook
const orderbook = await predictfun.getOrderbook(marketId);

// Create order (auth required)
const order = await predictfun.createOrder({
  marketId: '123',
  outcome: 'Yes',
  side: OrderSide.BUY,
  price: 0.55,
  size: 100,
});

// Fetch positions
const positions = await predictfun.fetchPositions();

// Fetch balance
const balance = await predictfun.fetchBalance();
console.log(`USDT: ${balance.USDT}`);

API Reference

Exchange Methods

All exchanges implement these core methods:

interface Exchange {
  // Market data
  fetchMarkets(params?: FetchMarketsParams): Promise<Market[]>;
  fetchMarket(marketId: string): Promise<Market>;

  // Orders (requires auth)
  createOrder(params: CreateOrderParams): Promise<Order>;
  cancelOrder(orderId: string, marketId?: string): Promise<Order>;
  fetchOrder(orderId: string, marketId?: string): Promise<Order>;
  fetchOpenOrders(marketId?: string): Promise<Order[]>;

  // Account (requires auth)
  fetchPositions(marketId?: string): Promise<Position[]>;
  fetchBalance(): Promise<Record<string, number>>;

  // Utilities
  describe(): { id: string; name: string; has: ExchangeCapabilities };
  findTradeableMarket(options?: { binary?: boolean; minLiquidity?: number }): Promise<Market | null>;
  calculateSpread(market: Market): number | null;
}

Polymarket-specific Methods

// Search markets by keyword
const markets = await polymarket.searchMarkets('bitcoin');

// Fetch by slug
const market = await polymarket.fetchMarketsBySlug('bitcoin-100k');

// Get orderbook
const orderbook = await polymarket.getOrderbook(tokenId);

// Fetch price history
const history = await polymarket.fetchPriceHistory(tokenId, '1d');

// Fetch public trades
const trades = await polymarket.fetchPublicTrades(tokenId, { limit: 50 });

// Find crypto hourly markets
const hourlyMarket = await polymarket.findCryptoHourlyMarket('BTC', 'higher');

WebSocket Streaming

Polymarket WebSocket

import { PolymarketWebSocket, OrderbookUtils } from '@alango/dr-manhattan';

const ws = new PolymarketWebSocket();

ws.on('open', () => {
  ws.subscribeToOrderbook([tokenId1, tokenId2]);
});

ws.on('orderbook', ({ tokenId, orderbook }) => {
  const bid = OrderbookUtils.bestBid(orderbook);
  const ask = OrderbookUtils.bestAsk(orderbook);
  console.log(`[${tokenId}] Bid: ${bid} | Ask: ${ask}`);
});

ws.on('error', (err) => console.error(err));
ws.on('close', () => console.log('Disconnected'));

await ws.connect();

// Cleanup
await ws.disconnect();

Limitless WebSocket

import { LimitlessWebSocket } from '@alango/dr-manhattan';

const ws = new LimitlessWebSocket();

ws.on('orderbook', ({ marketAddress, orderbook }) => {
  console.log(`[${marketAddress}] Updated`);
});

ws.on('price', ({ marketAddress, prices }) => {
  console.log(`Prices:`, prices);
});

await ws.connect();
ws.subscribeToMarket(marketAddress);

Utilities

Market Utilities

import { MarketUtils } from '@alango/dr-manhattan';

MarketUtils.isBinary(market);      // Has exactly 2 outcomes
MarketUtils.isOpen(market);        // Not closed, not resolved
MarketUtils.spread(market);        // Price spread between outcomes
MarketUtils.getTokenIds(market);   // Extract token IDs

Orderbook Utilities

import { OrderbookUtils } from '@alango/dr-manhattan';

OrderbookUtils.bestBid(orderbook);     // Highest bid price
OrderbookUtils.bestAsk(orderbook);     // Lowest ask price
OrderbookUtils.spread(orderbook);      // Ask - Bid
OrderbookUtils.midPrice(orderbook);    // (Bid + Ask) / 2
OrderbookUtils.totalVolume(orderbook, 'bids'); // Sum of bid sizes

Position Utilities

import { PositionUtils, calculateDelta } from '@alango/dr-manhattan';

PositionUtils.totalValue(positions);
PositionUtils.totalPnl(positions);
PositionUtils.filterByMarket(positions, marketId);

// Calculate position delta
const delta = calculateDelta(positions, market);
// { yes: 100, no: -50, net: 50 }

Price Utilities

import { roundToTickSize, clampPrice, formatPrice, formatUsd } from '@alango/dr-manhattan';

roundToTickSize(0.6543, 0.01);  // 0.65
clampPrice(1.5);                 // 1.0
formatPrice(0.6543);             // "0.654"
formatUsd(1234567);              // "$1,234,567"

Error Handling

import {
  DrManhattanError,
  ExchangeError,
  NetworkError,
  RateLimitError,
  AuthenticationError,
  InsufficientFunds,
  InvalidOrder,
  MarketNotFound,
} from '@alango/dr-manhattan';

try {
  await exchange.createOrder(params);
} catch (error) {
  if (error instanceof RateLimitError) {
    console.log(`Rate limited, retry after ${error.retryAfter}ms`);
  } else if (error instanceof InsufficientFunds) {
    console.log('Not enough balance');
  } else if (error instanceof InvalidOrder) {
    console.log('Invalid order parameters');
  }
}

Types

import type {
  Market,
  OutcomeToken,
  Order,
  CreateOrderParams,
  Position,
  DeltaInfo,
  Orderbook,
  PriceLevel,
  FetchMarketsParams,
  ExchangeConfig,
  ExchangeCapabilities,
} from '@alango/dr-manhattan';

import { OrderSide, OrderStatus } from '@alango/dr-manhattan';

Adding New Exchanges

import { Exchange, type ExchangeConfig } from '@alango/dr-manhattan';

class NewExchange extends Exchange {
  readonly id = 'newexchange';
  readonly name = 'New Exchange';

  async fetchMarkets(params?: FetchMarketsParams): Promise<Market[]> {
    // Implement API call
  }

  async fetchMarket(marketId: string): Promise<Market> {
    // Implement
  }

  // ... implement other abstract methods
}

Configuration Options

interface ExchangeConfig {
  // Authentication
  apiKey?: string;
  apiSecret?: string;
  privateKey?: string;
  funder?: string;

  // Request settings
  timeout?: number;      // Request timeout in ms (default: 30000)
  rateLimit?: number;    // Max requests per second (default: 10)
  maxRetries?: number;   // Retry count for failed requests (default: 3)
  retryDelay?: number;   // Initial retry delay in ms (default: 1000)
  retryBackoff?: number; // Backoff multiplier (default: 2)

  // Debug
  verbose?: boolean;     // Log debug info (default: false)
}

Examples

See the examples/ directory:

Example Description Exchanges
list-markets.ts Fetch and display markets from all exchanges All
websocket-orderbook.ts Real-time orderbook streaming via WebSocket Polymarket
spread-strategy.ts Market making strategy with inventory management All
spike-strategy.ts Mean reversion strategy - buys price spikes All
weather-bot-strategy.ts London temperature bucket mispricing strategy Polymarket

Running Examples

# List markets from all exchanges
npx tsx examples/list-markets.ts

# WebSocket orderbook streaming (Polymarket)
npx tsx examples/websocket-orderbook.ts

# Spread strategy - works with any exchange
# Polymarket (WebSocket)
EXCHANGE=polymarket PRIVATE_KEY=0x... npx tsx examples/spread-strategy.ts

# Limitless (WebSocket)
EXCHANGE=limitless PRIVATE_KEY=0x... npx tsx examples/spread-strategy.ts

# Kalshi (REST polling)
EXCHANGE=kalshi KALSHI_API_KEY_ID=... KALSHI_PRIVATE_KEY_PATH=./key.pem npx tsx examples/spread-strategy.ts

# Predict.fun (REST polling)
EXCHANGE=predictfun PREDICTFUN_API_KEY=... PRIVATE_KEY=0x... npx tsx examples/spread-strategy.ts

# Opinion (REST polling)
EXCHANGE=opinion OPINION_API_KEY=... PRIVATE_KEY=0x... npx tsx examples/spread-strategy.ts

# Simulation mode (no credentials = no real trades)
EXCHANGE=polymarket npx tsx examples/spread-strategy.ts

# Spike strategy - mean reversion (buys dips)
EXCHANGE=polymarket PRIVATE_KEY=0x... npx tsx examples/spike-strategy.ts

# Spike strategy with custom parameters
npx tsx examples/spike-strategy.ts --spike-threshold 0.02 --profit-target 0.03 --stop-loss 0.02

# Weather bot strategy - London temperature bucket mispricing
npx tsx examples/weather-bot-strategy.ts --dry-run

# Weather bot with live trading
PRIVATE_KEY=0x... npx tsx examples/weather-bot-strategy.ts --order-size 5

Requirements

  • Node.js >= 20.0.0
  • TypeScript >= 5.0 (for development)

License

MIT