@gaberoo/kalshitools 1.0.2 → 1.1.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 (61) hide show
  1. package/README.md +328 -27
  2. package/dist/commands/config/init.js +4 -4
  3. package/dist/commands/config/show.js +5 -5
  4. package/dist/commands/markets/list.d.ts +5 -1
  5. package/dist/commands/markets/list.js +28 -8
  6. package/dist/commands/markets/orderbook.d.ts +13 -0
  7. package/dist/commands/markets/orderbook.js +83 -0
  8. package/dist/commands/markets/scan.d.ts +18 -0
  9. package/dist/commands/markets/scan.js +237 -0
  10. package/dist/commands/markets/show.d.ts +3 -3
  11. package/dist/commands/markets/show.js +7 -7
  12. package/dist/commands/orders/cancel.d.ts +3 -3
  13. package/dist/commands/orders/cancel.js +7 -7
  14. package/dist/commands/orders/create.d.ts +5 -5
  15. package/dist/commands/orders/create.js +33 -33
  16. package/dist/commands/orders/list.d.ts +1 -1
  17. package/dist/commands/orders/list.js +9 -9
  18. package/dist/commands/portfolio/analytics.d.ts +12 -0
  19. package/dist/commands/portfolio/analytics.js +192 -0
  20. package/dist/commands/portfolio/fills.d.ts +1 -1
  21. package/dist/commands/portfolio/fills.js +7 -7
  22. package/dist/commands/portfolio/history.d.ts +14 -0
  23. package/dist/commands/portfolio/history.js +245 -0
  24. package/dist/commands/portfolio/positions.d.ts +1 -0
  25. package/dist/commands/portfolio/positions.js +11 -2
  26. package/dist/commands/portfolio/risk.d.ts +11 -0
  27. package/dist/commands/portfolio/risk.js +206 -0
  28. package/dist/lib/analytics.d.ts +64 -0
  29. package/dist/lib/analytics.js +236 -0
  30. package/dist/lib/base-command.d.ts +2 -2
  31. package/dist/lib/base-command.js +8 -8
  32. package/dist/lib/config/manager.d.ts +25 -25
  33. package/dist/lib/config/manager.js +51 -51
  34. package/dist/lib/config/schema.d.ts +11 -11
  35. package/dist/lib/config/schema.js +6 -6
  36. package/dist/lib/errors/base.d.ts +10 -10
  37. package/dist/lib/errors/base.js +7 -7
  38. package/dist/lib/kalshi/auth.d.ts +4 -4
  39. package/dist/lib/kalshi/auth.js +24 -24
  40. package/dist/lib/kalshi/client.d.ts +35 -35
  41. package/dist/lib/kalshi/client.js +93 -91
  42. package/dist/lib/kalshi/index.d.ts +1 -1
  43. package/dist/lib/kalshi/index.js +1 -1
  44. package/dist/lib/kalshi/types.d.ts +53 -53
  45. package/dist/lib/logger.js +3 -3
  46. package/dist/lib/output/formatter.d.ts +20 -20
  47. package/dist/lib/output/formatter.js +55 -55
  48. package/dist/lib/retry.d.ts +2 -2
  49. package/dist/lib/retry.js +8 -10
  50. package/dist/lib/risk.d.ts +51 -0
  51. package/dist/lib/risk.js +153 -0
  52. package/dist/lib/sanitize.js +9 -9
  53. package/dist/lib/scanner.d.ts +58 -0
  54. package/dist/lib/scanner.js +160 -0
  55. package/dist/lib/shutdown.d.ts +4 -4
  56. package/dist/lib/shutdown.js +7 -7
  57. package/dist/lib/validation.d.ts +5 -5
  58. package/dist/lib/validation.js +14 -20
  59. package/docs/TRADING_STRATEGIES.md +538 -0
  60. package/oclif.manifest.json +559 -170
  61. package/package.json +1 -1
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Market scanning and opportunity detection utilities
3
+ */
4
+ /**
5
+ * Calculate total liquidity from an orderbook
6
+ */
7
+ export function calculateOrderbookLiquidity(orderbook) {
8
+ const yesTotal = orderbook.yes.reduce((sum, level) => sum + level.count * level.price, 0);
9
+ const noTotal = orderbook.no.reduce((sum, level) => sum + level.count * level.price, 0);
10
+ return {
11
+ noTotal,
12
+ total: yesTotal + noTotal,
13
+ yesTotal,
14
+ };
15
+ }
16
+ /**
17
+ * Calculate bid-ask spread from an orderbook
18
+ *
19
+ * In Kalshi orderbooks:
20
+ * - Lower prices in the yes array are bids (buyers)
21
+ * - Higher prices in the yes array are asks (sellers)
22
+ * - Spread = ask - bid
23
+ */
24
+ export function calculateSpread(orderbook) {
25
+ if (orderbook.yes.length === 0) {
26
+ return null;
27
+ }
28
+ const prices = orderbook.yes.map(level => level.price).sort((a, b) => a - b);
29
+ // Best bid is the lowest price (buyers)
30
+ const yesBid = prices[0];
31
+ // Best ask is the highest price (sellers)
32
+ const yesAsk = prices.at(-1);
33
+ if (!yesAsk || !yesBid) {
34
+ return null;
35
+ }
36
+ // If bid and ask are the same, there's no spread
37
+ const spreadCents = yesAsk - yesBid;
38
+ const midPrice = (yesBid + yesAsk) / 2;
39
+ const spreadPct = midPrice > 0 ? (spreadCents / midPrice) * 100 : 0;
40
+ return {
41
+ spreadCents,
42
+ spreadPct,
43
+ yesAsk,
44
+ yesBid,
45
+ };
46
+ }
47
+ const DEFAULT_WEIGHTS = {
48
+ liquidity: 40,
49
+ spread: 30,
50
+ volume: 30,
51
+ };
52
+ /**
53
+ * Score a market based on liquidity, spread, and volume
54
+ * Returns score from 0-100
55
+ */
56
+ export function scoreMarket(market, orderbook, weights = DEFAULT_WEIGHTS) {
57
+ let score = 0;
58
+ // Liquidity score (0-40 points)
59
+ const liquidity = calculateOrderbookLiquidity(orderbook);
60
+ const liquidityScore = Math.min(weights.liquidity, (liquidity.total / 5000) * weights.liquidity);
61
+ score += liquidityScore;
62
+ // Spread score (0-30 points) - wider spreads are better for market making
63
+ const spread = calculateSpread(orderbook);
64
+ if (spread) {
65
+ // Spread between 0.03 (3 cents) and 0.20 (20 cents) is ideal
66
+ const { spreadCents } = spread;
67
+ let spreadScore = 0;
68
+ if (spreadCents >= 0.03 && spreadCents <= 0.2) {
69
+ spreadScore = weights.spread;
70
+ }
71
+ else if (spreadCents > 0.2) {
72
+ // Too wide - gradually decrease score
73
+ spreadScore = Math.max(0, weights.spread * (1 - (spreadCents - 0.2) / 0.3));
74
+ }
75
+ else {
76
+ // Too narrow - gradually decrease score
77
+ spreadScore = (spreadCents / 0.03) * weights.spread;
78
+ }
79
+ score += spreadScore;
80
+ }
81
+ // Volume score (0-30 points)
82
+ const volume = market.volume_24h || market.volume || 0;
83
+ const volumeScore = Math.min(weights.volume, (volume / 10_000) * weights.volume);
84
+ score += volumeScore;
85
+ return Math.round(score);
86
+ }
87
+ /**
88
+ * Classify the type of opportunity a market presents
89
+ */
90
+ export function classifyOpportunity(spread, liquidity, volume) {
91
+ const hasWideSpread = spread >= 0.05;
92
+ const hasHighLiquidity = liquidity >= 2000;
93
+ const hasHighVolume = volume >= 5000;
94
+ // Check combinations
95
+ if (hasWideSpread && hasHighLiquidity && hasHighVolume) {
96
+ return 'balanced';
97
+ }
98
+ if (hasWideSpread) {
99
+ return 'wide_spread';
100
+ }
101
+ if (hasHighLiquidity) {
102
+ return 'high_liquidity';
103
+ }
104
+ if (hasHighVolume) {
105
+ return 'high_volume';
106
+ }
107
+ // Default to balanced if none of the above
108
+ return 'balanced';
109
+ }
110
+ /**
111
+ * Generate human-readable reason for opportunity
112
+ */
113
+ export function generateOpportunityReason(type, spread, liquidity, volume) {
114
+ const spreadCents = Math.round(spread * 100);
115
+ switch (type) {
116
+ case 'balanced': {
117
+ return `Balanced opportunity: ${spreadCents} cent spread, $${Math.round(liquidity)} liquidity, $${Math.round(volume)} volume`;
118
+ }
119
+ case 'high_liquidity': {
120
+ return `Strong liquidity ($${Math.round(liquidity).toLocaleString()})`;
121
+ }
122
+ case 'high_volume': {
123
+ return `High trading volume ($${Math.round(volume).toLocaleString()} 24h)`;
124
+ }
125
+ case 'wide_spread': {
126
+ return `High liquidity with ${spreadCents} cent spread`;
127
+ }
128
+ }
129
+ }
130
+ /**
131
+ * Filter markets based on criteria
132
+ */
133
+ export function filterMarketsByCriteria(markets, criteria) {
134
+ return markets.filter(m => {
135
+ // Liquidity check
136
+ if (criteria.minLiquidity && m.liquidity < criteria.minLiquidity) {
137
+ return false;
138
+ }
139
+ // Spread checks
140
+ if (m.spread === null) {
141
+ // No spread data - exclude if spread filters are specified
142
+ if (criteria.minSpread || criteria.maxSpread) {
143
+ return false;
144
+ }
145
+ }
146
+ else {
147
+ if (criteria.minSpread && m.spread < criteria.minSpread) {
148
+ return false;
149
+ }
150
+ if (criteria.maxSpread && m.spread > criteria.maxSpread) {
151
+ return false;
152
+ }
153
+ }
154
+ // Volume check
155
+ if (criteria.minVolume && m.volume < criteria.minVolume) {
156
+ return false;
157
+ }
158
+ return true;
159
+ });
160
+ }
@@ -10,6 +10,10 @@ declare class ShutdownManager {
10
10
  private isShuttingDown;
11
11
  private shutdownTimeout;
12
12
  constructor();
13
+ /**
14
+ * Check if shutdown is in progress
15
+ */
16
+ isShutdownInProgress(): boolean;
13
17
  /**
14
18
  * Register a cleanup function to be called on shutdown
15
19
  */
@@ -26,10 +30,6 @@ declare class ShutdownManager {
26
30
  * Perform graceful shutdown
27
31
  */
28
32
  private shutdown;
29
- /**
30
- * Check if shutdown is in progress
31
- */
32
- isShutdownInProgress(): boolean;
33
33
  }
34
34
  export declare const shutdownManager: ShutdownManager;
35
35
  /**
@@ -10,6 +10,12 @@ class ShutdownManager {
10
10
  // Register signal handlers
11
11
  this.registerSignalHandlers();
12
12
  }
13
+ /**
14
+ * Check if shutdown is in progress
15
+ */
16
+ isShutdownInProgress() {
17
+ return this.isShuttingDown;
18
+ }
13
19
  /**
14
20
  * Register a cleanup function to be called on shutdown
15
21
  */
@@ -56,7 +62,7 @@ class ShutdownManager {
56
62
  return;
57
63
  }
58
64
  this.isShuttingDown = true;
59
- logger.info({ signal, cleanupCount: this.cleanupFunctions.length }, 'Starting graceful shutdown');
65
+ logger.info({ cleanupCount: this.cleanupFunctions.length, signal }, 'Starting graceful shutdown');
60
66
  // Set timeout for shutdown
61
67
  const shutdownTimer = setTimeout(() => {
62
68
  logger.error({ timeout: this.shutdownTimeout }, 'Shutdown timeout exceeded, forcing exit');
@@ -83,12 +89,6 @@ class ShutdownManager {
83
89
  process.exit(1);
84
90
  }
85
91
  }
86
- /**
87
- * Check if shutdown is in progress
88
- */
89
- isShutdownInProgress() {
90
- return this.isShuttingDown;
91
- }
92
92
  }
93
93
  // Singleton instance
94
94
  export const shutdownManager = new ShutdownManager();
@@ -18,7 +18,7 @@ export declare function validatePrice(price: number): void;
18
18
  /**
19
19
  * Validate side
20
20
  */
21
- export declare function validateSide(side: string): 'yes' | 'no';
21
+ export declare function validateSide(side: string): 'no' | 'yes';
22
22
  /**
23
23
  * Validate action
24
24
  */
@@ -26,12 +26,12 @@ export declare function validateAction(action: string): 'buy' | 'sell';
26
26
  /**
27
27
  * Validate order type
28
28
  */
29
- export declare function validateOrderType(type: string): 'market' | 'limit';
29
+ export declare function validateOrderType(type: string): 'limit' | 'market';
30
30
  /**
31
31
  * Calculate order cost estimate
32
32
  */
33
- export declare function estimateOrderCost(side: 'yes' | 'no', action: 'buy' | 'sell', quantity: number, price?: number): {
34
- min: number;
35
- max: number;
33
+ export declare function estimateOrderCost(side: 'no' | 'yes', action: 'buy' | 'sell', quantity: number, price?: number): {
36
34
  estimate: number;
35
+ max: number;
36
+ min: number;
37
37
  };
@@ -18,8 +18,8 @@ export function validateTicker(ticker) {
18
18
  }
19
19
  if (ticker.length < 3 || ticker.length > 100) {
20
20
  throw new ValidationError('Ticker must be between 3 and 100 characters', {
21
- ticker,
22
21
  length: ticker.length,
22
+ ticker,
23
23
  });
24
24
  }
25
25
  }
@@ -35,8 +35,8 @@ export function validateQuantity(quantity, maxOrderSize) {
35
35
  }
36
36
  if (quantity > maxOrderSize) {
37
37
  throw new ValidationError(`Quantity exceeds maximum order size of ${maxOrderSize}`, {
38
- quantity,
39
38
  maxOrderSize,
39
+ quantity,
40
40
  });
41
41
  }
42
42
  }
@@ -52,8 +52,8 @@ export function validatePrice(price) {
52
52
  const decimalPlaces = (price.toString().split('.')[1] || '').length;
53
53
  if (decimalPlaces > 2) {
54
54
  throw new ValidationError('Price cannot have more than 2 decimal places', {
55
- price,
56
55
  decimalPlaces,
56
+ price,
57
57
  });
58
58
  }
59
59
  }
@@ -97,24 +97,18 @@ export function estimateOrderCost(side, action, quantity, price) {
97
97
  if (price) {
98
98
  // Limit buy - exact price known
99
99
  const cost = quantity * price;
100
- return { min: cost, max: cost, estimate: cost };
101
- }
102
- else {
103
- // Market buy - could fill at any price
104
- // Estimate mid-range
105
- const estimate = quantity * 0.5;
106
- return { min: quantity * 0.01, max: quantity * 0.99, estimate };
100
+ return { estimate: cost, max: cost, min: cost };
107
101
  }
102
+ // Market buy - could fill at any price
103
+ // Estimate mid-range
104
+ const estimate = quantity * 0.5;
105
+ return { estimate, max: quantity * 0.99, min: quantity * 0.01 };
108
106
  }
109
- else {
110
- // Sell orders
111
- if (price) {
112
- const proceeds = quantity * price;
113
- return { min: proceeds, max: proceeds, estimate: proceeds };
114
- }
115
- else {
116
- const estimate = quantity * 0.5;
117
- return { min: quantity * 0.01, max: quantity * 0.99, estimate };
118
- }
107
+ // Sell orders
108
+ if (price) {
109
+ const proceeds = quantity * price;
110
+ return { estimate: proceeds, max: proceeds, min: proceeds };
119
111
  }
112
+ const estimate = quantity * 0.5;
113
+ return { estimate, max: quantity * 0.99, min: quantity * 0.01 };
120
114
  }