@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.
- package/dist/cache.d.ts +71 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +96 -0
- package/dist/cache.js.map +1 -0
- package/dist/cache.test.d.ts +5 -0
- package/dist/cache.test.d.ts.map +1 -0
- package/dist/cache.test.js +108 -0
- package/dist/cache.test.js.map +1 -0
- package/dist/format.d.ts +39 -0
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +80 -0
- package/dist/format.js.map +1 -1
- package/dist/format.test.js +66 -2
- package/dist/format.test.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/rate-limiter.d.ts +80 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +180 -0
- package/dist/rate-limiter.js.map +1 -0
- package/dist/rate-limiter.test.d.ts +5 -0
- package/dist/rate-limiter.test.d.ts.map +1 -0
- package/dist/rate-limiter.test.js +175 -0
- package/dist/rate-limiter.test.js.map +1 -0
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-order.d.ts +32 -0
- package/dist/validate-order.d.ts.map +1 -0
- package/dist/validate-order.js +82 -0
- package/dist/validate-order.js.map +1 -0
- package/dist/validate-order.test.d.ts +2 -0
- package/dist/validate-order.test.d.ts.map +1 -0
- package/dist/validate-order.test.js +234 -0
- package/dist/validate-order.test.js.map +1 -0
- package/dist/with-timeout.d.ts +25 -0
- package/dist/with-timeout.d.ts.map +1 -0
- package/dist/with-timeout.js +33 -0
- package/dist/with-timeout.js.map +1 -0
- package/dist/with-timeout.test.d.ts +2 -0
- package/dist/with-timeout.test.d.ts.map +1 -0
- package/dist/with-timeout.test.js +72 -0
- package/dist/with-timeout.test.js.map +1 -0
- 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":"AAAA;;;;;GAKG;AA8BH,MAAM,cAAc,GAAsB;IACxC,WAAW,EAAE,IAAI;IACjB,WAAW,EAAE,KAAK;IAClB,iBAAiB,EAAE,CAAC;IACpB,uBAAuB,EAAE,CAAC;IAC1B,mBAAmB,EAAE,KAAK;CAC3B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,iBAAiB,CAAC,SAAqC,EAAE;IACvE,MAAM,GAAG,GAAsB,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IAEhE,IAAI,KAAK,GAAqB;QAC5B,eAAe,EAAE,GAAG,CAAC,WAAW;QAChC,mBAAmB,EAAE,CAAC;QACtB,aAAa,EAAE,KAAK;QACpB,aAAa,EAAE,KAAK;QACpB,eAAe,EAAE,CAAC;QAClB,eAAe,EAAE,CAAC;KACnB,CAAC;IAEF,IAAI,iBAAiB,GAAyC,IAAI,CAAC;IAEnE;;OAEG;IACH,SAAS,WAAW;QAClB,OAAO,KAAK,CAAC,aAAa,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,SAAS,kBAAkB;QACzB,OAAO,KAAK,CAAC,eAAe,CAAC;IAC/B,CAAC;IAED;;OAEG;IACH,SAAS,QAAQ;QACf,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,SAAS,aAAa;QACpB,KAAK,CAAC,mBAAmB,GAAG,CAAC,CAAC;QAC9B,KAAK,CAAC,eAAe,GAAG,GAAG,CAAC,WAAW,CAAC;QACxC,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC;QAC5B,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEnC,+BAA+B;QAC/B,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;YACxB,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC;YAC5B,IAAI,iBAAiB,EAAE,CAAC;gBACtB,YAAY,CAAC,iBAAiB,CAAC,CAAC;gBAChC,iBAAiB,GAAG,IAAI,CAAC;YAC3B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,SAAS,oBAAoB;QAC3B,KAAK,CAAC,mBAAmB,EAAE,CAAC;QAC5B,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC;QAC3B,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEnC,4BAA4B;QAC5B,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,CAC9B,KAAK,CAAC,eAAe,GAAG,GAAG,CAAC,iBAAiB,EAC7C,GAAG,CAAC,WAAW,CAChB,CAAC;QAEF,oCAAoC;QACpC,IAAI,KAAK,CAAC,mBAAmB,IAAI,GAAG,CAAC,uBAAuB,EAAE,CAAC;YAC7D,WAAW,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,SAAS,aAAa;QACpB,KAAK,CAAC,mBAAmB,EAAE,CAAC;QAC5B,KAAK,CAAC,eAAe,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAEnC,oCAAoC;QACpC,IAAI,KAAK,CAAC,mBAAmB,IAAI,GAAG,CAAC,uBAAuB,EAAE,CAAC;YAC7D,WAAW,EAAE,CAAC;QAChB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,SAAS,WAAW;QAClB,IAAI,KAAK,CAAC,aAAa;YAAE,OAAO;QAEhC,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC;QAE3B,yBAAyB;QACzB,iBAAiB,GAAG,UAAU,CAAC,GAAG,EAAE;YAClC,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC;YAC5B,KAAK,CAAC,mBAAmB,GAAG,CAAC,CAAC;YAC9B,KAAK,CAAC,eAAe,GAAG,GAAG,CAAC,WAAW,CAAC;YACxC,iBAAiB,GAAG,IAAI,CAAC;QAC3B,CAAC,EAAE,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAC9B,CAAC;IAED;;OAEG;IACH,SAAS,KAAK;QACZ,IAAI,iBAAiB,EAAE,CAAC;YACtB,YAAY,CAAC,iBAAiB,CAAC,CAAC;YAChC,iBAAiB,GAAG,IAAI,CAAC;QAC3B,CAAC;QAED,KAAK,GAAG;YACN,eAAe,EAAE,GAAG,CAAC,WAAW;YAChC,mBAAmB,EAAE,CAAC;YACtB,aAAa,EAAE,KAAK;YACpB,aAAa,EAAE,KAAK;YACpB,eAAe,EAAE,CAAC;YAClB,eAAe,EAAE,CAAC;SACnB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,WAAW;QACX,kBAAkB;QAClB,QAAQ;QACR,aAAa;QACb,oBAAoB;QACpB,aAAa;QACb,KAAK;KACN,CAAC;AACJ,CAAC;AAOD;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAEtD,4BAA4B;IAC5B,IAAI,QAAQ,IAAI,KAAK,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IAC3D,IAAI,UAAU,IAAI,KAAK,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,KAAK,CAAC,QAA2C,CAAC;QACnE,IAAI,QAAQ,EAAE,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;IAC5C,CAAC;IAED,sBAAsB;IACtB,IAAI,SAAS,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5D,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QACxC,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC3F,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -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
package/dist/types.d.ts.map
CHANGED
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"validate-order.test.d.ts","sourceRoot":"","sources":["../src/validate-order.test.ts"],"names":[],"mappings":""}
|