@alango/dr-manhattan 0.1.2 → 0.1.3
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/README.md +39 -1
- package/dist/index.d.ts +52 -1
- package/dist/index.js +361 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,7 @@ CCXT-style unified API for prediction markets in TypeScript.
|
|
|
15
15
|
| [Polymarket](https://polymarket.com) | ✅ | ✅ | Polygon |
|
|
16
16
|
| [Limitless](https://limitless.exchange) | ✅ | ✅ | Base |
|
|
17
17
|
| [Opinion](https://opinion.trade) | ✅ | ❌ | BNB |
|
|
18
|
+
| [Kalshi](https://kalshi.com) | ✅ | ❌ | - |
|
|
18
19
|
|
|
19
20
|
## Installation
|
|
20
21
|
|
|
@@ -32,7 +33,7 @@ yarn add @alango/dr-manhattan
|
|
|
32
33
|
import { createExchange, listExchanges, MarketUtils } from '@alango/dr-manhattan';
|
|
33
34
|
|
|
34
35
|
// List available exchanges
|
|
35
|
-
console.log(listExchanges()); // ['polymarket', 'limitless', 'opinion']
|
|
36
|
+
console.log(listExchanges()); // ['polymarket', 'limitless', 'opinion', 'kalshi']
|
|
36
37
|
|
|
37
38
|
// Create exchange instance (no auth required for public data)
|
|
38
39
|
const polymarket = createExchange('polymarket');
|
|
@@ -101,6 +102,43 @@ const opinion = new Opinion({
|
|
|
101
102
|
});
|
|
102
103
|
```
|
|
103
104
|
|
|
105
|
+
### Kalshi
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import { Kalshi } from '@alango/dr-manhattan';
|
|
109
|
+
|
|
110
|
+
// With RSA private key file
|
|
111
|
+
const kalshi = new Kalshi({
|
|
112
|
+
apiKeyId: process.env.KALSHI_API_KEY_ID,
|
|
113
|
+
privateKeyPath: '/path/to/kalshi_private_key.pem',
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Or with PEM content directly
|
|
117
|
+
const kalshi = new Kalshi({
|
|
118
|
+
apiKeyId: process.env.KALSHI_API_KEY_ID,
|
|
119
|
+
privateKeyPem: process.env.KALSHI_PRIVATE_KEY_PEM,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// Demo environment
|
|
123
|
+
const kalshiDemo = new Kalshi({
|
|
124
|
+
apiKeyId: process.env.KALSHI_API_KEY_ID,
|
|
125
|
+
privateKeyPath: '/path/to/private_key.pem',
|
|
126
|
+
demo: true,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Fetch markets (no auth required)
|
|
130
|
+
const markets = await kalshi.fetchMarkets({ limit: 10 });
|
|
131
|
+
|
|
132
|
+
// Create order (auth required)
|
|
133
|
+
const order = await kalshi.createOrder({
|
|
134
|
+
marketId: 'INXD-24DEC31-B5000',
|
|
135
|
+
outcome: 'Yes',
|
|
136
|
+
side: OrderSide.BUY,
|
|
137
|
+
price: 0.55,
|
|
138
|
+
size: 10,
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
104
142
|
## API Reference
|
|
105
143
|
|
|
106
144
|
### Exchange Methods
|
package/dist/index.d.ts
CHANGED
|
@@ -740,6 +740,57 @@ declare class Limitless extends Exchange {
|
|
|
740
740
|
};
|
|
741
741
|
}
|
|
742
742
|
|
|
743
|
+
interface KalshiConfig extends ExchangeConfig {
|
|
744
|
+
/** API key ID (the public key identifier) */
|
|
745
|
+
apiKeyId?: string;
|
|
746
|
+
/** Path to RSA private key PEM file */
|
|
747
|
+
privateKeyPath?: string;
|
|
748
|
+
/** RSA private key PEM content (alternative to path) */
|
|
749
|
+
privateKeyPem?: string;
|
|
750
|
+
/** Use demo environment */
|
|
751
|
+
demo?: boolean;
|
|
752
|
+
/** Custom API URL */
|
|
753
|
+
apiUrl?: string;
|
|
754
|
+
}
|
|
755
|
+
declare class Kalshi extends Exchange {
|
|
756
|
+
readonly id = "kalshi";
|
|
757
|
+
readonly name = "Kalshi";
|
|
758
|
+
private readonly apiUrl;
|
|
759
|
+
private readonly apiKeyId;
|
|
760
|
+
private auth;
|
|
761
|
+
constructor(config?: KalshiConfig);
|
|
762
|
+
private isAuthenticated;
|
|
763
|
+
private ensureAuth;
|
|
764
|
+
private request;
|
|
765
|
+
private parseMarket;
|
|
766
|
+
private parseOrder;
|
|
767
|
+
private parsePosition;
|
|
768
|
+
fetchMarkets(params?: FetchMarketsParams): Promise<Market[]>;
|
|
769
|
+
fetchMarket(marketId: string): Promise<Market>;
|
|
770
|
+
fetchOrderbook(ticker: string): Promise<Orderbook>;
|
|
771
|
+
createOrder(params: CreateOrderParams): Promise<Order>;
|
|
772
|
+
cancelOrder(orderId: string, _marketId?: string): Promise<Order>;
|
|
773
|
+
fetchOrder(orderId: string, _marketId?: string): Promise<Order>;
|
|
774
|
+
fetchOpenOrders(marketId?: string): Promise<Order[]>;
|
|
775
|
+
fetchPositions(marketId?: string): Promise<Position[]>;
|
|
776
|
+
fetchBalance(): Promise<Record<string, number>>;
|
|
777
|
+
describe(): {
|
|
778
|
+
id: string;
|
|
779
|
+
name: string;
|
|
780
|
+
has: {
|
|
781
|
+
fetchMarkets: boolean;
|
|
782
|
+
fetchMarket: boolean;
|
|
783
|
+
createOrder: boolean;
|
|
784
|
+
cancelOrder: boolean;
|
|
785
|
+
fetchOrder: boolean;
|
|
786
|
+
fetchOpenOrders: boolean;
|
|
787
|
+
fetchPositions: boolean;
|
|
788
|
+
fetchBalance: boolean;
|
|
789
|
+
websocket: boolean;
|
|
790
|
+
};
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
|
|
743
794
|
declare function listExchanges(): string[];
|
|
744
795
|
declare function createExchange(exchangeId: string, config?: ExchangeConfig): Exchange;
|
|
745
796
|
|
|
@@ -761,4 +812,4 @@ declare function clampPrice(price: number, min?: number, max?: number): number;
|
|
|
761
812
|
declare function formatPrice(price: number, decimals?: number): string;
|
|
762
813
|
declare function formatUsd(amount: number): string;
|
|
763
814
|
|
|
764
|
-
export { AuthenticationError, Colors, type CreateOrderParams, type DeltaInfo, DrManhattanError, Exchange, type ExchangeCapabilities, type ExchangeConfig, ExchangeError, type FetchMarketsParams, InsufficientFunds, InvalidOrder, Limitless, LimitlessWebSocket, type Market, MarketNotFound, MarketUtils, NetworkError, Opinion, type Order, OrderBookWebSocket, OrderSide, OrderStatus, OrderUtils, type Orderbook, type OrderbookCallback, OrderbookManager, type OrderbookUpdate, OrderbookUtils, type OutcomeToken, Polymarket, PolymarketWebSocket, type Position, PositionUtils, type PriceLevel, RateLimitError, Strategy, type StrategyConfig, StrategyState, type WebSocketConfig, WebSocketState, calculateDelta, clampPrice, createExchange, createLogger, formatPrice, formatUsd, listExchanges, logger, roundToTickSize };
|
|
815
|
+
export { AuthenticationError, Colors, type CreateOrderParams, type DeltaInfo, DrManhattanError, Exchange, type ExchangeCapabilities, type ExchangeConfig, ExchangeError, type FetchMarketsParams, InsufficientFunds, InvalidOrder, Kalshi, Limitless, LimitlessWebSocket, type Market, MarketNotFound, MarketUtils, NetworkError, Opinion, type Order, OrderBookWebSocket, OrderSide, OrderStatus, OrderUtils, type Orderbook, type OrderbookCallback, OrderbookManager, type OrderbookUpdate, OrderbookUtils, type OutcomeToken, Polymarket, PolymarketWebSocket, type Position, PositionUtils, type PriceLevel, RateLimitError, Strategy, type StrategyConfig, StrategyState, type WebSocketConfig, WebSocketState, calculateDelta, clampPrice, createExchange, createLogger, formatPrice, formatUsd, listExchanges, logger, roundToTickSize };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
2
|
import WebSocket from 'ws';
|
|
3
|
+
import * as crypto from 'crypto';
|
|
4
|
+
import * as fs from 'fs';
|
|
3
5
|
import { Wallet } from 'ethers';
|
|
4
6
|
import { io } from 'socket.io-client';
|
|
5
7
|
import { ClobClient, Side, AssetType } from '@polymarket/clob-client';
|
|
@@ -702,6 +704,353 @@ var Strategy = class extends EventEmitter {
|
|
|
702
704
|
return this.state;
|
|
703
705
|
}
|
|
704
706
|
};
|
|
707
|
+
var BASE_URL = "https://api.elections.kalshi.com/trade-api/v2";
|
|
708
|
+
var DEMO_URL = "https://demo-api.kalshi.co/trade-api/v2";
|
|
709
|
+
function createAuth(privateKeyPem) {
|
|
710
|
+
return {
|
|
711
|
+
sign(timestampMs, method, path) {
|
|
712
|
+
const message = `${timestampMs}${method.toUpperCase()}${path}`;
|
|
713
|
+
const sign = crypto.createSign("RSA-SHA256");
|
|
714
|
+
sign.update(message);
|
|
715
|
+
sign.end();
|
|
716
|
+
const signature = sign.sign(privateKeyPem, "base64");
|
|
717
|
+
return signature;
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
var Kalshi = class extends Exchange {
|
|
722
|
+
id = "kalshi";
|
|
723
|
+
name = "Kalshi";
|
|
724
|
+
apiUrl;
|
|
725
|
+
apiKeyId;
|
|
726
|
+
auth = null;
|
|
727
|
+
constructor(config = {}) {
|
|
728
|
+
super(config);
|
|
729
|
+
this.apiUrl = config.apiUrl ?? (config.demo ? DEMO_URL : BASE_URL);
|
|
730
|
+
this.apiKeyId = config.apiKeyId ?? null;
|
|
731
|
+
if (config.apiKeyId) {
|
|
732
|
+
if (config.privateKeyPath) {
|
|
733
|
+
const pem = fs.readFileSync(config.privateKeyPath, "utf-8");
|
|
734
|
+
this.auth = createAuth(pem);
|
|
735
|
+
} else if (config.privateKeyPem) {
|
|
736
|
+
this.auth = createAuth(config.privateKeyPem);
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
isAuthenticated() {
|
|
741
|
+
return this.apiKeyId !== null && this.auth !== null;
|
|
742
|
+
}
|
|
743
|
+
ensureAuth() {
|
|
744
|
+
if (!this.isAuthenticated()) {
|
|
745
|
+
throw new AuthenticationError("Kalshi requires apiKeyId and privateKey for this operation");
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
async request(method, path, body) {
|
|
749
|
+
const url = `${this.apiUrl}${path}`;
|
|
750
|
+
const headers = {
|
|
751
|
+
"Content-Type": "application/json",
|
|
752
|
+
Accept: "application/json"
|
|
753
|
+
};
|
|
754
|
+
if (this.isAuthenticated() && this.auth && this.apiKeyId) {
|
|
755
|
+
const timestampMs = Date.now();
|
|
756
|
+
const signature = this.auth.sign(timestampMs, method, path);
|
|
757
|
+
headers["KALSHI-ACCESS-KEY"] = this.apiKeyId;
|
|
758
|
+
headers["KALSHI-ACCESS-SIGNATURE"] = signature;
|
|
759
|
+
headers["KALSHI-ACCESS-TIMESTAMP"] = timestampMs.toString();
|
|
760
|
+
}
|
|
761
|
+
const fetchOptions = {
|
|
762
|
+
method,
|
|
763
|
+
headers,
|
|
764
|
+
signal: AbortSignal.timeout(this.timeout)
|
|
765
|
+
};
|
|
766
|
+
if (body && (method === "POST" || method === "PUT")) {
|
|
767
|
+
fetchOptions.body = JSON.stringify(body);
|
|
768
|
+
}
|
|
769
|
+
const response = await fetch(url, fetchOptions);
|
|
770
|
+
if (response.status === 429) {
|
|
771
|
+
throw new NetworkError("Rate limited");
|
|
772
|
+
}
|
|
773
|
+
if (response.status === 401 || response.status === 403) {
|
|
774
|
+
const msg = await response.text();
|
|
775
|
+
throw new AuthenticationError(`Authentication failed: ${msg}`);
|
|
776
|
+
}
|
|
777
|
+
if (response.status === 404) {
|
|
778
|
+
throw new ExchangeError(`Resource not found: ${path}`);
|
|
779
|
+
}
|
|
780
|
+
if (!response.ok) {
|
|
781
|
+
const msg = await response.text();
|
|
782
|
+
throw new NetworkError(`HTTP ${response.status}: ${msg}`);
|
|
783
|
+
}
|
|
784
|
+
return response.json();
|
|
785
|
+
}
|
|
786
|
+
parseMarket(data) {
|
|
787
|
+
const ticker = data.ticker;
|
|
788
|
+
if (!ticker) return null;
|
|
789
|
+
const question = data.title ?? "";
|
|
790
|
+
const outcomes = ["Yes", "No"];
|
|
791
|
+
const yesPrice = (data.yes_ask ?? data.yes_bid ?? data.last_price ?? 50) / 100;
|
|
792
|
+
const noPrice = 1 - yesPrice;
|
|
793
|
+
const prices = {
|
|
794
|
+
Yes: yesPrice,
|
|
795
|
+
No: noPrice
|
|
796
|
+
};
|
|
797
|
+
const volume = data.volume ?? 0;
|
|
798
|
+
const liquidity = data.open_interest ?? 0;
|
|
799
|
+
let closeTime;
|
|
800
|
+
const closeTimeStr = data.close_time ?? data.expiration_time;
|
|
801
|
+
if (closeTimeStr) {
|
|
802
|
+
const parsed = this.parseDateTime(closeTimeStr);
|
|
803
|
+
if (parsed) closeTime = parsed;
|
|
804
|
+
}
|
|
805
|
+
const description = data.subtitle ?? data.rules_primary ?? "";
|
|
806
|
+
const tickSize = 0.01;
|
|
807
|
+
const status = data.status ?? "";
|
|
808
|
+
const closed = status.toLowerCase() === "closed" || status.toLowerCase() === "settled" || data.result != null;
|
|
809
|
+
return {
|
|
810
|
+
id: ticker,
|
|
811
|
+
question,
|
|
812
|
+
outcomes,
|
|
813
|
+
closeTime,
|
|
814
|
+
volume,
|
|
815
|
+
liquidity,
|
|
816
|
+
prices,
|
|
817
|
+
tickSize,
|
|
818
|
+
description,
|
|
819
|
+
metadata: {
|
|
820
|
+
...data,
|
|
821
|
+
ticker,
|
|
822
|
+
eventTicker: data.event_ticker,
|
|
823
|
+
closed
|
|
824
|
+
}
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
parseOrder(data) {
|
|
828
|
+
const orderId = data.order_id ?? "";
|
|
829
|
+
const marketId = data.ticker ?? "";
|
|
830
|
+
const action = (data.action ?? "buy").toLowerCase();
|
|
831
|
+
const side = action === "buy" ? OrderSide.BUY : OrderSide.SELL;
|
|
832
|
+
const outcomeSide = (data.side ?? "yes").toLowerCase();
|
|
833
|
+
const outcome = outcomeSide === "yes" ? "Yes" : "No";
|
|
834
|
+
const statusStr = (data.status ?? "resting").toLowerCase();
|
|
835
|
+
let status;
|
|
836
|
+
switch (statusStr) {
|
|
837
|
+
case "resting":
|
|
838
|
+
case "active":
|
|
839
|
+
case "pending":
|
|
840
|
+
status = OrderStatus.OPEN;
|
|
841
|
+
break;
|
|
842
|
+
case "executed":
|
|
843
|
+
case "filled":
|
|
844
|
+
status = OrderStatus.FILLED;
|
|
845
|
+
break;
|
|
846
|
+
case "canceled":
|
|
847
|
+
case "cancelled":
|
|
848
|
+
status = OrderStatus.CANCELLED;
|
|
849
|
+
break;
|
|
850
|
+
case "partial":
|
|
851
|
+
status = OrderStatus.PARTIALLY_FILLED;
|
|
852
|
+
break;
|
|
853
|
+
default:
|
|
854
|
+
status = OrderStatus.OPEN;
|
|
855
|
+
}
|
|
856
|
+
const priceCents = data.yes_price ?? data.no_price ?? 0;
|
|
857
|
+
const price = priceCents / 100;
|
|
858
|
+
const size = data.count ?? data.remaining_count ?? 0;
|
|
859
|
+
const filled = data.filled_count ?? 0;
|
|
860
|
+
let createdAt = /* @__PURE__ */ new Date();
|
|
861
|
+
if (data.created_time) {
|
|
862
|
+
const parsed = this.parseDateTime(data.created_time);
|
|
863
|
+
if (parsed) createdAt = parsed;
|
|
864
|
+
}
|
|
865
|
+
let updatedAt;
|
|
866
|
+
if (data.updated_time) {
|
|
867
|
+
updatedAt = this.parseDateTime(data.updated_time);
|
|
868
|
+
}
|
|
869
|
+
return {
|
|
870
|
+
id: orderId,
|
|
871
|
+
marketId,
|
|
872
|
+
outcome,
|
|
873
|
+
side,
|
|
874
|
+
price,
|
|
875
|
+
size,
|
|
876
|
+
filled,
|
|
877
|
+
status,
|
|
878
|
+
createdAt,
|
|
879
|
+
updatedAt
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
parsePosition(data) {
|
|
883
|
+
const marketId = data.ticker ?? "";
|
|
884
|
+
const positionValue = data.position ?? 0;
|
|
885
|
+
const outcome = positionValue >= 0 ? "Yes" : "No";
|
|
886
|
+
const size = Math.abs(positionValue);
|
|
887
|
+
const averagePrice = 0;
|
|
888
|
+
const currentPrice = 0;
|
|
889
|
+
return {
|
|
890
|
+
marketId,
|
|
891
|
+
outcome,
|
|
892
|
+
size,
|
|
893
|
+
averagePrice,
|
|
894
|
+
currentPrice
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
async fetchMarkets(params) {
|
|
898
|
+
return this.withRetry(async () => {
|
|
899
|
+
const limit = params?.limit ?? 100;
|
|
900
|
+
let endpoint = `/markets?limit=${Math.min(limit, 200)}`;
|
|
901
|
+
if (params?.active !== false) {
|
|
902
|
+
endpoint += "&status=open";
|
|
903
|
+
}
|
|
904
|
+
const response = await this.request("GET", endpoint);
|
|
905
|
+
const markets = response.markets ?? [];
|
|
906
|
+
return markets.map((m) => this.parseMarket(m)).filter((m) => m !== null);
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
async fetchMarket(marketId) {
|
|
910
|
+
return this.withRetry(async () => {
|
|
911
|
+
try {
|
|
912
|
+
const response = await this.request("GET", `/markets/${marketId}`);
|
|
913
|
+
const market = this.parseMarket(response.market);
|
|
914
|
+
if (!market) {
|
|
915
|
+
throw new MarketNotFound(`Market ${marketId} not found`);
|
|
916
|
+
}
|
|
917
|
+
return market;
|
|
918
|
+
} catch (error) {
|
|
919
|
+
if (error instanceof ExchangeError && error.message.includes("not found")) {
|
|
920
|
+
throw new MarketNotFound(`Market ${marketId} not found`);
|
|
921
|
+
}
|
|
922
|
+
throw error;
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
async fetchOrderbook(ticker) {
|
|
927
|
+
this.ensureAuth();
|
|
928
|
+
return this.withRetry(async () => {
|
|
929
|
+
const response = await this.request("GET", `/markets/${ticker}/orderbook`);
|
|
930
|
+
const bids = [];
|
|
931
|
+
const asks = [];
|
|
932
|
+
if (response.orderbook.yes) {
|
|
933
|
+
for (const [priceCents, size] of response.orderbook.yes) {
|
|
934
|
+
const price = priceCents / 100;
|
|
935
|
+
bids.push([price, size]);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
if (response.orderbook.no) {
|
|
939
|
+
for (const [priceCents, size] of response.orderbook.no) {
|
|
940
|
+
const price = 1 - priceCents / 100;
|
|
941
|
+
asks.push([price, size]);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
bids.sort((a, b) => b[0] - a[0]);
|
|
945
|
+
asks.sort((a, b) => a[0] - b[0]);
|
|
946
|
+
return {
|
|
947
|
+
bids,
|
|
948
|
+
asks,
|
|
949
|
+
timestamp: Date.now(),
|
|
950
|
+
assetId: ticker,
|
|
951
|
+
marketId: ticker
|
|
952
|
+
};
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
async createOrder(params) {
|
|
956
|
+
this.ensureAuth();
|
|
957
|
+
if (params.price <= 0 || params.price >= 1) {
|
|
958
|
+
throw new InvalidOrder("Price must be between 0 and 1");
|
|
959
|
+
}
|
|
960
|
+
const outcome = params.outcome.toLowerCase();
|
|
961
|
+
if (outcome !== "yes" && outcome !== "no") {
|
|
962
|
+
throw new InvalidOrder("Outcome must be 'Yes' or 'No'");
|
|
963
|
+
}
|
|
964
|
+
return this.withRetry(async () => {
|
|
965
|
+
const action = params.side === OrderSide.BUY ? "buy" : "sell";
|
|
966
|
+
const side = outcome;
|
|
967
|
+
const priceCents = Math.round(params.price * 100);
|
|
968
|
+
const body = {
|
|
969
|
+
ticker: params.marketId,
|
|
970
|
+
action,
|
|
971
|
+
side,
|
|
972
|
+
type: "limit",
|
|
973
|
+
count: Math.floor(params.size)
|
|
974
|
+
};
|
|
975
|
+
if (outcome === "yes") {
|
|
976
|
+
body.yes_price = priceCents;
|
|
977
|
+
} else {
|
|
978
|
+
body.no_price = priceCents;
|
|
979
|
+
}
|
|
980
|
+
const response = await this.request(
|
|
981
|
+
"POST",
|
|
982
|
+
"/portfolio/orders",
|
|
983
|
+
body
|
|
984
|
+
);
|
|
985
|
+
return this.parseOrder(response.order);
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
async cancelOrder(orderId, _marketId) {
|
|
989
|
+
this.ensureAuth();
|
|
990
|
+
return this.withRetry(async () => {
|
|
991
|
+
const response = await this.request(
|
|
992
|
+
"DELETE",
|
|
993
|
+
`/portfolio/orders/${orderId}`
|
|
994
|
+
);
|
|
995
|
+
return this.parseOrder(response.order);
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
async fetchOrder(orderId, _marketId) {
|
|
999
|
+
this.ensureAuth();
|
|
1000
|
+
return this.withRetry(async () => {
|
|
1001
|
+
const response = await this.request("GET", `/portfolio/orders/${orderId}`);
|
|
1002
|
+
return this.parseOrder(response.order);
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
async fetchOpenOrders(marketId) {
|
|
1006
|
+
this.ensureAuth();
|
|
1007
|
+
return this.withRetry(async () => {
|
|
1008
|
+
let endpoint = "/portfolio/orders?status=resting";
|
|
1009
|
+
if (marketId) {
|
|
1010
|
+
endpoint += `&ticker=${marketId}`;
|
|
1011
|
+
}
|
|
1012
|
+
const response = await this.request("GET", endpoint);
|
|
1013
|
+
return (response.orders ?? []).map((o) => this.parseOrder(o));
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
async fetchPositions(marketId) {
|
|
1017
|
+
this.ensureAuth();
|
|
1018
|
+
return this.withRetry(async () => {
|
|
1019
|
+
let endpoint = "/portfolio/positions";
|
|
1020
|
+
if (marketId) {
|
|
1021
|
+
endpoint += `?ticker=${marketId}`;
|
|
1022
|
+
}
|
|
1023
|
+
const response = await this.request("GET", endpoint);
|
|
1024
|
+
const positions = (response.market_positions ?? []).map((p) => this.parsePosition(p)).filter((p) => p.size > 0);
|
|
1025
|
+
return positions;
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
async fetchBalance() {
|
|
1029
|
+
this.ensureAuth();
|
|
1030
|
+
return this.withRetry(async () => {
|
|
1031
|
+
const response = await this.request("GET", "/portfolio/balance");
|
|
1032
|
+
const balance = (response.available_balance ?? response.balance ?? 0) / 100;
|
|
1033
|
+
return { USD: balance };
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
describe() {
|
|
1037
|
+
return {
|
|
1038
|
+
id: this.id,
|
|
1039
|
+
name: this.name,
|
|
1040
|
+
has: {
|
|
1041
|
+
fetchMarkets: true,
|
|
1042
|
+
fetchMarket: true,
|
|
1043
|
+
createOrder: true,
|
|
1044
|
+
cancelOrder: true,
|
|
1045
|
+
fetchOrder: true,
|
|
1046
|
+
fetchOpenOrders: true,
|
|
1047
|
+
fetchPositions: true,
|
|
1048
|
+
fetchBalance: true,
|
|
1049
|
+
websocket: false
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
705
1054
|
var WS_URL = "wss://ws.limitless.exchange";
|
|
706
1055
|
var NAMESPACE = "/markets";
|
|
707
1056
|
var LimitlessWebSocket = class extends EventEmitter {
|
|
@@ -1045,7 +1394,7 @@ var LimitlessWebSocket = class extends EventEmitter {
|
|
|
1045
1394
|
};
|
|
1046
1395
|
|
|
1047
1396
|
// src/exchanges/limitless/index.ts
|
|
1048
|
-
var
|
|
1397
|
+
var BASE_URL2 = "https://api.limitless.exchange";
|
|
1049
1398
|
var CHAIN_ID = 8453;
|
|
1050
1399
|
var Limitless = class extends Exchange {
|
|
1051
1400
|
id = "limitless";
|
|
@@ -1061,7 +1410,7 @@ var Limitless = class extends Exchange {
|
|
|
1061
1410
|
noTokens = /* @__PURE__ */ new Set();
|
|
1062
1411
|
constructor(config = {}) {
|
|
1063
1412
|
super(config);
|
|
1064
|
-
this.host = config.host ??
|
|
1413
|
+
this.host = config.host ?? BASE_URL2;
|
|
1065
1414
|
this.chainId = config.chainId ?? CHAIN_ID;
|
|
1066
1415
|
if (config.privateKey) {
|
|
1067
1416
|
this.wallet = new Wallet(config.privateKey);
|
|
@@ -1725,7 +2074,7 @@ var Limitless = class extends Exchange {
|
|
|
1725
2074
|
};
|
|
1726
2075
|
}
|
|
1727
2076
|
};
|
|
1728
|
-
var
|
|
2077
|
+
var BASE_URL3 = "https://proxy.opinion.trade:8443";
|
|
1729
2078
|
var CHAIN_ID2 = 56;
|
|
1730
2079
|
var Opinion = class extends Exchange {
|
|
1731
2080
|
id = "opinion";
|
|
@@ -1740,7 +2089,7 @@ var Opinion = class extends Exchange {
|
|
|
1740
2089
|
this.apiKey = config.apiKey ?? "";
|
|
1741
2090
|
this.multiSigAddr = config.multiSigAddr ?? "";
|
|
1742
2091
|
this.chainId = config.chainId ?? CHAIN_ID2;
|
|
1743
|
-
this.host = config.host ??
|
|
2092
|
+
this.host = config.host ?? BASE_URL3;
|
|
1744
2093
|
if (config.privateKey) {
|
|
1745
2094
|
this.wallet = new Wallet(config.privateKey);
|
|
1746
2095
|
}
|
|
@@ -2112,7 +2461,7 @@ var Opinion = class extends Exchange {
|
|
|
2112
2461
|
};
|
|
2113
2462
|
}
|
|
2114
2463
|
};
|
|
2115
|
-
var
|
|
2464
|
+
var BASE_URL4 = "https://gamma-api.polymarket.com";
|
|
2116
2465
|
var CLOB_URL = "https://clob.polymarket.com";
|
|
2117
2466
|
var Polymarket = class extends Exchange {
|
|
2118
2467
|
id = "polymarket";
|
|
@@ -2173,7 +2522,7 @@ var Polymarket = class extends Exchange {
|
|
|
2173
2522
|
}
|
|
2174
2523
|
async fetchMarket(marketId) {
|
|
2175
2524
|
return this.withRetry(async () => {
|
|
2176
|
-
const response = await fetch(`${
|
|
2525
|
+
const response = await fetch(`${BASE_URL4}/markets/${marketId}`, {
|
|
2177
2526
|
signal: AbortSignal.timeout(this.timeout)
|
|
2178
2527
|
});
|
|
2179
2528
|
if (response.status === 404) {
|
|
@@ -2190,7 +2539,7 @@ var Polymarket = class extends Exchange {
|
|
|
2190
2539
|
const slug = this.parseMarketIdentifier(slugOrUrl);
|
|
2191
2540
|
if (!slug) throw new Error("Empty slug provided");
|
|
2192
2541
|
return this.withRetry(async () => {
|
|
2193
|
-
const response = await fetch(`${
|
|
2542
|
+
const response = await fetch(`${BASE_URL4}/events?slug=${slug}`, {
|
|
2194
2543
|
signal: AbortSignal.timeout(this.timeout)
|
|
2195
2544
|
});
|
|
2196
2545
|
if (response.status === 404) {
|
|
@@ -2566,7 +2915,7 @@ var Polymarket = class extends Exchange {
|
|
|
2566
2915
|
closed: String(closed)
|
|
2567
2916
|
});
|
|
2568
2917
|
if (tagId) params.set("tag_id", tagId);
|
|
2569
|
-
const response = await fetch(`${
|
|
2918
|
+
const response = await fetch(`${BASE_URL4}/markets?${params}`, {
|
|
2570
2919
|
signal: AbortSignal.timeout(this.timeout)
|
|
2571
2920
|
});
|
|
2572
2921
|
if (!response.ok) {
|
|
@@ -2648,7 +2997,7 @@ var Polymarket = class extends Exchange {
|
|
|
2648
2997
|
async getTagBySlug(slug) {
|
|
2649
2998
|
if (!slug) throw new Error("slug must be a non-empty string");
|
|
2650
2999
|
return this.withRetry(async () => {
|
|
2651
|
-
const response = await fetch(`${
|
|
3000
|
+
const response = await fetch(`${BASE_URL4}/tags/slug/${slug}`, {
|
|
2652
3001
|
signal: AbortSignal.timeout(this.timeout)
|
|
2653
3002
|
});
|
|
2654
3003
|
if (!response.ok) {
|
|
@@ -2823,7 +3172,8 @@ var PolymarketWebSocket = class extends OrderBookWebSocket {
|
|
|
2823
3172
|
var exchanges = {
|
|
2824
3173
|
polymarket: Polymarket,
|
|
2825
3174
|
opinion: Opinion,
|
|
2826
|
-
limitless: Limitless
|
|
3175
|
+
limitless: Limitless,
|
|
3176
|
+
kalshi: Kalshi
|
|
2827
3177
|
};
|
|
2828
3178
|
function listExchanges() {
|
|
2829
3179
|
return Object.keys(exchanges);
|
|
@@ -2873,6 +3223,6 @@ function formatUsd(amount) {
|
|
|
2873
3223
|
return `$${amount.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
|
|
2874
3224
|
}
|
|
2875
3225
|
|
|
2876
|
-
export { AuthenticationError, Colors, DrManhattanError, Exchange, ExchangeError, InsufficientFunds, InvalidOrder, Limitless, LimitlessWebSocket, MarketNotFound, MarketUtils, NetworkError, Opinion, OrderBookWebSocket, OrderSide, OrderStatus, OrderUtils, OrderbookManager, OrderbookUtils, Polymarket, PolymarketWebSocket, PositionUtils, RateLimitError, Strategy, StrategyState, WebSocketState, calculateDelta, clampPrice, createExchange, createLogger, formatPrice, formatUsd, listExchanges, logger, roundToTickSize };
|
|
3226
|
+
export { AuthenticationError, Colors, DrManhattanError, Exchange, ExchangeError, InsufficientFunds, InvalidOrder, Kalshi, Limitless, LimitlessWebSocket, MarketNotFound, MarketUtils, NetworkError, Opinion, OrderBookWebSocket, OrderSide, OrderStatus, OrderUtils, OrderbookManager, OrderbookUtils, Polymarket, PolymarketWebSocket, PositionUtils, RateLimitError, Strategy, StrategyState, WebSocketState, calculateDelta, clampPrice, createExchange, createLogger, formatPrice, formatUsd, listExchanges, logger, roundToTickSize };
|
|
2877
3227
|
//# sourceMappingURL=index.js.map
|
|
2878
3228
|
//# sourceMappingURL=index.js.map
|