@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 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 BASE_URL = "https://api.limitless.exchange";
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 ?? BASE_URL;
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 BASE_URL2 = "https://proxy.opinion.trade:8443";
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 ?? BASE_URL2;
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 BASE_URL3 = "https://gamma-api.polymarket.com";
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(`${BASE_URL3}/markets/${marketId}`, {
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(`${BASE_URL3}/events?slug=${slug}`, {
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(`${BASE_URL3}/markets?${params}`, {
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(`${BASE_URL3}/tags/slug/${slug}`, {
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