@gaberoo/kalshitools 1.0.3 → 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.
package/README.md CHANGED
@@ -16,6 +16,10 @@ A powerful CLI tool for interacting with the Kalshi prediction markets API. Buil
16
16
  - 🛡️ **Safety First**: Demo environment default, confirmations, and order limits
17
17
  - 📈 **Order Book Analysis**: Real-time order depth and liquidity assessment
18
18
  - 🔍 **Market Discovery**: Event/series filtering, nested markets, multi-ticker batch queries
19
+ - 📊 **Portfolio Analytics**: Comprehensive performance metrics, win rate, P&L analysis, fee tracking
20
+ - ⚠️ **Risk Management**: Real-time risk assessment, concentration analysis, correlation detection
21
+ - 📈 **Historical Analysis**: Time-series performance data, maker/taker statistics, trend analysis
22
+ - 🔎 **Market Scanner**: Automated opportunity discovery based on liquidity, spreads, and volume
19
23
 
20
24
  ## Quick Start
21
25
 
@@ -120,6 +124,86 @@ kalshitools orders create \
120
124
 
121
125
  # View order history
122
126
  kalshitools orders list
127
+
128
+ # Analyze portfolio performance
129
+ kalshitools portfolio analytics --period 30 --json
130
+
131
+ # Check portfolio risk
132
+ kalshitools portfolio risk --threshold 20
133
+
134
+ # View historical performance
135
+ kalshitools portfolio history --group-by week
136
+
137
+ # Scan markets for opportunities
138
+ kalshitools markets scan --min-liquidity 200 --min-spread 0.05
139
+ ```
140
+
141
+ ## Advanced Features for AI Trading Agents
142
+
143
+ kalshitools provides powerful analytics and scanning capabilities designed for automated trading systems:
144
+
145
+ ### Portfolio Analytics
146
+
147
+ Get comprehensive performance metrics for strategy evaluation:
148
+
149
+ ```bash
150
+ # Full analytics for last 30 days
151
+ kalshitools portfolio analytics --period 30 --json
152
+
153
+ # Custom date range
154
+ kalshitools portfolio analytics \
155
+ --min-ts 2026-01-01T00:00:00Z \
156
+ --max-ts 2026-02-01T00:00:00Z \
157
+ --json
158
+
159
+ # Returns: win rate, avg P&L, total return, exposure ratio, concentration, fees
160
+ ```
161
+
162
+ ### Risk Management
163
+
164
+ Real-time risk assessment to prevent over-concentration:
165
+
166
+ ```bash
167
+ # Check portfolio risk with 20% position limit
168
+ kalshitools portfolio risk --threshold 20 --json
169
+
170
+ # Analyze concentration by event
171
+ kalshitools portfolio risk --group-by event --json
172
+
173
+ # Returns: risk score, concentrations, correlations, alerts, recommendations
174
+ ```
175
+
176
+ ### Historical Performance
177
+
178
+ Deep dive into trading history with time-series analytics:
179
+
180
+ ```bash
181
+ # Daily performance breakdown
182
+ kalshitools portfolio history --group-by day --json
183
+
184
+ # Filter by specific market
185
+ kalshitools portfolio history --ticker MARKET-A --json
186
+
187
+ # Returns: time-series data, maker/taker stats, performance by ticker
188
+ ```
189
+
190
+ ### Market Opportunity Scanner
191
+
192
+ Discover high-quality trading opportunities:
193
+
194
+ ```bash
195
+ # Scan for markets with good liquidity and spreads
196
+ kalshitools markets scan \
197
+ --min-liquidity 200 \
198
+ --min-spread 0.05 \
199
+ --max-spread 0.20 \
200
+ --min-volume 1000 \
201
+ --json
202
+
203
+ # Filter to specific event
204
+ kalshitools markets scan --event-ticker EVENT-2024 --json
205
+
206
+ # Returns: scored opportunities with liquidity, spread, and volume data
123
207
  ```
124
208
 
125
209
  ## Configuration
@@ -171,6 +255,22 @@ export LOG_LEVEL=info
171
255
  * [Place an order (with confirmation)](#place-an-order-with-confirmation)
172
256
  * [Use --dry-run to simulate without placing order](#use---dry-run-to-simulate-without-placing-order)
173
257
  * [View order history](#view-order-history)
258
+ * [Analyze portfolio performance](#analyze-portfolio-performance)
259
+ * [Check portfolio risk](#check-portfolio-risk)
260
+ * [View historical performance](#view-historical-performance)
261
+ * [Scan markets for opportunities](#scan-markets-for-opportunities)
262
+ * [Full analytics for last 30 days](#full-analytics-for-last-30-days)
263
+ * [Custom date range](#custom-date-range)
264
+ * [Returns: win rate, avg P&L, total return, exposure ratio, concentration, fees](#returns-win-rate-avg-pl-total-return-exposure-ratio-concentration-fees)
265
+ * [Check portfolio risk with 20% position limit](#check-portfolio-risk-with-20-position-limit)
266
+ * [Analyze concentration by event](#analyze-concentration-by-event)
267
+ * [Returns: risk score, concentrations, correlations, alerts, recommendations](#returns-risk-score-concentrations-correlations-alerts-recommendations)
268
+ * [Daily performance breakdown](#daily-performance-breakdown)
269
+ * [Filter by specific market](#filter-by-specific-market)
270
+ * [Returns: time-series data, maker/taker stats, performance by ticker](#returns-time-series-data-makertaker-stats-performance-by-ticker)
271
+ * [Scan for markets with good liquidity and spreads](#scan-for-markets-with-good-liquidity-and-spreads)
272
+ * [Filter to specific event](#filter-to-specific-event)
273
+ * [Returns: scored opportunities with liquidity, spread, and volume data](#returns-scored-opportunities-with-liquidity-spread-and-volume-data)
174
274
  * [Usage](#usage)
175
275
  * [Commands](#commands)
176
276
  <!-- tocstop -->
@@ -181,7 +281,7 @@ $ npm install -g @gaberoo/kalshitools
181
281
  $ kalshitools COMMAND
182
282
  running command...
183
283
  $ kalshitools (--version)
184
- @gaberoo/kalshitools/1.0.3 darwin-arm64 node-v22.20.0
284
+ @gaberoo/kalshitools/1.1.0 darwin-arm64 node-v22.20.0
185
285
  $ kalshitools --help [COMMAND]
186
286
  USAGE
187
287
  $ kalshitools COMMAND
@@ -195,6 +295,7 @@ USAGE
195
295
  * [`kalshitools help [COMMAND]`](#kalshitools-help-command)
196
296
  * [`kalshitools markets list`](#kalshitools-markets-list)
197
297
  * [`kalshitools markets orderbook TICKER`](#kalshitools-markets-orderbook-ticker)
298
+ * [`kalshitools markets scan`](#kalshitools-markets-scan)
198
299
  * [`kalshitools markets show TICKER`](#kalshitools-markets-show-ticker)
199
300
  * [`kalshitools orders cancel ORDERID`](#kalshitools-orders-cancel-orderid)
200
301
  * [`kalshitools orders create`](#kalshitools-orders-create)
@@ -209,9 +310,12 @@ USAGE
209
310
  * [`kalshitools plugins uninstall [PLUGIN]`](#kalshitools-plugins-uninstall-plugin)
210
311
  * [`kalshitools plugins unlink [PLUGIN]`](#kalshitools-plugins-unlink-plugin)
211
312
  * [`kalshitools plugins update`](#kalshitools-plugins-update)
313
+ * [`kalshitools portfolio analytics`](#kalshitools-portfolio-analytics)
212
314
  * [`kalshitools portfolio balance`](#kalshitools-portfolio-balance)
213
315
  * [`kalshitools portfolio fills`](#kalshitools-portfolio-fills)
316
+ * [`kalshitools portfolio history`](#kalshitools-portfolio-history)
214
317
  * [`kalshitools portfolio positions`](#kalshitools-portfolio-positions)
318
+ * [`kalshitools portfolio risk`](#kalshitools-portfolio-risk)
215
319
 
216
320
  ## `kalshitools config init`
217
321
 
@@ -237,7 +341,7 @@ EXAMPLES
237
341
  $ kalshitools config init --env production
238
342
  ```
239
343
 
240
- _See code: [src/commands/config/init.ts](https://github.com/kalshitools/kalshitools/blob/v1.0.3/src/commands/config/init.ts)_
344
+ _See code: [src/commands/config/init.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/config/init.ts)_
241
345
 
242
346
  ## `kalshitools config show`
243
347
 
@@ -259,7 +363,7 @@ EXAMPLES
259
363
  $ kalshitools config show --json
260
364
  ```
261
365
 
262
- _See code: [src/commands/config/show.ts](https://github.com/kalshitools/kalshitools/blob/v1.0.3/src/commands/config/show.ts)_
366
+ _See code: [src/commands/config/show.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/config/show.ts)_
263
367
 
264
368
  ## `kalshitools help [COMMAND]`
265
369
 
@@ -317,7 +421,7 @@ EXAMPLES
317
421
  $ kalshitools markets list --json
318
422
  ```
319
423
 
320
- _See code: [src/commands/markets/list.ts](https://github.com/kalshitools/kalshitools/blob/v1.0.3/src/commands/markets/list.ts)_
424
+ _See code: [src/commands/markets/list.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/markets/list.ts)_
321
425
 
322
426
  ## `kalshitools markets orderbook TICKER`
323
427
 
@@ -345,7 +449,47 @@ EXAMPLES
345
449
  $ kalshitools markets orderbook TICKER --json
346
450
  ```
347
451
 
348
- _See code: [src/commands/markets/orderbook.ts](https://github.com/kalshitools/kalshitools/blob/v1.0.3/src/commands/markets/orderbook.ts)_
452
+ _See code: [src/commands/markets/orderbook.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/markets/orderbook.ts)_
453
+
454
+ ## `kalshitools markets scan`
455
+
456
+ Scan markets for trading opportunities based on liquidity and spreads
457
+
458
+ ```
459
+ USAGE
460
+ $ kalshitools markets scan [--json] [--depth <value>] [--event-ticker <value>] [--limit <value>] [--max-spread
461
+ <value>] [--min-liquidity <value>] [--min-spread <value>] [--min-volume <value>] [--series-ticker <value>]
462
+ [--sort-by liquidity|score|spread|volume]
463
+
464
+ FLAGS
465
+ --depth=<value> [default: 5] Orderbook depth to fetch for analysis
466
+ --event-ticker=<value> Filter to specific event
467
+ --json Output in JSON format
468
+ --limit=<value> [default: 50] Maximum markets to scan
469
+ --max-spread=<value> [default: 0.30] Maximum spread in cents (e.g., 0.30 = 30 cents)
470
+ --min-liquidity=<value> [default: 100] Minimum orderbook depth in dollars
471
+ --min-spread=<value> [default: 0.03] Minimum spread in cents (e.g., 0.03 = 3 cents)
472
+ --min-volume=<value> [default: 500] Minimum 24h volume in dollars
473
+ --series-ticker=<value> Filter to specific series
474
+ --sort-by=<option> [default: score] Ranking criteria
475
+ <options: liquidity|score|spread|volume>
476
+
477
+ DESCRIPTION
478
+ Scan markets for trading opportunities based on liquidity and spreads
479
+
480
+ EXAMPLES
481
+ $ kalshitools markets scan
482
+
483
+ $ kalshitools markets scan --min-liquidity 200 --min-spread 0.05
484
+
485
+ $ kalshitools markets scan --event-ticker EVENT-2024
486
+
487
+ $ kalshitools markets scan --sort-by liquidity --limit 20
488
+
489
+ $ kalshitools markets scan --json
490
+ ```
491
+
492
+ _See code: [src/commands/markets/scan.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/markets/scan.ts)_
349
493
 
350
494
  ## `kalshitools markets show TICKER`
351
495
 
@@ -370,7 +514,7 @@ EXAMPLES
370
514
  $ kalshitools markets show TICKER --json
371
515
  ```
372
516
 
373
- _See code: [src/commands/markets/show.ts](https://github.com/kalshitools/kalshitools/blob/v1.0.3/src/commands/markets/show.ts)_
517
+ _See code: [src/commands/markets/show.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/markets/show.ts)_
374
518
 
375
519
  ## `kalshitools orders cancel ORDERID`
376
520
 
@@ -398,7 +542,7 @@ EXAMPLES
398
542
  $ kalshitools orders cancel ORDER_ID --json
399
543
  ```
400
544
 
401
- _See code: [src/commands/orders/cancel.ts](https://github.com/kalshitools/kalshitools/blob/v1.0.3/src/commands/orders/cancel.ts)_
545
+ _See code: [src/commands/orders/cancel.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/orders/cancel.ts)_
402
546
 
403
547
  ## `kalshitools orders create`
404
548
 
@@ -436,7 +580,7 @@ EXAMPLES
436
580
  $ kalshitools orders create --ticker TICKER --action buy --side yes --quantity 100 --dry-run
437
581
  ```
438
582
 
439
- _See code: [src/commands/orders/create.ts](https://github.com/kalshitools/kalshitools/blob/v1.0.3/src/commands/orders/create.ts)_
583
+ _See code: [src/commands/orders/create.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/orders/create.ts)_
440
584
 
441
585
  ## `kalshitools orders list`
442
586
 
@@ -467,7 +611,7 @@ EXAMPLES
467
611
  $ kalshitools orders list --json
468
612
  ```
469
613
 
470
- _See code: [src/commands/orders/list.ts](https://github.com/kalshitools/kalshitools/blob/v1.0.3/src/commands/orders/list.ts)_
614
+ _See code: [src/commands/orders/list.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/orders/list.ts)_
471
615
 
472
616
  ## `kalshitools plugins`
473
617
 
@@ -759,6 +903,35 @@ DESCRIPTION
759
903
 
760
904
  _See code: [@oclif/plugin-plugins](https://github.com/oclif/plugin-plugins/blob/v5.4.56/src/commands/plugins/update.ts)_
761
905
 
906
+ ## `kalshitools portfolio analytics`
907
+
908
+ Comprehensive portfolio performance analytics
909
+
910
+ ```
911
+ USAGE
912
+ $ kalshitools portfolio analytics [--json] [--max-ts <value>] [--min-ts <value>] [--period <value>]
913
+
914
+ FLAGS
915
+ --json Output in JSON format
916
+ --max-ts=<value> End date for analysis (ISO-8601 format)
917
+ --min-ts=<value> Start date for analysis (ISO-8601 format)
918
+ --period=<value> Analysis period in days (overridden by min-ts/max-ts)
919
+
920
+ DESCRIPTION
921
+ Comprehensive portfolio performance analytics
922
+
923
+ EXAMPLES
924
+ $ kalshitools portfolio analytics
925
+
926
+ $ kalshitools portfolio analytics --period 7
927
+
928
+ $ kalshitools portfolio analytics --min-ts 2026-01-01T00:00:00Z
929
+
930
+ $ kalshitools portfolio analytics --json
931
+ ```
932
+
933
+ _See code: [src/commands/portfolio/analytics.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/portfolio/analytics.ts)_
934
+
762
935
  ## `kalshitools portfolio balance`
763
936
 
764
937
  View account balance
@@ -779,7 +952,7 @@ EXAMPLES
779
952
  $ kalshitools portfolio balance --json
780
953
  ```
781
954
 
782
- _See code: [src/commands/portfolio/balance.ts](https://github.com/kalshitools/kalshitools/blob/v1.0.3/src/commands/portfolio/balance.ts)_
955
+ _See code: [src/commands/portfolio/balance.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/portfolio/balance.ts)_
783
956
 
784
957
  ## `kalshitools portfolio fills`
785
958
 
@@ -807,7 +980,42 @@ EXAMPLES
807
980
  $ kalshitools portfolio fills --json
808
981
  ```
809
982
 
810
- _See code: [src/commands/portfolio/fills.ts](https://github.com/kalshitools/kalshitools/blob/v1.0.3/src/commands/portfolio/fills.ts)_
983
+ _See code: [src/commands/portfolio/fills.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/portfolio/fills.ts)_
984
+
985
+ ## `kalshitools portfolio history`
986
+
987
+ Historical performance analysis with time-series data
988
+
989
+ ```
990
+ USAGE
991
+ $ kalshitools portfolio history [--json] [--group-by day|week|month] [--limit <value>] [--max-ts <value>] [--min-ts
992
+ <value>] [--ticker <value>]
993
+
994
+ FLAGS
995
+ --group-by=<option> [default: day] Time aggregation level
996
+ <options: day|week|month>
997
+ --json Output in JSON format
998
+ --limit=<value> [default: 500] Maximum fills to fetch (handles pagination)
999
+ --max-ts=<value> End date (ISO-8601 format)
1000
+ --min-ts=<value> Start date (ISO-8601 format)
1001
+ --ticker=<value> Filter by specific market ticker
1002
+
1003
+ DESCRIPTION
1004
+ Historical performance analysis with time-series data
1005
+
1006
+ EXAMPLES
1007
+ $ kalshitools portfolio history
1008
+
1009
+ $ kalshitools portfolio history --ticker MARKET-A
1010
+
1011
+ $ kalshitools portfolio history --group-by week
1012
+
1013
+ $ kalshitools portfolio history --min-ts 2026-01-01T00:00:00Z --max-ts 2026-02-01T00:00:00Z
1014
+
1015
+ $ kalshitools portfolio history --json
1016
+ ```
1017
+
1018
+ _See code: [src/commands/portfolio/history.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/portfolio/history.ts)_
811
1019
 
812
1020
  ## `kalshitools portfolio positions`
813
1021
 
@@ -835,5 +1043,34 @@ EXAMPLES
835
1043
  $ kalshitools portfolio positions --json
836
1044
  ```
837
1045
 
838
- _See code: [src/commands/portfolio/positions.ts](https://github.com/kalshitools/kalshitools/blob/v1.0.3/src/commands/portfolio/positions.ts)_
1046
+ _See code: [src/commands/portfolio/positions.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/portfolio/positions.ts)_
1047
+
1048
+ ## `kalshitools portfolio risk`
1049
+
1050
+ Real-time portfolio risk analysis and warnings
1051
+
1052
+ ```
1053
+ USAGE
1054
+ $ kalshitools portfolio risk [--json] [--group-by event|series|ticker] [--threshold <value>]
1055
+
1056
+ FLAGS
1057
+ --group-by=<option> [default: ticker] Grouping for concentration analysis
1058
+ <options: event|series|ticker>
1059
+ --json Output in JSON format
1060
+ --threshold=<value> [default: 20] Alert threshold for position concentration (%)
1061
+
1062
+ DESCRIPTION
1063
+ Real-time portfolio risk analysis and warnings
1064
+
1065
+ EXAMPLES
1066
+ $ kalshitools portfolio risk
1067
+
1068
+ $ kalshitools portfolio risk --threshold 15
1069
+
1070
+ $ kalshitools portfolio risk --group-by event
1071
+
1072
+ $ kalshitools portfolio risk --json
1073
+ ```
1074
+
1075
+ _See code: [src/commands/portfolio/risk.ts](https://github.com/kalshitools/kalshitools/blob/v1.1.0/src/commands/portfolio/risk.ts)_
839
1076
  <!-- commandsstop -->
@@ -0,0 +1,18 @@
1
+ import { BaseCommand } from '../../lib/base-command.js';
2
+ export default class MarketsScan extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ depth: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'event-ticker': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
9
+ 'max-spread': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
+ 'min-liquidity': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
11
+ 'min-spread': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
+ 'min-volume': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
13
+ 'series-ticker': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
14
+ 'sort-by': import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
15
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
+ };
17
+ run(): Promise<void>;
18
+ }
@@ -0,0 +1,237 @@
1
+ import { Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { BaseCommand } from '../../lib/base-command.js';
4
+ import { createClientFromConfig } from '../../lib/kalshi/index.js';
5
+ import { logger } from '../../lib/logger.js';
6
+ import { calculateOrderbookLiquidity, calculateSpread, classifyOpportunity, filterMarketsByCriteria, generateOpportunityReason, scoreMarket, } from '../../lib/scanner.js';
7
+ export default class MarketsScan extends BaseCommand {
8
+ static description = 'Scan markets for trading opportunities based on liquidity and spreads';
9
+ static examples = [
10
+ '<%= config.bin %> <%= command.id %>',
11
+ '<%= config.bin %> <%= command.id %> --min-liquidity 200 --min-spread 0.05',
12
+ '<%= config.bin %> <%= command.id %> --event-ticker EVENT-2024',
13
+ '<%= config.bin %> <%= command.id %> --sort-by liquidity --limit 20',
14
+ '<%= config.bin %> <%= command.id %> --json',
15
+ ];
16
+ static flags = {
17
+ ...BaseCommand.baseFlags,
18
+ 'depth': Flags.integer({
19
+ default: 5,
20
+ description: 'Orderbook depth to fetch for analysis',
21
+ }),
22
+ 'event-ticker': Flags.string({
23
+ description: 'Filter to specific event',
24
+ }),
25
+ 'limit': Flags.integer({
26
+ default: 50,
27
+ description: 'Maximum markets to scan',
28
+ }),
29
+ 'max-spread': Flags.string({
30
+ default: '0.30',
31
+ description: 'Maximum spread in cents (e.g., 0.30 = 30 cents)',
32
+ }),
33
+ 'min-liquidity': Flags.string({
34
+ default: '100',
35
+ description: 'Minimum orderbook depth in dollars',
36
+ }),
37
+ 'min-spread': Flags.string({
38
+ default: '0.03',
39
+ description: 'Minimum spread in cents (e.g., 0.03 = 3 cents)',
40
+ }),
41
+ 'min-volume': Flags.string({
42
+ default: '500',
43
+ description: 'Minimum 24h volume in dollars',
44
+ }),
45
+ 'series-ticker': Flags.string({
46
+ description: 'Filter to specific series',
47
+ }),
48
+ 'sort-by': Flags.string({
49
+ default: 'score',
50
+ description: 'Ranking criteria',
51
+ options: ['liquidity', 'score', 'spread', 'volume'],
52
+ }),
53
+ };
54
+ async run() {
55
+ const { flags } = await this.parse(MarketsScan);
56
+ try {
57
+ const client = createClientFromConfig();
58
+ // Parse numeric flags
59
+ const minLiquidity = Number.parseFloat(flags['min-liquidity']);
60
+ const minSpread = Number.parseFloat(flags['min-spread']);
61
+ const maxSpread = Number.parseFloat(flags['max-spread']);
62
+ const minVolume = Number.parseFloat(flags['min-volume']);
63
+ if (!this.formatter.isJSONMode()) {
64
+ this.log(chalk.cyan.bold('Scanning Markets for Opportunities...'));
65
+ this.log();
66
+ }
67
+ // Fetch active markets
68
+ const marketsResult = await client.getMarkets({
69
+ event_ticker: flags['event-ticker'],
70
+ limit: flags.limit,
71
+ series_ticker: flags['series-ticker'],
72
+ status: 'active',
73
+ });
74
+ const { markets } = marketsResult;
75
+ if (markets.length === 0) {
76
+ if (this.formatter.isJSONMode()) {
77
+ this.formatter.success({
78
+ markets: [],
79
+ opportunities: 0,
80
+ scanned: 0,
81
+ stats: {
82
+ avg_liquidity: 0,
83
+ avg_spread: 0,
84
+ avg_volume_24h: 0,
85
+ },
86
+ });
87
+ }
88
+ else {
89
+ this.log(chalk.yellow('No active markets found'));
90
+ }
91
+ return;
92
+ }
93
+ if (!this.formatter.isJSONMode()) {
94
+ this.log(`Found ${markets.length} active markets. Fetching orderbooks...`);
95
+ this.log(chalk.gray('(This may take a few seconds due to rate limiting)'));
96
+ this.log();
97
+ }
98
+ // Fetch orderbooks for all markets (rate limited automatically by client)
99
+ const marketData = [];
100
+ for (const market of markets) {
101
+ try {
102
+ const orderbook = await client.getOrderBook(market.ticker, flags.depth);
103
+ const liquidity = calculateOrderbookLiquidity(orderbook);
104
+ const spreadData = calculateSpread(orderbook);
105
+ const volume = market.volume_24h || market.volume || 0;
106
+ const score = scoreMarket(market, orderbook);
107
+ marketData.push({
108
+ liquidity: liquidity.total,
109
+ market,
110
+ orderbook,
111
+ score,
112
+ spread: spreadData ? spreadData.spreadCents : null,
113
+ volume,
114
+ });
115
+ }
116
+ catch (error) {
117
+ logger.debug({ error, ticker: market.ticker }, 'Failed to fetch orderbook');
118
+ // Skip markets with orderbook fetch errors
119
+ continue;
120
+ }
121
+ }
122
+ // Filter markets by criteria
123
+ const filtered = filterMarketsByCriteria(marketData, {
124
+ maxSpread,
125
+ minLiquidity,
126
+ minSpread,
127
+ minVolume,
128
+ });
129
+ // Sort markets
130
+ const sorted = [...filtered];
131
+ if (flags['sort-by'] === 'liquidity') {
132
+ sorted.sort((a, b) => b.liquidity - a.liquidity);
133
+ }
134
+ else if (flags['sort-by'] === 'spread') {
135
+ sorted.sort((a, b) => (b.spread || 0) - (a.spread || 0));
136
+ }
137
+ else if (flags['sort-by'] === 'volume') {
138
+ sorted.sort((a, b) => b.volume - a.volume);
139
+ }
140
+ else {
141
+ // Default to score
142
+ sorted.sort((a, b) => b.score - a.score);
143
+ }
144
+ // Calculate statistics
145
+ const avgLiquidity = filtered.length > 0
146
+ ? filtered.reduce((sum, m) => sum + m.liquidity, 0) / filtered.length
147
+ : 0;
148
+ const avgSpread = filtered.length > 0
149
+ ? filtered.filter(m => m.spread !== null).reduce((sum, m) => sum + (m.spread || 0), 0) / filtered.filter(m => m.spread !== null).length
150
+ : 0;
151
+ const avgVolume24h = filtered.length > 0
152
+ ? filtered.reduce((sum, m) => sum + m.volume, 0) / filtered.length
153
+ : 0;
154
+ if (this.formatter.isJSONMode()) {
155
+ const opportunities = sorted.map(m => {
156
+ const spreadData = m.orderbook ? calculateSpread(m.orderbook) : null;
157
+ const liquidityData = m.orderbook ? calculateOrderbookLiquidity(m.orderbook) : { noTotal: 0, total: 0, yesTotal: 0 };
158
+ const opportunityType = classifyOpportunity(m.spread || 0, m.liquidity, m.volume);
159
+ return {
160
+ liquidity: {
161
+ no_total: liquidityData.noTotal,
162
+ total: liquidityData.total,
163
+ yes_total: liquidityData.yesTotal,
164
+ },
165
+ opportunity_type: opportunityType,
166
+ reason: generateOpportunityReason(opportunityType, m.spread || 0, m.liquidity, m.volume),
167
+ score: m.score,
168
+ spread: spreadData ? {
169
+ spread_cents: spreadData.spreadCents,
170
+ spread_pct: spreadData.spreadPct,
171
+ yes_ask: spreadData.yesAsk,
172
+ yes_bid: spreadData.yesBid,
173
+ } : null,
174
+ ticker: m.market.ticker,
175
+ title: m.market.title,
176
+ volume: {
177
+ volume_24h: m.volume,
178
+ },
179
+ };
180
+ });
181
+ this.formatter.success({
182
+ markets: opportunities,
183
+ opportunities: filtered.length,
184
+ scanned: markets.length,
185
+ stats: {
186
+ avg_liquidity: avgLiquidity,
187
+ avg_spread: avgSpread,
188
+ avg_volume_24h: avgVolume24h,
189
+ },
190
+ });
191
+ }
192
+ else {
193
+ // Human-readable output
194
+ this.log(chalk.cyan.bold('Market Opportunities'));
195
+ this.log();
196
+ this.log(chalk.yellow('Summary:'));
197
+ this.log(` Markets Scanned: ${markets.length}`);
198
+ this.log(` Opportunities Found: ${filtered.length}`);
199
+ this.log(` Avg Liquidity: ${chalk.cyan('$' + avgLiquidity.toFixed(2))}`);
200
+ this.log(` Avg Spread: ${chalk.cyan(avgSpread.toFixed(3))} cents`);
201
+ this.log(` Avg 24h Volume: ${chalk.cyan('$' + avgVolume24h.toFixed(2))}`);
202
+ this.log();
203
+ if (sorted.length > 0) {
204
+ this.log(chalk.yellow('Top Opportunities:'));
205
+ const rows = sorted.slice(0, 20).map(m => {
206
+ const spreadData = m.orderbook ? calculateSpread(m.orderbook) : null;
207
+ return [
208
+ m.market.ticker,
209
+ m.score.toString(),
210
+ spreadData ? spreadData.spreadCents.toFixed(3) : 'N/A',
211
+ '$' + m.liquidity.toFixed(0),
212
+ '$' + m.volume.toFixed(0),
213
+ m.market.title.length > 40 ? m.market.title.slice(0, 37) + '...' : m.market.title,
214
+ ];
215
+ });
216
+ this.formatter.outputTable(['Ticker', 'Score', 'Spread', 'Liquidity', '24h Vol', 'Title'], rows);
217
+ if (sorted.length > 20) {
218
+ this.log();
219
+ this.log(chalk.gray(` ... showing top 20 of ${sorted.length} opportunities`));
220
+ }
221
+ }
222
+ else {
223
+ this.log(chalk.yellow('No opportunities found matching criteria'));
224
+ }
225
+ this.log();
226
+ this.log(chalk.gray('Tip: Use --json for full data including orderbook details'));
227
+ }
228
+ logger.info({
229
+ filtered: filtered.length,
230
+ scanned: markets.length,
231
+ }, 'Market scan completed');
232
+ }
233
+ catch (error) {
234
+ throw error;
235
+ }
236
+ }
237
+ }
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../lib/base-command.js';
2
+ export default class PortfolioAnalytics extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ 'max-ts': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 'min-ts': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ period: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }