@jsnw/kalshi-client 0.0.1 → 0.2.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/dist/index.mjs CHANGED
@@ -3,6 +3,7 @@ import { z } from "zod";
3
3
  import { v4 } from "uuid";
4
4
  import { WebSocket } from "ws";
5
5
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
6
+ import { ApiClient as ApiClient$1, createRouter } from "@jsnw/api-client";
6
7
  //#region src/request-signer.ts
7
8
  var RequestSigner = class {
8
9
  privkey;
@@ -77,8 +78,39 @@ const primitives$fixedPointSchema = z.string().regex(/^\d{1,14}\.\d{2,8}$/);
77
78
  const primitives$orderSideSchema = z.enum(["yes", "no"]);
78
79
  const primitives$bookSideSchema = z.enum(["bid", "ask"]);
79
80
  const primitives$orderActionSchema = z.enum(["buy", "sell"]);
81
+ const primitives$rateLimitBucketMetaSchema = z.object({
82
+ refill_rate: z.number(),
83
+ bucket_capacity: z.number()
84
+ });
85
+ //#endregion
86
+ //#region src/schemas/http.ts
87
+ const http$errorMessageSchema = z.object({
88
+ code: z.number(),
89
+ message: z.string().optional(),
90
+ details: z.any().optional(),
91
+ service: z.any().optional()
92
+ });
93
+ //#endregion
94
+ //#region src/schemas/account.ts
95
+ const account$accountTierSchema = z.enum([
96
+ "basic",
97
+ "advanced",
98
+ "expert",
99
+ "premier",
100
+ "paragon",
101
+ "prime",
102
+ "prestige"
103
+ ]);
104
+ const account$subaccountSchema = z.number().min(0).max(63).default(0);
105
+ const account$accountGrantSchema = z.object({
106
+ exchange_instance: z.enum(["event_contract", "margined"]),
107
+ level: account$accountTierSchema,
108
+ source: z.enum(["volume", "manual"]),
109
+ expires_ts: z.number().nullable().default(null)
110
+ });
80
111
  //#endregion
81
112
  //#region src/schemas/orders.ts
113
+ const orders$clientOrderIdSchema = z.string().nullable().optional();
82
114
  const orders$orderStatusSchema = z.enum([
83
115
  "resting",
84
116
  "canceled",
@@ -86,17 +118,22 @@ const orders$orderStatusSchema = z.enum([
86
118
  ]);
87
119
  const orders$orderTypeSchema = z.enum(["limit", "market"]);
88
120
  const orders$selfTradePreventionTypeSchema = z.enum(["taker_at_cross", "maker"]);
121
+ const orders$timeInForceSchema = z.enum([
122
+ "fill_or_kill",
123
+ "good_till_canceled",
124
+ "immediate_or_cancel"
125
+ ]);
89
126
  const orders$orderDetailedSchema = z.object({
90
127
  order_id: z.uuid(),
91
128
  user_id: z.uuid(),
92
- subaccount_number: z.number().min(0).max(32).default(0),
93
- order_group_id: z.uuid().nullable().optional(),
129
+ client_order_id: orders$clientOrderIdSchema,
130
+ order_group_id: z.string().nullable().optional(),
131
+ subaccount_number: account$subaccountSchema,
94
132
  exchange_index: z.number().min(0).default(0),
95
- client_order_id: z.string(),
96
133
  ticker: z.string(),
134
+ type: orders$orderTypeSchema,
97
135
  outcome_side: primitives$orderSideSchema,
98
136
  book_side: primitives$bookSideSchema,
99
- type: orders$orderTypeSchema,
100
137
  status: orders$orderStatusSchema,
101
138
  yes_price_dollars: primitives$fixedPointSchema,
102
139
  no_price_dollars: primitives$fixedPointSchema,
@@ -107,11 +144,11 @@ const orders$orderDetailedSchema = z.object({
107
144
  maker_fill_cost_dollars: primitives$fixedPointSchema,
108
145
  taker_fees_dollars: primitives$fixedPointSchema,
109
146
  maker_fees_dollars: primitives$fixedPointSchema,
110
- self_trade_prevention_type: orders$selfTradePreventionTypeSchema.nullable().optional(),
147
+ self_trade_prevention_type: orders$selfTradePreventionTypeSchema,
111
148
  cancel_order_on_pause: z.boolean(),
112
149
  created_time: z.iso.datetime(),
113
- expiration_time: z.iso.datetime().nullable().optional(),
114
- last_update_time: z.iso.datetime().nullable().optional()
150
+ last_update_time: z.iso.datetime().nullable().optional(),
151
+ expiration_time: z.iso.datetime().nullable().optional()
115
152
  });
116
153
  const orders$orderWsSchema = orders$orderDetailedSchema.omit({
117
154
  exchange_index: true,
@@ -124,12 +161,32 @@ const orders$orderWsSchema = orders$orderDetailedSchema.omit({
124
161
  last_updated_ts_ms: z.number().positive().optional(),
125
162
  expiration_ts_ms: z.number().positive().optional()
126
163
  });
164
+ const orders$createOrderDtoSchema = z.object({
165
+ client_order_id: z.string().optional(),
166
+ order_group_id: z.string().optional(),
167
+ exchange_index: z.number().min(0).default(0),
168
+ ticker: z.string(),
169
+ side: primitives$bookSideSchema,
170
+ count: primitives$fixedPointSchema,
171
+ subaccount: account$subaccountSchema,
172
+ price: primitives$fixedPointSchema,
173
+ post_only: z.boolean().default(false),
174
+ reduce_only: z.boolean().default(false),
175
+ cancel_order_on_pause: z.boolean(),
176
+ self_trade_prevention_type: orders$selfTradePreventionTypeSchema,
177
+ time_in_force: orders$timeInForceSchema,
178
+ expiration_time: z.number().optional()
179
+ });
127
180
  //#endregion
128
181
  //#region src/consts.ts
129
182
  const KALSHI_WS_URL = {
130
183
  "development": "wss://external-api-ws.demo.kalshi.co",
131
184
  "production": "wss://external-api-ws.kalshi.com"
132
185
  };
186
+ const KALSHI_API_URL = {
187
+ "development": "https://external-api.demo.kalshi.co",
188
+ "production": "https://external-api.kalshi.com"
189
+ };
133
190
  //#endregion
134
191
  //#region src/typed-emitter.ts
135
192
  var TypedEmitter = class {
@@ -501,6 +558,8 @@ var WsKalshiError = class extends Error {
501
558
  this.code = code;
502
559
  }
503
560
  };
561
+ var WsClosedError = class extends Error {};
562
+ var WsClosedByUserError = class extends Error {};
504
563
  //#endregion
505
564
  //#region src/ws/ws-client.ts
506
565
  var WsClient = class extends TypedEmitter {
@@ -545,9 +604,39 @@ var WsClient = class extends TypedEmitter {
545
604
  this.ws.on("reconnecting", this.onReconnecting);
546
605
  this.ws.on("close", this.onClose);
547
606
  }
607
+ /**
608
+ * @return {Promise<void>}
609
+ */
610
+ connect() {
611
+ return this.ws.open();
612
+ }
613
+ /**
614
+ * @return {Promise<void>}
615
+ */
616
+ async subscribe(options) {
617
+ const { channel, timeoutMs, ...rest } = options;
618
+ const messageId = ++this.lastMessageId;
619
+ const payload = {
620
+ id: messageId,
621
+ cmd: "subscribe",
622
+ params: {
623
+ channels: [channel],
624
+ ...rest
625
+ }
626
+ };
627
+ const promise = this.subscribeAwaiters.add(messageId, timeoutMs ?? 2e3);
628
+ await this.ws.send(JSON.stringify(payload));
629
+ return promise;
630
+ }
631
+ /**
632
+ * @return {Promise<void>}
633
+ */
634
+ close() {
635
+ this.subscribeAwaiters.clear(new WsClosedByUserError());
636
+ return this.ws.close();
637
+ }
548
638
  onReady = async (afterReconnect) => {
549
- this.log("debug", "WebSocket connection ready", { afterReconnect });
550
- this.emit("ready");
639
+ this.emit("ready", afterReconnect);
551
640
  };
552
641
  /**
553
642
  * @param {WebSocket.RawData} data
@@ -581,21 +670,19 @@ var WsClient = class extends TypedEmitter {
581
670
  * @param {number} attempt
582
671
  */
583
672
  onReconnecting = (attempt) => {
584
- this.log("debug", "reconnecting", { attempt });
585
673
  this.emit("reconnect", attempt);
586
674
  };
587
675
  /**
588
676
  * @param {Error} err
589
677
  */
590
678
  onError = (err) => {
591
- this.log("error", "WebSocket error", { err });
679
+ this.emit("ws_error", err);
592
680
  };
593
681
  /**
594
682
  */
595
683
  onClose = () => {
596
- this.log("debug", "WebSocket connection closed");
597
684
  this.emit("close");
598
- this.subscribeAwaiters.clear();
685
+ this.subscribeAwaiters.clear(new WsClosedError());
599
686
  };
600
687
  /**
601
688
  * @param {WsAnyMessage} msg
@@ -649,4 +736,155 @@ const WS_KALSHI_SERVER_ERRORS = [
649
736
  WS_KALSHI_ERRORS.COMMAND_TIMEOUT
650
737
  ];
651
738
  //#endregion
652
- export { InMemoryCredentialsProvider, RequestSigner, WS_KALSHI_ERRORS, WS_KALSHI_SERVER_ERRORS, WsClient, WsKalshiError, orders$orderDetailedSchema, orders$orderStatusSchema, orders$orderTypeSchema, orders$orderWsSchema, orders$selfTradePreventionTypeSchema, primitives$bookSideSchema, primitives$fixedPointSchema, primitives$orderActionSchema, primitives$orderSideSchema, wsAnyMessageSchema, wsErrorMessageSchema, wsOkMessageSchema, wsSubscribedMessageSchema, wsUserOrderMessageSchema };
739
+ //#region src/api/metadata.ts
740
+ const METADATA = { NO_AUTH: Symbol("no-auth") };
741
+ //#endregion
742
+ //#region src/api/router/index.ts
743
+ const apiRouter = createRouter({
744
+ exchange: createRouter({
745
+ getStatus: {
746
+ method: "get",
747
+ path: "/status",
748
+ responses: { 200: z.object({
749
+ exchange_active: z.boolean(),
750
+ trading_active: z.boolean(),
751
+ exchange_estimated_resume_time: z.iso.datetime().nullable().optional()
752
+ }) }
753
+ },
754
+ getUserDataTimestamp: {
755
+ method: "get",
756
+ path: "/user_data_timestamp",
757
+ responses: { 200: z.object({ as_of_time: z.iso.datetime() }) }
758
+ }
759
+ }, {
760
+ path: "exchange",
761
+ metadata: { [METADATA.NO_AUTH]: true }
762
+ }),
763
+ account: createRouter({
764
+ getLimits: {
765
+ method: "get",
766
+ path: "/limits",
767
+ responses: { 200: z.object({
768
+ usage_tier: account$accountTierSchema,
769
+ read: primitives$rateLimitBucketMetaSchema,
770
+ write: primitives$rateLimitBucketMetaSchema,
771
+ grants: z.array(account$accountGrantSchema)
772
+ }) }
773
+ },
774
+ listNonDefaultEndpointCosts: {
775
+ method: "get",
776
+ path: "/endpoint_costs",
777
+ responses: { 200: z.object({
778
+ default_cost: z.number().min(0),
779
+ endpoint_costs: z.array(z.object({
780
+ method: z.string(),
781
+ path: z.string(),
782
+ cost: z.number().min(0)
783
+ }))
784
+ }) },
785
+ metadata: { [METADATA.NO_AUTH]: true }
786
+ }
787
+ }, { path: "/account" }),
788
+ orders: createRouter({
789
+ getOrder: {
790
+ method: "get",
791
+ path: "/orders/:order_id",
792
+ params: z.object({ order_id: z.uuid() }),
793
+ responses: { 200: z.object({ order: orders$orderDetailedSchema }) }
794
+ },
795
+ createOrder: {
796
+ method: "post",
797
+ path: "/events/orders",
798
+ body: orders$createOrderDtoSchema,
799
+ responses: { 201: z.object({
800
+ order_id: z.uuid(),
801
+ client_order_id: orders$clientOrderIdSchema,
802
+ fill_count: primitives$fixedPointSchema,
803
+ remaining_count: primitives$fixedPointSchema,
804
+ average_fill_price: primitives$fixedPointSchema.nullable().optional(),
805
+ average_fee_paid: primitives$fixedPointSchema.nullable().optional(),
806
+ ts_ms: z.number().min(0)
807
+ }) }
808
+ },
809
+ cancelOrderV1: {
810
+ method: "delete",
811
+ path: "/orders/:order_id",
812
+ params: z.object({ order_id: z.uuid() }),
813
+ query: z.object({
814
+ subaccount: z.number().min(0).max(63).default(0),
815
+ exchange_index: z.number().min(0).default(0)
816
+ }).prefault({}),
817
+ responses: { 200: z.object({
818
+ order: orders$orderDetailedSchema,
819
+ reduced_by_fp: primitives$fixedPointSchema
820
+ }) }
821
+ },
822
+ cancelOrderV2: {
823
+ method: "delete",
824
+ path: "/events/orders/:order_id",
825
+ params: z.object({ order_id: z.uuid() }),
826
+ query: z.object({
827
+ subaccount: z.number().min(0).max(63).default(0),
828
+ exchange_index: z.number().min(0).default(0)
829
+ }).prefault({}),
830
+ responses: { 200: z.object({
831
+ order_id: z.uuid(),
832
+ client_order_id: orders$clientOrderIdSchema,
833
+ reduced_by: primitives$fixedPointSchema,
834
+ ts_ms: z.number().min(0)
835
+ }) }
836
+ },
837
+ getOrderQueuePosition: {
838
+ method: "get",
839
+ path: "/orders/:order_id/queue_position",
840
+ params: z.object({ order_id: z.uuid() }),
841
+ responses: { 200: z.object({ queue_position_fp: primitives$fixedPointSchema }) }
842
+ }
843
+ }, { path: "/portfolio" })
844
+ }, { path: "/trade-api/v2" });
845
+ //#endregion
846
+ //#region src/api/client.ts
847
+ var ApiClient = class extends ApiClient$1 {
848
+ /**
849
+ * @param {ApiClientConfig} config
850
+ */
851
+ constructor(config) {
852
+ super(apiRouter, {
853
+ ...config,
854
+ baseURL: KALSHI_API_URL[config.environment ?? "development"],
855
+ qsArrayFormat: "brackets",
856
+ contentType: "application/json"
857
+ });
858
+ }
859
+ /**
860
+ * @template {Route} TRoute
861
+ * @param {TRoute} route
862
+ * @param {ApiRequestParams<TRoute>} params
863
+ * @param {ApiCallOptions} options
864
+ * @return {Promise<ApiResponse<TRoute>>}
865
+ */
866
+ /**
867
+ * @param {"force"} force
868
+ * @return {Promise<void>}
869
+ * @private
870
+ */
871
+ /**
872
+ * @param {ApiRawRequest} req
873
+ * @return {ApiRawRequest}
874
+ * @protected
875
+ */
876
+ onBeforeRequest(req) {
877
+ if (req.route.metadata?.[METADATA.NO_AUTH] && req.route.metadata[METADATA.NO_AUTH] === true) return req;
878
+ const ts = Date.now();
879
+ req.headers["kalshi-access-key"] = this.config.credentialsProvider.getAccessKey();
880
+ req.headers["kalshi-access-timestamp"] = ts.toString();
881
+ req.headers["kalshi-access-signature"] = this.config.credentialsProvider.getSignature({
882
+ httpMethod: req.route.method,
883
+ path: req.path,
884
+ timestamp: ts
885
+ });
886
+ return req;
887
+ }
888
+ };
889
+ //#endregion
890
+ export { ApiClient, InMemoryCredentialsProvider, RequestSigner, WS_KALSHI_ERRORS, WS_KALSHI_SERVER_ERRORS, WsClient, WsClosedByUserError, WsClosedError, WsKalshiError, account$accountGrantSchema, account$accountTierSchema, account$subaccountSchema, http$errorMessageSchema, orders$clientOrderIdSchema, orders$createOrderDtoSchema, orders$orderDetailedSchema, orders$orderStatusSchema, orders$orderTypeSchema, orders$orderWsSchema, orders$selfTradePreventionTypeSchema, orders$timeInForceSchema, primitives$bookSideSchema, primitives$fixedPointSchema, primitives$orderActionSchema, primitives$orderSideSchema, primitives$rateLimitBucketMetaSchema, wsAnyMessageSchema, wsErrorMessageSchema, wsOkMessageSchema, wsSubscribedMessageSchema, wsUserOrderMessageSchema };
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.0.1",
7
+ "version": "0.2.0",
8
8
  "engines": {
9
9
  "node": ">=22"
10
10
  },
@@ -24,6 +24,8 @@
24
24
  "dist"
25
25
  ],
26
26
  "dependencies": {
27
+ "@jsnw/api-client": "^0.7.1",
28
+ "@ts-rest/core": "3.53.0-rc.1",
27
29
  "uuid": "^14.0.0",
28
30
  "ws": "^8.21.0",
29
31
  "zod": "^4.4.3"
@@ -32,12 +34,11 @@
32
34
  "bufferutil": "^4.1.0"
33
35
  },
34
36
  "devDependencies": {
35
- "@types/node": "^24.12.4",
37
+ "@types/node": "^24.13.1",
36
38
  "@types/ws": "^8.18.1",
37
39
  "nodemon": "^3.1.14",
38
- "rimraf": "^6.1.3",
39
40
  "ts-node": "^10.9.2",
40
- "tsdown": "^0.22.1",
41
+ "tsdown": "^0.22.2",
41
42
  "typescript": "^5.9.3"
42
43
  },
43
44
  "scripts": {