@newyorkcompute/kalshi-core 0.1.0 → 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.
Files changed (45) hide show
  1. package/dist/cache.d.ts +71 -0
  2. package/dist/cache.d.ts.map +1 -0
  3. package/dist/cache.js +96 -0
  4. package/dist/cache.js.map +1 -0
  5. package/dist/cache.test.d.ts +5 -0
  6. package/dist/cache.test.d.ts.map +1 -0
  7. package/dist/cache.test.js +108 -0
  8. package/dist/cache.test.js.map +1 -0
  9. package/dist/format.d.ts +39 -0
  10. package/dist/format.d.ts.map +1 -1
  11. package/dist/format.js +80 -0
  12. package/dist/format.js.map +1 -1
  13. package/dist/format.test.js +66 -2
  14. package/dist/format.test.js.map +1 -1
  15. package/dist/index.d.ts +5 -1
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +9 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/rate-limiter.d.ts +80 -0
  20. package/dist/rate-limiter.d.ts.map +1 -0
  21. package/dist/rate-limiter.js +180 -0
  22. package/dist/rate-limiter.js.map +1 -0
  23. package/dist/rate-limiter.test.d.ts +5 -0
  24. package/dist/rate-limiter.test.d.ts.map +1 -0
  25. package/dist/rate-limiter.test.js +175 -0
  26. package/dist/rate-limiter.test.js.map +1 -0
  27. package/dist/types.d.ts +1 -0
  28. package/dist/types.d.ts.map +1 -1
  29. package/dist/validate-order.d.ts +32 -0
  30. package/dist/validate-order.d.ts.map +1 -0
  31. package/dist/validate-order.js +82 -0
  32. package/dist/validate-order.js.map +1 -0
  33. package/dist/validate-order.test.d.ts +2 -0
  34. package/dist/validate-order.test.d.ts.map +1 -0
  35. package/dist/validate-order.test.js +234 -0
  36. package/dist/validate-order.test.js.map +1 -0
  37. package/dist/with-timeout.d.ts +25 -0
  38. package/dist/with-timeout.d.ts.map +1 -0
  39. package/dist/with-timeout.js +33 -0
  40. package/dist/with-timeout.js.map +1 -0
  41. package/dist/with-timeout.test.d.ts +2 -0
  42. package/dist/with-timeout.test.d.ts.map +1 -0
  43. package/dist/with-timeout.test.js +72 -0
  44. package/dist/with-timeout.test.js.map +1 -0
  45. package/package.json +1 -2
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Rate Limiter
3
+ *
4
+ * Implements exponential backoff and circuit breaker patterns
5
+ * for handling API rate limits gracefully.
6
+ */
7
+ export interface RateLimiterConfig {
8
+ /** Minimum interval between requests in ms (default: 1000) */
9
+ minInterval: number;
10
+ /** Maximum interval between requests in ms (default: 60000) */
11
+ maxInterval: number;
12
+ /** Multiplier for exponential backoff (default: 2) */
13
+ backoffMultiplier: number;
14
+ /** Number of consecutive failures before circuit opens (default: 5) */
15
+ circuitBreakerThreshold: number;
16
+ /** Time to wait before attempting to close circuit in ms (default: 30000) */
17
+ circuitResetTimeout: number;
18
+ }
19
+ export interface RateLimiterState {
20
+ /** Current interval between requests */
21
+ currentInterval: number;
22
+ /** Number of consecutive failures */
23
+ consecutiveFailures: number;
24
+ /** Whether the circuit breaker is open (blocking requests) */
25
+ isCircuitOpen: boolean;
26
+ /** Whether rate limiting is active */
27
+ isRateLimited: boolean;
28
+ /** Timestamp of last successful request */
29
+ lastSuccessTime: number;
30
+ /** Timestamp of last failure */
31
+ lastFailureTime: number;
32
+ }
33
+ /**
34
+ * Create a rate limiter instance
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * const limiter = createRateLimiter();
39
+ *
40
+ * async function fetchData() {
41
+ * if (limiter.shouldBlock()) {
42
+ * return; // Skip request
43
+ * }
44
+ *
45
+ * try {
46
+ * const data = await api.getData();
47
+ * limiter.recordSuccess();
48
+ * return data;
49
+ * } catch (err) {
50
+ * if (isRateLimitError(err)) {
51
+ * limiter.recordRateLimitError();
52
+ * } else {
53
+ * limiter.recordFailure();
54
+ * }
55
+ * throw err;
56
+ * }
57
+ * }
58
+ * ```
59
+ */
60
+ export declare function createRateLimiter(config?: Partial<RateLimiterConfig>): {
61
+ shouldBlock: () => boolean;
62
+ getCurrentInterval: () => number;
63
+ getState: () => Readonly<RateLimiterState>;
64
+ recordSuccess: () => void;
65
+ recordRateLimitError: () => void;
66
+ recordFailure: () => void;
67
+ reset: () => void;
68
+ };
69
+ /**
70
+ * Type for the rate limiter instance
71
+ */
72
+ export type RateLimiter = ReturnType<typeof createRateLimiter>;
73
+ /**
74
+ * Check if an error is a rate limit error (HTTP 429)
75
+ *
76
+ * @param error - Error to check
77
+ * @returns true if the error is a rate limit error
78
+ */
79
+ export declare function isRateLimitError(error: unknown): boolean;
80
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uEAAuE;IACvE,uBAAuB,EAAE,MAAM,CAAC;IAChC,6EAA6E;IAC7E,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,wCAAwC;IACxC,eAAe,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,aAAa,EAAE,OAAO,CAAC;IACvB,sCAAsC;IACtC,aAAa,EAAE,OAAO,CAAC;IACvB,2CAA2C;IAC3C,eAAe,EAAE,MAAM,CAAC;IACxB,gCAAgC;IAChC,eAAe,EAAE,MAAM,CAAC;CACzB;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,GAAE,OAAO,CAAC,iBAAiB,CAAM;uBAiB/C,OAAO;8BAOA,MAAM;oBAOhB,QAAQ,CAAC,gBAAgB,CAAC;yBAOrB,IAAI;gCAmBG,IAAI;yBAoBX,IAAI;iBA8BZ,IAAI;EAyBvB;AAED;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE/D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAmBxD"}
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Rate Limiter
3
+ *
4
+ * Implements exponential backoff and circuit breaker patterns
5
+ * for handling API rate limits gracefully.
6
+ */
7
+ const DEFAULT_CONFIG = {
8
+ minInterval: 1000,
9
+ maxInterval: 60000,
10
+ backoffMultiplier: 2,
11
+ circuitBreakerThreshold: 5,
12
+ circuitResetTimeout: 30000,
13
+ };
14
+ /**
15
+ * Create a rate limiter instance
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * const limiter = createRateLimiter();
20
+ *
21
+ * async function fetchData() {
22
+ * if (limiter.shouldBlock()) {
23
+ * return; // Skip request
24
+ * }
25
+ *
26
+ * try {
27
+ * const data = await api.getData();
28
+ * limiter.recordSuccess();
29
+ * return data;
30
+ * } catch (err) {
31
+ * if (isRateLimitError(err)) {
32
+ * limiter.recordRateLimitError();
33
+ * } else {
34
+ * limiter.recordFailure();
35
+ * }
36
+ * throw err;
37
+ * }
38
+ * }
39
+ * ```
40
+ */
41
+ export function createRateLimiter(config = {}) {
42
+ const cfg = { ...DEFAULT_CONFIG, ...config };
43
+ let state = {
44
+ currentInterval: cfg.minInterval,
45
+ consecutiveFailures: 0,
46
+ isCircuitOpen: false,
47
+ isRateLimited: false,
48
+ lastSuccessTime: 0,
49
+ lastFailureTime: 0,
50
+ };
51
+ let circuitResetTimer = null;
52
+ /**
53
+ * Check if requests should be blocked
54
+ */
55
+ function shouldBlock() {
56
+ return state.isCircuitOpen;
57
+ }
58
+ /**
59
+ * Get the current recommended interval between requests
60
+ */
61
+ function getCurrentInterval() {
62
+ return state.currentInterval;
63
+ }
64
+ /**
65
+ * Get the current state (for debugging/display)
66
+ */
67
+ function getState() {
68
+ return { ...state };
69
+ }
70
+ /**
71
+ * Record a successful request - resets backoff
72
+ */
73
+ function recordSuccess() {
74
+ state.consecutiveFailures = 0;
75
+ state.currentInterval = cfg.minInterval;
76
+ state.isRateLimited = false;
77
+ state.lastSuccessTime = Date.now();
78
+ // Close circuit if it was open
79
+ if (state.isCircuitOpen) {
80
+ state.isCircuitOpen = false;
81
+ if (circuitResetTimer) {
82
+ clearTimeout(circuitResetTimer);
83
+ circuitResetTimer = null;
84
+ }
85
+ }
86
+ }
87
+ /**
88
+ * Record a rate limit error (429) - applies exponential backoff
89
+ */
90
+ function recordRateLimitError() {
91
+ state.consecutiveFailures++;
92
+ state.isRateLimited = true;
93
+ state.lastFailureTime = Date.now();
94
+ // Apply exponential backoff
95
+ state.currentInterval = Math.min(state.currentInterval * cfg.backoffMultiplier, cfg.maxInterval);
96
+ // Open circuit if threshold reached
97
+ if (state.consecutiveFailures >= cfg.circuitBreakerThreshold) {
98
+ openCircuit();
99
+ }
100
+ }
101
+ /**
102
+ * Record a general failure (not rate limit)
103
+ */
104
+ function recordFailure() {
105
+ state.consecutiveFailures++;
106
+ state.lastFailureTime = Date.now();
107
+ // Open circuit if threshold reached
108
+ if (state.consecutiveFailures >= cfg.circuitBreakerThreshold) {
109
+ openCircuit();
110
+ }
111
+ }
112
+ /**
113
+ * Open the circuit breaker (block all requests temporarily)
114
+ */
115
+ function openCircuit() {
116
+ if (state.isCircuitOpen)
117
+ return;
118
+ state.isCircuitOpen = true;
119
+ // Schedule circuit reset
120
+ circuitResetTimer = setTimeout(() => {
121
+ state.isCircuitOpen = false;
122
+ state.consecutiveFailures = 0;
123
+ state.currentInterval = cfg.minInterval;
124
+ circuitResetTimer = null;
125
+ }, cfg.circuitResetTimeout);
126
+ }
127
+ /**
128
+ * Reset the rate limiter to initial state
129
+ */
130
+ function reset() {
131
+ if (circuitResetTimer) {
132
+ clearTimeout(circuitResetTimer);
133
+ circuitResetTimer = null;
134
+ }
135
+ state = {
136
+ currentInterval: cfg.minInterval,
137
+ consecutiveFailures: 0,
138
+ isCircuitOpen: false,
139
+ isRateLimited: false,
140
+ lastSuccessTime: 0,
141
+ lastFailureTime: 0,
142
+ };
143
+ }
144
+ return {
145
+ shouldBlock,
146
+ getCurrentInterval,
147
+ getState,
148
+ recordSuccess,
149
+ recordRateLimitError,
150
+ recordFailure,
151
+ reset,
152
+ };
153
+ }
154
+ /**
155
+ * Check if an error is a rate limit error (HTTP 429)
156
+ *
157
+ * @param error - Error to check
158
+ * @returns true if the error is a rate limit error
159
+ */
160
+ export function isRateLimitError(error) {
161
+ if (!error || typeof error !== 'object')
162
+ return false;
163
+ // Check for status code 429
164
+ if ('status' in error && error.status === 429)
165
+ return true;
166
+ if ('response' in error) {
167
+ const response = error.response;
168
+ if (response?.status === 429)
169
+ return true;
170
+ }
171
+ // Check error message
172
+ if ('message' in error && typeof error.message === 'string') {
173
+ const msg = error.message.toLowerCase();
174
+ if (msg.includes('429') || msg.includes('rate limit') || msg.includes('too many requests')) {
175
+ return true;
176
+ }
177
+ }
178
+ return false;
179
+ }
180
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../src/rate-limiter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Rate Limiter Tests
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=rate-limiter.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.test.d.ts","sourceRoot":"","sources":["../src/rate-limiter.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Rate Limiter Tests
3
+ */
4
+ import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
5
+ import { createRateLimiter, isRateLimitError, } from './rate-limiter.js';
6
+ describe('createRateLimiter', () => {
7
+ beforeEach(() => {
8
+ vi.useFakeTimers();
9
+ });
10
+ afterEach(() => {
11
+ vi.useRealTimers();
12
+ });
13
+ describe('initial state', () => {
14
+ it('should not block requests initially', () => {
15
+ const limiter = createRateLimiter();
16
+ expect(limiter.shouldBlock()).toBe(false);
17
+ });
18
+ it('should have minimum interval initially', () => {
19
+ const limiter = createRateLimiter({ minInterval: 1000 });
20
+ expect(limiter.getCurrentInterval()).toBe(1000);
21
+ });
22
+ it('should have correct initial state', () => {
23
+ const limiter = createRateLimiter();
24
+ const state = limiter.getState();
25
+ expect(state.consecutiveFailures).toBe(0);
26
+ expect(state.isCircuitOpen).toBe(false);
27
+ expect(state.isRateLimited).toBe(false);
28
+ });
29
+ });
30
+ describe('recordSuccess', () => {
31
+ it('should reset consecutive failures', () => {
32
+ const limiter = createRateLimiter();
33
+ limiter.recordFailure();
34
+ limiter.recordFailure();
35
+ expect(limiter.getState().consecutiveFailures).toBe(2);
36
+ limiter.recordSuccess();
37
+ expect(limiter.getState().consecutiveFailures).toBe(0);
38
+ });
39
+ it('should reset interval to minimum', () => {
40
+ const limiter = createRateLimiter({ minInterval: 1000 });
41
+ limiter.recordRateLimitError();
42
+ expect(limiter.getCurrentInterval()).toBeGreaterThan(1000);
43
+ limiter.recordSuccess();
44
+ expect(limiter.getCurrentInterval()).toBe(1000);
45
+ });
46
+ it('should clear rate limited flag', () => {
47
+ const limiter = createRateLimiter();
48
+ limiter.recordRateLimitError();
49
+ expect(limiter.getState().isRateLimited).toBe(true);
50
+ limiter.recordSuccess();
51
+ expect(limiter.getState().isRateLimited).toBe(false);
52
+ });
53
+ });
54
+ describe('recordRateLimitError', () => {
55
+ it('should increase interval with exponential backoff', () => {
56
+ const limiter = createRateLimiter({
57
+ minInterval: 1000,
58
+ backoffMultiplier: 2,
59
+ });
60
+ limiter.recordRateLimitError();
61
+ expect(limiter.getCurrentInterval()).toBe(2000);
62
+ limiter.recordRateLimitError();
63
+ expect(limiter.getCurrentInterval()).toBe(4000);
64
+ limiter.recordRateLimitError();
65
+ expect(limiter.getCurrentInterval()).toBe(8000);
66
+ });
67
+ it('should not exceed max interval', () => {
68
+ const limiter = createRateLimiter({
69
+ minInterval: 1000,
70
+ maxInterval: 5000,
71
+ backoffMultiplier: 2,
72
+ });
73
+ // 1000 -> 2000 -> 4000 -> 5000 (capped)
74
+ limiter.recordRateLimitError();
75
+ limiter.recordRateLimitError();
76
+ limiter.recordRateLimitError();
77
+ limiter.recordRateLimitError();
78
+ expect(limiter.getCurrentInterval()).toBe(5000);
79
+ });
80
+ it('should set rate limited flag', () => {
81
+ const limiter = createRateLimiter();
82
+ limiter.recordRateLimitError();
83
+ expect(limiter.getState().isRateLimited).toBe(true);
84
+ });
85
+ it('should increment consecutive failures', () => {
86
+ const limiter = createRateLimiter();
87
+ limiter.recordRateLimitError();
88
+ expect(limiter.getState().consecutiveFailures).toBe(1);
89
+ limiter.recordRateLimitError();
90
+ expect(limiter.getState().consecutiveFailures).toBe(2);
91
+ });
92
+ });
93
+ describe('circuit breaker', () => {
94
+ it('should open circuit after threshold failures', () => {
95
+ const limiter = createRateLimiter({
96
+ circuitBreakerThreshold: 3,
97
+ });
98
+ limiter.recordFailure();
99
+ limiter.recordFailure();
100
+ expect(limiter.shouldBlock()).toBe(false);
101
+ limiter.recordFailure();
102
+ expect(limiter.shouldBlock()).toBe(true);
103
+ });
104
+ it('should reset circuit after timeout', () => {
105
+ const limiter = createRateLimiter({
106
+ circuitBreakerThreshold: 2,
107
+ circuitResetTimeout: 5000,
108
+ });
109
+ limiter.recordFailure();
110
+ limiter.recordFailure();
111
+ expect(limiter.shouldBlock()).toBe(true);
112
+ // Advance time past reset timeout
113
+ vi.advanceTimersByTime(5001);
114
+ expect(limiter.shouldBlock()).toBe(false);
115
+ expect(limiter.getState().consecutiveFailures).toBe(0);
116
+ });
117
+ it('should close circuit on success', () => {
118
+ const limiter = createRateLimiter({
119
+ circuitBreakerThreshold: 2,
120
+ });
121
+ limiter.recordFailure();
122
+ limiter.recordFailure();
123
+ expect(limiter.shouldBlock()).toBe(true);
124
+ limiter.recordSuccess();
125
+ expect(limiter.shouldBlock()).toBe(false);
126
+ });
127
+ });
128
+ describe('reset', () => {
129
+ it('should reset all state', () => {
130
+ const limiter = createRateLimiter({
131
+ minInterval: 1000,
132
+ circuitBreakerThreshold: 2,
133
+ });
134
+ limiter.recordRateLimitError();
135
+ limiter.recordRateLimitError();
136
+ expect(limiter.shouldBlock()).toBe(true);
137
+ expect(limiter.getCurrentInterval()).toBeGreaterThan(1000);
138
+ limiter.reset();
139
+ expect(limiter.shouldBlock()).toBe(false);
140
+ expect(limiter.getCurrentInterval()).toBe(1000);
141
+ expect(limiter.getState().consecutiveFailures).toBe(0);
142
+ expect(limiter.getState().isRateLimited).toBe(false);
143
+ });
144
+ });
145
+ });
146
+ describe('isRateLimitError', () => {
147
+ it('should return true for error with status 429', () => {
148
+ expect(isRateLimitError({ status: 429 })).toBe(true);
149
+ });
150
+ it('should return true for error with response.status 429', () => {
151
+ expect(isRateLimitError({ response: { status: 429 } })).toBe(true);
152
+ });
153
+ it('should return true for error message containing 429', () => {
154
+ expect(isRateLimitError({ message: 'Error 429: Too Many Requests' })).toBe(true);
155
+ });
156
+ it('should return true for error message containing rate limit', () => {
157
+ expect(isRateLimitError({ message: 'Rate limit exceeded' })).toBe(true);
158
+ });
159
+ it('should return true for error message containing too many requests', () => {
160
+ expect(isRateLimitError({ message: 'too many requests' })).toBe(true);
161
+ });
162
+ it('should return false for other errors', () => {
163
+ expect(isRateLimitError({ status: 500 })).toBe(false);
164
+ expect(isRateLimitError({ message: 'Internal server error' })).toBe(false);
165
+ });
166
+ it('should return false for null/undefined', () => {
167
+ expect(isRateLimitError(null)).toBe(false);
168
+ expect(isRateLimitError(undefined)).toBe(false);
169
+ });
170
+ it('should return false for non-objects', () => {
171
+ expect(isRateLimitError('error')).toBe(false);
172
+ expect(isRateLimitError(123)).toBe(false);
173
+ });
174
+ });
175
+ //# sourceMappingURL=rate-limiter.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.test.js","sourceRoot":"","sources":["../src/rate-limiter.test.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -38,6 +38,7 @@ export interface MarketDisplay {
38
38
  no_bid?: number;
39
39
  no_ask?: number;
40
40
  volume?: number;
41
+ volume_24h?: number;
41
42
  open_interest?: number;
42
43
  close_time?: string;
43
44
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,SAAS,EACT,YAAY,EACZ,SAAS,EACT,SAAS,EACT,aAAa,GACd,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,0BAA0B,EAC1B,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC;AAEhC;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,cAAc,EAAE,CAAC;IACtB,EAAE,EAAE,cAAc,EAAE,CAAC;CACtB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,YAAY,EACV,SAAS,EACT,YAAY,EACZ,SAAS,EACT,SAAS,EACT,aAAa,GACd,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,0BAA0B,EAC1B,4BAA4B,EAC5B,0BAA0B,GAC3B,MAAM,mBAAmB,CAAC;AAE3B;;GAEG;AACH,MAAM,MAAM,IAAI,GAAG,KAAK,GAAG,IAAI,CAAC;AAEhC;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAEpC;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE3C;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzD;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,cAAc,EAAE,CAAC;IACtB,EAAE,EAAE,cAAc,EAAE,CAAC;CACtB"}
@@ -0,0 +1,32 @@
1
+ import type { MarketApi, PortfolioApi } from "kalshi-typescript";
2
+ export interface OrderValidationInput {
3
+ ticker: string;
4
+ side: "yes" | "no";
5
+ action: "buy" | "sell";
6
+ count: number;
7
+ price?: number;
8
+ }
9
+ export interface OrderValidationResult {
10
+ valid: boolean;
11
+ errors: string[];
12
+ warnings: string[];
13
+ estimatedCost?: number;
14
+ currentBalance?: number;
15
+ marketStatus?: string;
16
+ }
17
+ /**
18
+ * Validates an order before submission
19
+ *
20
+ * Performs pre-flight checks:
21
+ * - Market is open for trading
22
+ * - Sufficient balance for buy orders
23
+ * - Price is reasonable (warns if far from market)
24
+ * - Order quantity is valid
25
+ *
26
+ * @param input - Order parameters to validate
27
+ * @param marketApi - Market API client
28
+ * @param portfolioApi - Portfolio API client
29
+ * @returns Validation result with errors and warnings
30
+ */
31
+ export declare function validateOrder(input: OrderValidationInput, marketApi: MarketApi, portfolioApi: PortfolioApi): Promise<OrderValidationResult>;
32
+ //# sourceMappingURL=validate-order.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-order.d.ts","sourceRoot":"","sources":["../src/validate-order.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjE,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,KAAK,GAAG,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,oBAAoB,EAC3B,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,qBAAqB,CAAC,CAqFhC"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Validates an order before submission
3
+ *
4
+ * Performs pre-flight checks:
5
+ * - Market is open for trading
6
+ * - Sufficient balance for buy orders
7
+ * - Price is reasonable (warns if far from market)
8
+ * - Order quantity is valid
9
+ *
10
+ * @param input - Order parameters to validate
11
+ * @param marketApi - Market API client
12
+ * @param portfolioApi - Portfolio API client
13
+ * @returns Validation result with errors and warnings
14
+ */
15
+ export async function validateOrder(input, marketApi, portfolioApi) {
16
+ const errors = [];
17
+ const warnings = [];
18
+ let estimatedCost = 0;
19
+ let currentBalance = 0;
20
+ let marketStatus = "unknown";
21
+ try {
22
+ // 1. Fetch market details
23
+ const marketResponse = await marketApi.getMarket(input.ticker);
24
+ const market = marketResponse.data.market;
25
+ marketStatus = market.status || "unknown";
26
+ // Check market is open
27
+ if (market.status?.toLowerCase() !== "open") {
28
+ errors.push(`Market ${input.ticker} is ${market.status}, not open for trading`);
29
+ }
30
+ // Get current market price
31
+ const currentPrice = input.side === "yes"
32
+ ? input.action === "buy"
33
+ ? market.yes_ask
34
+ : market.yes_bid
35
+ : input.action === "buy"
36
+ ? market.no_ask
37
+ : market.no_bid;
38
+ // Warn if user price is far from market
39
+ if (input.price && currentPrice) {
40
+ const priceDiff = Math.abs(input.price - currentPrice);
41
+ if (priceDiff > 20) {
42
+ warnings.push(`Your price (${input.price}¢) is ${priceDiff}¢ away from market (${currentPrice}¢)`);
43
+ }
44
+ }
45
+ // 2. Fetch balance
46
+ const balanceResponse = await portfolioApi.getBalance();
47
+ currentBalance = balanceResponse.data.balance || 0;
48
+ // Calculate estimated cost
49
+ // For buy orders: cost = count * price
50
+ // For sell orders: no cost (you receive money)
51
+ if (input.action === "buy") {
52
+ const price = input.price || currentPrice || 50; // Default to 50¢ if unknown
53
+ estimatedCost = input.count * price;
54
+ // Check sufficient balance
55
+ if (estimatedCost > currentBalance) {
56
+ errors.push(`Insufficient balance: need ${estimatedCost}¢, have ${currentBalance}¢`);
57
+ }
58
+ }
59
+ // 3. Sanity checks
60
+ if (input.count <= 0) {
61
+ errors.push("Order quantity must be positive");
62
+ }
63
+ if (input.count > 1000) {
64
+ warnings.push("Large order size may have poor execution");
65
+ }
66
+ if (input.price && (input.price < 1 || input.price > 99)) {
67
+ errors.push("Price must be between 1¢ and 99¢");
68
+ }
69
+ }
70
+ catch (error) {
71
+ errors.push(`Validation failed: ${error instanceof Error ? error.message : "Unknown error"}`);
72
+ }
73
+ return {
74
+ valid: errors.length === 0,
75
+ errors,
76
+ warnings,
77
+ estimatedCost,
78
+ currentBalance,
79
+ marketStatus,
80
+ };
81
+ }
82
+ //# sourceMappingURL=validate-order.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-order.js","sourceRoot":"","sources":["../src/validate-order.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=validate-order.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-order.test.d.ts","sourceRoot":"","sources":["../src/validate-order.test.ts"],"names":[],"mappings":""}