@gaberoo/kalshitools 1.0.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 (57) hide show
  1. package/README.md +666 -0
  2. package/bin/dev.cmd +3 -0
  3. package/bin/dev.js +5 -0
  4. package/bin/run.cmd +3 -0
  5. package/bin/run.js +5 -0
  6. package/dist/commands/config/init.d.ts +13 -0
  7. package/dist/commands/config/init.js +89 -0
  8. package/dist/commands/config/show.d.ts +10 -0
  9. package/dist/commands/config/show.js +77 -0
  10. package/dist/commands/markets/list.d.ts +11 -0
  11. package/dist/commands/markets/list.js +64 -0
  12. package/dist/commands/markets/show.d.ts +13 -0
  13. package/dist/commands/markets/show.js +79 -0
  14. package/dist/commands/orders/cancel.d.ts +14 -0
  15. package/dist/commands/orders/cancel.js +129 -0
  16. package/dist/commands/orders/create.d.ts +19 -0
  17. package/dist/commands/orders/create.js +211 -0
  18. package/dist/commands/orders/list.d.ts +13 -0
  19. package/dist/commands/orders/list.js +92 -0
  20. package/dist/commands/portfolio/balance.d.ts +9 -0
  21. package/dist/commands/portfolio/balance.js +36 -0
  22. package/dist/commands/portfolio/fills.d.ts +11 -0
  23. package/dist/commands/portfolio/fills.js +80 -0
  24. package/dist/commands/portfolio/positions.d.ts +9 -0
  25. package/dist/commands/portfolio/positions.js +58 -0
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.js +1 -0
  28. package/dist/lib/base-command.d.ts +13 -0
  29. package/dist/lib/base-command.js +38 -0
  30. package/dist/lib/config/manager.d.ts +71 -0
  31. package/dist/lib/config/manager.js +137 -0
  32. package/dist/lib/config/schema.d.ts +175 -0
  33. package/dist/lib/config/schema.js +59 -0
  34. package/dist/lib/errors/base.d.ts +84 -0
  35. package/dist/lib/errors/base.js +106 -0
  36. package/dist/lib/kalshi/auth.d.ts +17 -0
  37. package/dist/lib/kalshi/auth.js +71 -0
  38. package/dist/lib/kalshi/client.d.ts +86 -0
  39. package/dist/lib/kalshi/client.js +228 -0
  40. package/dist/lib/kalshi/index.d.ts +8 -0
  41. package/dist/lib/kalshi/index.js +19 -0
  42. package/dist/lib/kalshi/types.d.ts +155 -0
  43. package/dist/lib/kalshi/types.js +4 -0
  44. package/dist/lib/logger.d.ts +9 -0
  45. package/dist/lib/logger.js +41 -0
  46. package/dist/lib/output/formatter.d.ts +69 -0
  47. package/dist/lib/output/formatter.js +111 -0
  48. package/dist/lib/retry.d.ts +18 -0
  49. package/dist/lib/retry.js +81 -0
  50. package/dist/lib/sanitize.d.ts +28 -0
  51. package/dist/lib/sanitize.js +124 -0
  52. package/dist/lib/shutdown.d.ts +43 -0
  53. package/dist/lib/shutdown.js +106 -0
  54. package/dist/lib/validation.d.ts +37 -0
  55. package/dist/lib/validation.js +120 -0
  56. package/oclif.manifest.json +520 -0
  57. package/package.json +98 -0
@@ -0,0 +1,211 @@
1
+ import { Flags } from '@oclif/core';
2
+ import chalk from 'chalk';
3
+ import { BaseCommand } from '../../lib/base-command.js';
4
+ import { getConfig } from '../../lib/config/manager.js';
5
+ import { ValidationError } from '../../lib/errors/base.js';
6
+ import { createClientFromConfig } from '../../lib/kalshi/index.js';
7
+ import { logger } from '../../lib/logger.js';
8
+ import { estimateOrderCost, validateAction, validateOrderType, validatePrice, validateQuantity, validateSide, validateTicker, } from '../../lib/validation.js';
9
+ export default class OrdersCreate extends BaseCommand {
10
+ static description = 'Create a new order (buy or sell contracts)';
11
+ static examples = [
12
+ '<%= config.bin %> <%= command.id %> --ticker TICKER --action buy --side yes --quantity 10 --type market',
13
+ '<%= config.bin %> <%= command.id %> --ticker TICKER --action buy --side yes --quantity 10 --type limit --price 0.65',
14
+ '<%= config.bin %> <%= command.id %> --ticker TICKER --action sell --side no --quantity 5 --type market --yes',
15
+ '<%= config.bin %> <%= command.id %> --ticker TICKER --action buy --side yes --quantity 100 --dry-run',
16
+ ];
17
+ static flags = {
18
+ ...BaseCommand.baseFlags,
19
+ ticker: Flags.string({
20
+ char: 't',
21
+ description: 'Market ticker symbol',
22
+ required: true,
23
+ }),
24
+ action: Flags.string({
25
+ char: 'a',
26
+ description: 'Order action (buy or sell)',
27
+ options: ['buy', 'sell'],
28
+ required: true,
29
+ }),
30
+ side: Flags.string({
31
+ char: 's',
32
+ description: 'Contract side (yes or no)',
33
+ options: ['yes', 'no'],
34
+ required: true,
35
+ }),
36
+ quantity: Flags.integer({
37
+ char: 'q',
38
+ description: 'Number of contracts',
39
+ required: true,
40
+ }),
41
+ type: Flags.string({
42
+ description: 'Order type (market or limit)',
43
+ options: ['market', 'limit'],
44
+ default: 'market',
45
+ }),
46
+ price: Flags.string({
47
+ char: 'p',
48
+ description: 'Limit price (required for limit orders, 0.01-0.99)',
49
+ }),
50
+ yes: Flags.boolean({
51
+ char: 'y',
52
+ description: 'Skip confirmation prompt',
53
+ default: false,
54
+ }),
55
+ 'dry-run': Flags.boolean({
56
+ description: 'Simulate the order without actually placing it',
57
+ default: false,
58
+ }),
59
+ };
60
+ async run() {
61
+ const { flags } = await this.parse(OrdersCreate);
62
+ const configManager = getConfig();
63
+ const tradingConfig = configManager.getTradingConfig();
64
+ const environment = configManager.getEnvironment();
65
+ try {
66
+ // Validate inputs
67
+ validateTicker(flags.ticker);
68
+ const action = validateAction(flags.action);
69
+ const side = validateSide(flags.side);
70
+ const orderType = validateOrderType(flags.type);
71
+ validateQuantity(flags.quantity, tradingConfig.maxOrderSize);
72
+ let price;
73
+ if (orderType === 'limit') {
74
+ if (!flags.price) {
75
+ throw new ValidationError('Price is required for limit orders');
76
+ }
77
+ price = Number.parseFloat(flags.price);
78
+ if (Number.isNaN(price)) {
79
+ throw new ValidationError('Price must be a valid number', { price: flags.price });
80
+ }
81
+ validatePrice(price);
82
+ }
83
+ // Estimate cost
84
+ const costEstimate = estimateOrderCost(side, action, flags.quantity, price);
85
+ // Build order request
86
+ const orderRequest = {
87
+ ticker: flags.ticker,
88
+ side,
89
+ action,
90
+ count: flags.quantity,
91
+ type: orderType,
92
+ };
93
+ if (orderType === 'limit' && price) {
94
+ if (side === 'yes') {
95
+ orderRequest.yes_price = price;
96
+ }
97
+ else {
98
+ orderRequest.no_price = price;
99
+ }
100
+ }
101
+ // Display order summary
102
+ if (!this.formatter.isJSONMode()) {
103
+ this.log();
104
+ this.log(chalk.cyan.bold('Order Summary'));
105
+ this.log(chalk.gray('─'.repeat(50)));
106
+ this.log(`${chalk.cyan('Environment:')} ${environment === 'demo' ? chalk.yellow('DEMO') : chalk.red('PRODUCTION')}`);
107
+ this.log(`${chalk.cyan('Ticker:')} ${flags.ticker}`);
108
+ this.log(`${chalk.cyan('Action:')} ${action.toUpperCase()}`);
109
+ this.log(`${chalk.cyan('Side:')} ${side.toUpperCase()}`);
110
+ this.log(`${chalk.cyan('Quantity:')} ${flags.quantity}`);
111
+ this.log(`${chalk.cyan('Type:')} ${orderType.toUpperCase()}`);
112
+ if (price) {
113
+ this.log(`${chalk.cyan('Price:')} ${price.toFixed(2)}`);
114
+ }
115
+ this.log();
116
+ if (action === 'buy') {
117
+ this.log(`${chalk.cyan('Estimated Cost:')} $${costEstimate.estimate.toFixed(2)}`);
118
+ if (orderType === 'market') {
119
+ this.log(chalk.gray(` (Range: $${costEstimate.min.toFixed(2)} - $${costEstimate.max.toFixed(2)})`));
120
+ }
121
+ }
122
+ else {
123
+ this.log(`${chalk.cyan('Estimated Proceeds:')} $${costEstimate.estimate.toFixed(2)}`);
124
+ if (orderType === 'market') {
125
+ this.log(chalk.gray(` (Range: $${costEstimate.min.toFixed(2)} - $${costEstimate.max.toFixed(2)})`));
126
+ }
127
+ }
128
+ this.log(chalk.gray('─'.repeat(50)));
129
+ this.log();
130
+ }
131
+ // Dry run mode
132
+ if (flags['dry-run']) {
133
+ if (this.formatter.isJSONMode()) {
134
+ this.formatter.success({
135
+ dryRun: true,
136
+ order: orderRequest,
137
+ costEstimate,
138
+ });
139
+ }
140
+ else {
141
+ this.log(chalk.yellow('DRY RUN - Order not placed'));
142
+ this.log(chalk.gray('Order would be submitted with the parameters above'));
143
+ }
144
+ logger.info({ orderRequest, dryRun: true }, 'Dry run order');
145
+ return;
146
+ }
147
+ // Confirmation prompt (unless --yes flag or JSON mode)
148
+ if (tradingConfig.confirmations && !flags.yes && !this.formatter.isJSONMode()) {
149
+ const confirmed = await this.confirm(environment === 'production'
150
+ ? chalk.red.bold('⚠️ This will place a REAL order with REAL money. Continue?')
151
+ : 'Place this order?');
152
+ if (!confirmed) {
153
+ this.log(chalk.yellow('Order cancelled'));
154
+ return;
155
+ }
156
+ }
157
+ // Create API client and place order
158
+ const client = createClientFromConfig();
159
+ const order = await client.createOrder(orderRequest);
160
+ if (this.formatter.isJSONMode()) {
161
+ this.formatter.success(order);
162
+ }
163
+ else {
164
+ this.log(chalk.green('✓ Order placed successfully!'));
165
+ this.log();
166
+ this.log(`${chalk.cyan('Order ID:')} ${order.order_id}`);
167
+ this.log(`${chalk.cyan('Status:')} ${this.formatStatus(order.status)}`);
168
+ this.log(`${chalk.cyan('Filled:')} ${order.filled_count}/${order.count}`);
169
+ if (order.avg_fill_price) {
170
+ this.log(`${chalk.cyan('Avg Fill Price:')} ${order.avg_fill_price.toFixed(2)}`);
171
+ }
172
+ this.log();
173
+ if (order.status === 'executed') {
174
+ this.log(chalk.green('✓ Order fully executed'));
175
+ }
176
+ else if (order.status === 'resting') {
177
+ this.log(chalk.yellow('⏳ Order resting on the book'));
178
+ }
179
+ }
180
+ logger.info({
181
+ orderId: order.order_id,
182
+ status: order.status,
183
+ ticker: flags.ticker,
184
+ }, 'Order created successfully');
185
+ }
186
+ catch (error) {
187
+ throw error;
188
+ }
189
+ }
190
+ formatStatus(status) {
191
+ const colors = {
192
+ executed: chalk.green,
193
+ pending: chalk.yellow,
194
+ resting: chalk.cyan,
195
+ canceled: chalk.gray,
196
+ expired: chalk.red,
197
+ };
198
+ const colorFn = colors[status] || chalk.white;
199
+ return colorFn(status);
200
+ }
201
+ async confirm(message) {
202
+ const readline = await import('node:readline/promises');
203
+ const rl = readline.createInterface({
204
+ input: process.stdin,
205
+ output: process.stdout,
206
+ });
207
+ const answer = await rl.question(`${message} (y/N): `);
208
+ rl.close();
209
+ return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
210
+ }
211
+ }
@@ -0,0 +1,13 @@
1
+ import { BaseCommand } from '../../lib/base-command.js';
2
+ export default class OrdersList extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ 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
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ private formatStatus;
13
+ }
@@ -0,0 +1,92 @@
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
+ export default class OrdersList extends BaseCommand {
7
+ static description = 'List orders';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %>',
10
+ '<%= config.bin %> <%= command.id %> --status resting',
11
+ '<%= config.bin %> <%= command.id %> --ticker TICKER',
12
+ '<%= config.bin %> <%= command.id %> --json',
13
+ ];
14
+ static flags = {
15
+ ...BaseCommand.baseFlags,
16
+ status: Flags.string({
17
+ description: 'Filter by order status',
18
+ options: ['pending', 'resting', 'canceled', 'executed', 'expired'],
19
+ }),
20
+ ticker: Flags.string({
21
+ description: 'Filter by ticker',
22
+ }),
23
+ limit: Flags.integer({
24
+ description: 'Maximum number of orders to return',
25
+ default: 50,
26
+ }),
27
+ };
28
+ async run() {
29
+ const { flags } = await this.parse(OrdersList);
30
+ try {
31
+ // Create API client
32
+ const client = createClientFromConfig();
33
+ // Fetch orders
34
+ const result = await client.getOrders({
35
+ status: flags.status,
36
+ ticker: flags.ticker,
37
+ limit: flags.limit,
38
+ });
39
+ const orders = result.orders;
40
+ if (this.formatter.isJSONMode()) {
41
+ this.formatter.success({ orders, cursor: result.cursor });
42
+ }
43
+ else {
44
+ if (orders.length === 0) {
45
+ this.log(chalk.yellow('No orders found'));
46
+ return;
47
+ }
48
+ this.log(chalk.cyan.bold(`Orders (${orders.length})`));
49
+ this.log();
50
+ const rows = orders.map((order) => [
51
+ order.order_id.slice(0, 8) + '...',
52
+ order.ticker,
53
+ this.formatStatus(order.status),
54
+ `${order.action.toUpperCase()} ${order.side.toUpperCase()}`,
55
+ `${order.filled_count}/${order.count}`,
56
+ order.yes_price?.toFixed(2) || order.no_price?.toFixed(2) || 'MKT',
57
+ new Date(order.created_time).toLocaleString(),
58
+ ]);
59
+ this.formatter.outputTable(['Order ID', 'Ticker', 'Status', 'Action', 'Filled', 'Price', 'Created'], rows);
60
+ if (result.cursor) {
61
+ this.log();
62
+ this.log(chalk.gray(`More results available. Use cursor: ${result.cursor}`));
63
+ }
64
+ // Summary statistics
65
+ const statusCounts = orders.reduce((acc, order) => {
66
+ acc[order.status] = (acc[order.status] || 0) + 1;
67
+ return acc;
68
+ }, {});
69
+ this.log();
70
+ this.log(chalk.cyan('Summary:'));
71
+ for (const [status, count] of Object.entries(statusCounts)) {
72
+ this.log(` ${this.formatStatus(status)}: ${count}`);
73
+ }
74
+ }
75
+ logger.info({ count: orders.length }, 'Orders fetched successfully');
76
+ }
77
+ catch (error) {
78
+ throw error;
79
+ }
80
+ }
81
+ formatStatus(status) {
82
+ const colors = {
83
+ executed: chalk.green,
84
+ pending: chalk.yellow,
85
+ resting: chalk.cyan,
86
+ canceled: chalk.gray,
87
+ expired: chalk.red,
88
+ };
89
+ const colorFn = colors[status] || chalk.white;
90
+ return colorFn(status);
91
+ }
92
+ }
@@ -0,0 +1,9 @@
1
+ import { BaseCommand } from '../../lib/base-command.js';
2
+ export default class PortfolioBalance extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,36 @@
1
+ import chalk from 'chalk';
2
+ import { BaseCommand } from '../../lib/base-command.js';
3
+ import { createClientFromConfig } from '../../lib/kalshi/index.js';
4
+ import { logger } from '../../lib/logger.js';
5
+ export default class PortfolioBalance extends BaseCommand {
6
+ static description = 'View account balance';
7
+ static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> --json'];
8
+ static flags = {
9
+ ...BaseCommand.baseFlags,
10
+ };
11
+ async run() {
12
+ await this.parse(PortfolioBalance);
13
+ try {
14
+ // Create API client from configuration
15
+ const client = createClientFromConfig();
16
+ // Fetch balance
17
+ const balance = await client.getBalance();
18
+ if (this.formatter.isJSONMode()) {
19
+ this.formatter.success(balance);
20
+ }
21
+ else {
22
+ this.log(chalk.cyan.bold('Account Balance'));
23
+ this.log();
24
+ this.log(`${chalk.cyan('Balance:')} ${chalk.green('$' + balance.balance.toFixed(2))}`);
25
+ this.log(`${chalk.cyan('Payout:')} ${chalk.yellow('$' + balance.payout.toFixed(2))}`);
26
+ this.log();
27
+ this.log(chalk.gray(`Total: $${(balance.balance + balance.payout).toFixed(2)}`));
28
+ }
29
+ logger.info({ balance: balance.balance, payout: balance.payout }, 'Balance fetched successfully');
30
+ }
31
+ catch (error) {
32
+ // Error will be handled by BaseCommand.catch()
33
+ throw error;
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,11 @@
1
+ import { BaseCommand } from '../../lib/base-command.js';
2
+ export default class PortfolioFills extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ ticker: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ limit: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
8
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ }
@@ -0,0 +1,80 @@
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
+ export default class PortfolioFills extends BaseCommand {
7
+ static description = 'View trade history (fills)';
8
+ static examples = [
9
+ '<%= config.bin %> <%= command.id %>',
10
+ '<%= config.bin %> <%= command.id %> --ticker TICKER',
11
+ '<%= config.bin %> <%= command.id %> --limit 20',
12
+ '<%= config.bin %> <%= command.id %> --json',
13
+ ];
14
+ static flags = {
15
+ ...BaseCommand.baseFlags,
16
+ ticker: Flags.string({
17
+ description: 'Filter by ticker',
18
+ }),
19
+ limit: Flags.integer({
20
+ description: 'Maximum number of fills to return',
21
+ default: 50,
22
+ }),
23
+ };
24
+ async run() {
25
+ const { flags } = await this.parse(PortfolioFills);
26
+ try {
27
+ // Create API client
28
+ const client = createClientFromConfig();
29
+ // Fetch fills
30
+ const result = await client.getFills({
31
+ ticker: flags.ticker,
32
+ limit: flags.limit,
33
+ });
34
+ const fills = result.fills;
35
+ if (this.formatter.isJSONMode()) {
36
+ this.formatter.success({ fills, cursor: result.cursor });
37
+ }
38
+ else {
39
+ if (fills.length === 0) {
40
+ this.log(chalk.yellow('No fills found'));
41
+ return;
42
+ }
43
+ this.log(chalk.cyan.bold(`Trade History (${fills.length} fills)`));
44
+ this.log();
45
+ const rows = fills.map((fill) => [
46
+ fill.ticker,
47
+ `${fill.action.toUpperCase()} ${fill.side.toUpperCase()}`,
48
+ fill.count.toString(),
49
+ fill.price.toFixed(2),
50
+ (fill.count * fill.price).toFixed(2),
51
+ fill.is_taker ? chalk.yellow('Taker') : chalk.green('Maker'),
52
+ new Date(fill.created_time).toLocaleString(),
53
+ ]);
54
+ this.formatter.outputTable(['Ticker', 'Action', 'Qty', 'Price', 'Total', 'Type', 'Time'], rows);
55
+ if (result.cursor) {
56
+ this.log();
57
+ this.log(chalk.gray(`More results available. Use cursor: ${result.cursor}`));
58
+ }
59
+ // Summary statistics
60
+ const totalVolume = fills.reduce((sum, fill) => sum + fill.count * fill.price, 0);
61
+ const buyVolume = fills
62
+ .filter((f) => f.action === 'buy')
63
+ .reduce((sum, fill) => sum + fill.count * fill.price, 0);
64
+ const sellVolume = fills
65
+ .filter((f) => f.action === 'sell')
66
+ .reduce((sum, fill) => sum + fill.count * fill.price, 0);
67
+ this.log();
68
+ this.log(chalk.cyan('Summary:'));
69
+ this.log(` Total Volume: ${chalk.yellow('$' + totalVolume.toFixed(2))}`);
70
+ this.log(` Buy Volume: ${chalk.green('$' + buyVolume.toFixed(2))}`);
71
+ this.log(` Sell Volume: ${chalk.red('$' + sellVolume.toFixed(2))}`);
72
+ this.log(` Net: ${chalk.yellow('$' + (sellVolume - buyVolume).toFixed(2))}`);
73
+ }
74
+ logger.info({ count: fills.length }, 'Fills fetched successfully');
75
+ }
76
+ catch (error) {
77
+ throw error;
78
+ }
79
+ }
80
+ }
@@ -0,0 +1,9 @@
1
+ import { BaseCommand } from '../../lib/base-command.js';
2
+ export default class PortfolioPositions extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static flags: {
6
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
7
+ };
8
+ run(): Promise<void>;
9
+ }
@@ -0,0 +1,58 @@
1
+ import chalk from 'chalk';
2
+ import { BaseCommand } from '../../lib/base-command.js';
3
+ import { createClientFromConfig } from '../../lib/kalshi/index.js';
4
+ import { logger } from '../../lib/logger.js';
5
+ export default class PortfolioPositions extends BaseCommand {
6
+ static description = 'View current positions with P&L';
7
+ static examples = [
8
+ '<%= config.bin %> <%= command.id %>',
9
+ '<%= config.bin %> <%= command.id %> --json',
10
+ ];
11
+ static flags = {
12
+ ...BaseCommand.baseFlags,
13
+ };
14
+ async run() {
15
+ await this.parse(PortfolioPositions);
16
+ try {
17
+ // Create API client from configuration
18
+ const client = createClientFromConfig();
19
+ // Fetch positions
20
+ const positions = await client.getPositions();
21
+ if (this.formatter.isJSONMode()) {
22
+ this.formatter.success(positions);
23
+ }
24
+ else {
25
+ if (positions.length === 0) {
26
+ this.log(chalk.yellow('No open positions'));
27
+ return;
28
+ }
29
+ this.log(chalk.cyan.bold('Current Positions'));
30
+ this.log();
31
+ const rows = positions.map((pos) => [
32
+ pos.ticker,
33
+ pos.position.toString(),
34
+ `$${pos.total_cost.toFixed(2)}`,
35
+ `$${pos.market_exposure.toFixed(2)}`,
36
+ pos.realized_pnl >= 0
37
+ ? chalk.green(`+$${pos.realized_pnl.toFixed(2)}`)
38
+ : chalk.red(`-$${Math.abs(pos.realized_pnl).toFixed(2)}`),
39
+ ]);
40
+ this.formatter.outputTable(['Ticker', 'Quantity', 'Cost', 'Exposure', 'P&L'], rows);
41
+ const totalCost = positions.reduce((sum, pos) => sum + pos.total_cost, 0);
42
+ const totalExposure = positions.reduce((sum, pos) => sum + pos.market_exposure, 0);
43
+ const totalPnL = positions.reduce((sum, pos) => sum + pos.realized_pnl, 0);
44
+ this.log();
45
+ this.log(chalk.cyan('Summary:'));
46
+ this.log(` Total Cost: ${chalk.yellow('$' + totalCost.toFixed(2))}`);
47
+ this.log(` Total Exposure: ${chalk.yellow('$' + totalExposure.toFixed(2))}`);
48
+ this.log(` Total P&L: ${totalPnL >= 0
49
+ ? chalk.green('+$' + totalPnL.toFixed(2))
50
+ : chalk.red('-$' + Math.abs(totalPnL).toFixed(2))}`);
51
+ }
52
+ logger.info({ count: positions.length }, 'Positions fetched successfully');
53
+ }
54
+ catch (error) {
55
+ throw error;
56
+ }
57
+ }
58
+ }
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
@@ -0,0 +1,13 @@
1
+ import { Command } from '@oclif/core';
2
+ import { OutputFormatter } from './output/formatter.js';
3
+ /**
4
+ * Base command class that all kalshitools commands extend
5
+ */
6
+ export declare abstract class BaseCommand extends Command {
7
+ protected formatter: OutputFormatter;
8
+ static baseFlags: {
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ init(): Promise<void>;
12
+ protected catch(error: Error): Promise<unknown>;
13
+ }
@@ -0,0 +1,38 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { KalshiToolsError } from './errors/base.js';
3
+ import { logger } from './logger.js';
4
+ import { OutputFormatter } from './output/formatter.js';
5
+ /**
6
+ * Base command class that all kalshitools commands extend
7
+ */
8
+ export class BaseCommand extends Command {
9
+ formatter;
10
+ static baseFlags = {
11
+ json: Flags.boolean({
12
+ description: 'Output in JSON format',
13
+ default: false,
14
+ }),
15
+ };
16
+ async init() {
17
+ await super.init();
18
+ const { flags } = await this.parse(this.constructor);
19
+ this.formatter = new OutputFormatter(flags.json, this.id);
20
+ }
21
+ async catch(error) {
22
+ logger.error({ error, command: this.id }, 'Command failed');
23
+ if (error instanceof KalshiToolsError) {
24
+ if (this.formatter?.isJSONMode()) {
25
+ this.formatter.error(error.code, error.message, error.details);
26
+ }
27
+ else {
28
+ this.error(error.message, { exit: error.exitCode });
29
+ }
30
+ this.exit(error.exitCode);
31
+ }
32
+ // Handle unexpected errors
33
+ if (this.formatter?.isJSONMode()) {
34
+ this.formatter.error('GENERAL_ERROR', error.message);
35
+ }
36
+ throw error;
37
+ }
38
+ }
@@ -0,0 +1,71 @@
1
+ import { type Config, type ApiEnvConfig } from './schema.js';
2
+ /**
3
+ * Configuration manager for kalshitools
4
+ */
5
+ export declare class ConfigManager {
6
+ private store;
7
+ private static instance;
8
+ constructor();
9
+ /**
10
+ * Get singleton instance
11
+ */
12
+ static getInstance(): ConfigManager;
13
+ /**
14
+ * Get the full configuration
15
+ */
16
+ getConfig(): Config;
17
+ /**
18
+ * Get the current environment (demo or production)
19
+ */
20
+ getEnvironment(): 'demo' | 'production';
21
+ /**
22
+ * Set the environment
23
+ */
24
+ setEnvironment(env: 'demo' | 'production'): void;
25
+ /**
26
+ * Get API configuration for the current environment
27
+ */
28
+ getApiConfig(): ApiEnvConfig;
29
+ /**
30
+ * Read private key from file
31
+ */
32
+ readPrivateKey(): string;
33
+ /**
34
+ * Set API configuration for an environment
35
+ */
36
+ setApiConfig(env: 'demo' | 'production', config: Partial<ApiEnvConfig>): void;
37
+ /**
38
+ * Get output configuration
39
+ */
40
+ getOutputConfig(): {
41
+ defaultFormat: "json" | "table";
42
+ colors: boolean;
43
+ };
44
+ /**
45
+ * Get trading configuration
46
+ */
47
+ getTradingConfig(): {
48
+ confirmations: boolean;
49
+ maxOrderSize: number;
50
+ };
51
+ /**
52
+ * Set a configuration value
53
+ */
54
+ set<K extends keyof Config>(key: K, value: Config[K]): void;
55
+ /**
56
+ * Get the configuration file path
57
+ */
58
+ getConfigPath(): string;
59
+ /**
60
+ * Reset configuration to defaults
61
+ */
62
+ reset(): void;
63
+ /**
64
+ * Check if configuration is initialized
65
+ */
66
+ isConfigured(): boolean;
67
+ }
68
+ /**
69
+ * Get the global configuration manager instance
70
+ */
71
+ export declare function getConfig(): ConfigManager;