@alango/dr-manhattan 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Taegeon Go (Alan)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,365 @@
1
+ # dr-manhattan
2
+
3
+ CCXT-style unified API for prediction markets in TypeScript.
4
+
5
+ > TypeScript port of [guzus/dr-manhattan](https://github.com/guzus/dr-manhattan) (Python)
6
+
7
+ [![Node.js](https://img.shields.io/badge/Node.js-20%2B-green)](https://nodejs.org)
8
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue)](https://www.typescriptlang.org)
9
+ [![License](https://img.shields.io/badge/License-MIT-yellow)](LICENSE)
10
+
11
+ ## Supported Exchanges
12
+
13
+ | Exchange | REST | WebSocket | Chain |
14
+ |----------|------|-----------|-------|
15
+ | [Polymarket](https://polymarket.com) | ✅ | ✅ | Polygon |
16
+ | [Limitless](https://limitless.exchange) | ✅ | ✅ | Base |
17
+ | [Opinion](https://opinion.trade) | ✅ | ❌ | BNB |
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @alango/dr-manhattan
23
+ # or
24
+ pnpm add @alango/dr-manhattan
25
+ # or
26
+ yarn add @alango/dr-manhattan
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```typescript
32
+ import { createExchange, listExchanges, MarketUtils } from '@alango/dr-manhattan';
33
+
34
+ // List available exchanges
35
+ console.log(listExchanges()); // ['polymarket', 'limitless', 'opinion']
36
+
37
+ // Create exchange instance (no auth required for public data)
38
+ const polymarket = createExchange('polymarket');
39
+
40
+ // Fetch markets
41
+ const markets = await polymarket.fetchMarkets({ limit: 10 });
42
+
43
+ for (const market of markets) {
44
+ console.log(`${market.question}`);
45
+ console.log(` Volume: $${market.volume.toLocaleString()}`);
46
+ console.log(` Binary: ${MarketUtils.isBinary(market)}`);
47
+ console.log(` Spread: ${MarketUtils.spread(market)?.toFixed(4)}`);
48
+ }
49
+ ```
50
+
51
+ ## Authentication
52
+
53
+ ### Polymarket
54
+
55
+ ```typescript
56
+ import { Polymarket } from '@alango/dr-manhattan';
57
+
58
+ const polymarket = new Polymarket({
59
+ privateKey: process.env.PRIVATE_KEY,
60
+ funder: process.env.FUNDER_ADDRESS, // optional
61
+ chainId: 137, // Polygon (default)
62
+ });
63
+
64
+ // Create order
65
+ const order = await polymarket.createOrder({
66
+ marketId: 'market-condition-id',
67
+ outcome: 'Yes',
68
+ side: OrderSide.BUY,
69
+ price: 0.65,
70
+ size: 100,
71
+ tokenId: 'outcome-token-id',
72
+ });
73
+
74
+ // Fetch balance
75
+ const balance = await polymarket.fetchBalance();
76
+ console.log(`USDC: ${balance.USDC}`);
77
+ ```
78
+
79
+ ### Limitless
80
+
81
+ ```typescript
82
+ import { Limitless } from '@alango/dr-manhattan';
83
+
84
+ const limitless = new Limitless({
85
+ privateKey: process.env.PRIVATE_KEY,
86
+ });
87
+
88
+ // Authentication happens automatically via EIP-191/EIP-712 signing
89
+ const positions = await limitless.fetchPositions();
90
+ ```
91
+
92
+ ### Opinion
93
+
94
+ ```typescript
95
+ import { Opinion } from '@alango/dr-manhattan';
96
+
97
+ const opinion = new Opinion({
98
+ apiKey: process.env.OPINION_API_KEY,
99
+ privateKey: process.env.PRIVATE_KEY,
100
+ multiSigAddr: process.env.MULTI_SIG_ADDR,
101
+ });
102
+ ```
103
+
104
+ ## API Reference
105
+
106
+ ### Exchange Methods
107
+
108
+ All exchanges implement these core methods:
109
+
110
+ ```typescript
111
+ interface Exchange {
112
+ // Market data
113
+ fetchMarkets(params?: FetchMarketsParams): Promise<Market[]>;
114
+ fetchMarket(marketId: string): Promise<Market>;
115
+
116
+ // Orders (requires auth)
117
+ createOrder(params: CreateOrderParams): Promise<Order>;
118
+ cancelOrder(orderId: string, marketId?: string): Promise<Order>;
119
+ fetchOrder(orderId: string, marketId?: string): Promise<Order>;
120
+ fetchOpenOrders(marketId?: string): Promise<Order[]>;
121
+
122
+ // Account (requires auth)
123
+ fetchPositions(marketId?: string): Promise<Position[]>;
124
+ fetchBalance(): Promise<Record<string, number>>;
125
+
126
+ // Utilities
127
+ describe(): { id: string; name: string; has: ExchangeCapabilities };
128
+ findTradeableMarket(options?: { binary?: boolean; minLiquidity?: number }): Promise<Market | null>;
129
+ calculateSpread(market: Market): number | null;
130
+ }
131
+ ```
132
+
133
+ ### Polymarket-specific Methods
134
+
135
+ ```typescript
136
+ // Search markets by keyword
137
+ const markets = await polymarket.searchMarkets('bitcoin');
138
+
139
+ // Fetch by slug
140
+ const market = await polymarket.fetchMarketsBySlug('bitcoin-100k');
141
+
142
+ // Get orderbook
143
+ const orderbook = await polymarket.getOrderbook(tokenId);
144
+
145
+ // Fetch price history
146
+ const history = await polymarket.fetchPriceHistory(tokenId, '1d');
147
+
148
+ // Fetch public trades
149
+ const trades = await polymarket.fetchPublicTrades(tokenId, { limit: 50 });
150
+
151
+ // Find crypto hourly markets
152
+ const hourlyMarket = await polymarket.findCryptoHourlyMarket('BTC', 'higher');
153
+ ```
154
+
155
+ ### WebSocket Streaming
156
+
157
+ #### Polymarket WebSocket
158
+
159
+ ```typescript
160
+ import { PolymarketWebSocket, OrderbookUtils } from '@alango/dr-manhattan';
161
+
162
+ const ws = new PolymarketWebSocket();
163
+
164
+ ws.on('open', () => {
165
+ ws.subscribeToOrderbook([tokenId1, tokenId2]);
166
+ });
167
+
168
+ ws.on('orderbook', ({ tokenId, orderbook }) => {
169
+ const bid = OrderbookUtils.bestBid(orderbook);
170
+ const ask = OrderbookUtils.bestAsk(orderbook);
171
+ console.log(`[${tokenId}] Bid: ${bid} | Ask: ${ask}`);
172
+ });
173
+
174
+ ws.on('error', (err) => console.error(err));
175
+ ws.on('close', () => console.log('Disconnected'));
176
+
177
+ await ws.connect();
178
+
179
+ // Cleanup
180
+ await ws.disconnect();
181
+ ```
182
+
183
+ #### Limitless WebSocket
184
+
185
+ ```typescript
186
+ import { Limitless } from '@alango/dr-manhattan';
187
+ import { LimitlessWebSocket } from '@alango/dr-manhattan/exchanges/limitless';
188
+
189
+ const ws = new LimitlessWebSocket();
190
+
191
+ ws.on('orderbook', ({ marketAddress, orderbook }) => {
192
+ console.log(`[${marketAddress}] Updated`);
193
+ });
194
+
195
+ ws.on('price', ({ marketAddress, prices }) => {
196
+ console.log(`Prices:`, prices);
197
+ });
198
+
199
+ await ws.connect();
200
+ ws.subscribeToMarket(marketAddress);
201
+ ```
202
+
203
+ ## Utilities
204
+
205
+ ### Market Utilities
206
+
207
+ ```typescript
208
+ import { MarketUtils } from '@alango/dr-manhattan';
209
+
210
+ MarketUtils.isBinary(market); // Has exactly 2 outcomes
211
+ MarketUtils.isOpen(market); // Not closed, not resolved
212
+ MarketUtils.spread(market); // Price spread between outcomes
213
+ MarketUtils.getTokenIds(market); // Extract token IDs
214
+ ```
215
+
216
+ ### Orderbook Utilities
217
+
218
+ ```typescript
219
+ import { OrderbookUtils } from '@alango/dr-manhattan';
220
+
221
+ OrderbookUtils.bestBid(orderbook); // Highest bid price
222
+ OrderbookUtils.bestAsk(orderbook); // Lowest ask price
223
+ OrderbookUtils.spread(orderbook); // Ask - Bid
224
+ OrderbookUtils.midPrice(orderbook); // (Bid + Ask) / 2
225
+ OrderbookUtils.totalVolume(orderbook, 'bids'); // Sum of bid sizes
226
+ ```
227
+
228
+ ### Position Utilities
229
+
230
+ ```typescript
231
+ import { PositionUtils, calculateDelta } from '@alango/dr-manhattan';
232
+
233
+ PositionUtils.totalValue(positions);
234
+ PositionUtils.totalPnl(positions);
235
+ PositionUtils.filterByMarket(positions, marketId);
236
+
237
+ // Calculate position delta
238
+ const delta = calculateDelta(positions, market);
239
+ // { yes: 100, no: -50, net: 50 }
240
+ ```
241
+
242
+ ### Price Utilities
243
+
244
+ ```typescript
245
+ import { roundToTickSize, clampPrice, formatPrice, formatUsd } from '@alango/dr-manhattan';
246
+
247
+ roundToTickSize(0.6543, 0.01); // 0.65
248
+ clampPrice(1.5); // 1.0
249
+ formatPrice(0.6543); // "0.654"
250
+ formatUsd(1234567); // "$1,234,567"
251
+ ```
252
+
253
+ ## Error Handling
254
+
255
+ ```typescript
256
+ import {
257
+ DrManhattanError,
258
+ ExchangeError,
259
+ NetworkError,
260
+ RateLimitError,
261
+ AuthenticationError,
262
+ InsufficientFunds,
263
+ InvalidOrder,
264
+ MarketNotFound,
265
+ } from '@alango/dr-manhattan';
266
+
267
+ try {
268
+ await exchange.createOrder(params);
269
+ } catch (error) {
270
+ if (error instanceof RateLimitError) {
271
+ console.log(`Rate limited, retry after ${error.retryAfter}ms`);
272
+ } else if (error instanceof InsufficientFunds) {
273
+ console.log('Not enough balance');
274
+ } else if (error instanceof InvalidOrder) {
275
+ console.log('Invalid order parameters');
276
+ }
277
+ }
278
+ ```
279
+
280
+ ## Types
281
+
282
+ ```typescript
283
+ import type {
284
+ Market,
285
+ OutcomeToken,
286
+ Order,
287
+ CreateOrderParams,
288
+ Position,
289
+ DeltaInfo,
290
+ Orderbook,
291
+ PriceLevel,
292
+ FetchMarketsParams,
293
+ ExchangeConfig,
294
+ ExchangeCapabilities,
295
+ } from '@alango/dr-manhattan';
296
+
297
+ import { OrderSide, OrderStatus } from '@alango/dr-manhattan';
298
+ ```
299
+
300
+ ## Adding New Exchanges
301
+
302
+ ```typescript
303
+ import { Exchange, type ExchangeConfig } from '@alango/dr-manhattan';
304
+
305
+ class NewExchange extends Exchange {
306
+ readonly id = 'newexchange';
307
+ readonly name = 'New Exchange';
308
+
309
+ async fetchMarkets(params?: FetchMarketsParams): Promise<Market[]> {
310
+ // Implement API call
311
+ }
312
+
313
+ async fetchMarket(marketId: string): Promise<Market> {
314
+ // Implement
315
+ }
316
+
317
+ // ... implement other abstract methods
318
+ }
319
+ ```
320
+
321
+ ## Configuration Options
322
+
323
+ ```typescript
324
+ interface ExchangeConfig {
325
+ // Authentication
326
+ apiKey?: string;
327
+ apiSecret?: string;
328
+ privateKey?: string;
329
+ funder?: string;
330
+
331
+ // Request settings
332
+ timeout?: number; // Request timeout in ms (default: 30000)
333
+ rateLimit?: number; // Max requests per second (default: 10)
334
+ maxRetries?: number; // Retry count for failed requests (default: 3)
335
+ retryDelay?: number; // Initial retry delay in ms (default: 1000)
336
+ retryBackoff?: number; // Backoff multiplier (default: 2)
337
+
338
+ // Debug
339
+ verbose?: boolean; // Log debug info (default: false)
340
+ }
341
+ ```
342
+
343
+ ## Examples
344
+
345
+ See the [examples/](examples/) directory:
346
+
347
+ - **list-markets.ts** - Fetch and display markets
348
+ - **websocket-orderbook.ts** - Real-time orderbook streaming
349
+ - **spread-strategy.ts** - Market making strategy with inventory management
350
+
351
+ ```bash
352
+ # Run examples
353
+ npx tsx examples/list-markets.ts
354
+ npx tsx examples/websocket-orderbook.ts
355
+ npx tsx examples/spread-strategy.ts # Requires PRIVATE_KEY env var for real trades
356
+ ```
357
+
358
+ ## Requirements
359
+
360
+ - Node.js >= 20.0.0
361
+ - TypeScript >= 5.0 (for development)
362
+
363
+ ## License
364
+
365
+ MIT