1use std::path::PathBuf;
2
3use clap::{Parser, Subcommand};
4
5use crate::disclaimer::HELP_DISCLAIMER;
6use crate::mode::CliMode;
7use crate::output::OutputFormat;
8
9#[derive(Debug, Parser)]
10#[command(
11 name = "schwab",
12 version,
13 about = "Agent-first CLI for Charles Schwab Trader API (experimental — use at your own risk)",
14 long_about = "Schwab Trader API CLI (Accounts and Trading Production).\n\n\
15 ⚠️ EXPERIMENTAL — USE AT YOUR OWN RISK. Can submit real trades.\n\
16 Run `schwab disclaimer show` and `schwab disclaimer accept --yes` before live trading.\n\n\
17 AGENTS: Prefer --mode agent (default). Discover commands with:\n\
18 schwab --help --json\n\
19 schwab capabilities --json\n\
20 schwab env schema --json\n\
21 schwab instructions --json\n\n\
22 HUMANS: Use --mode human for guided prompts when arguments are omitted.",
23 after_help = HELP_DISCLAIMER
24)]
25pub struct Cli {
26 #[arg(long, env = "SCHWAB_MODE", default_value = "agent")]
28 pub mode: CliMode,
29
30 #[arg(long, env = "SCHWAB_OUTPUT", default_value = "pretty")]
32 pub output: OutputFormat,
33
34 #[arg(long, short = 'j', global = true)]
36 pub json: bool,
37
38 #[arg(long, global = true)]
40 pub md: bool,
41
42 #[arg(long, global = true)]
44 pub yes: bool,
45
46 #[arg(long, global = true)]
48 pub dry_run: bool,
49
50 #[arg(long, global = true)]
52 pub simulate: bool,
53
54 #[arg(long, global = true)]
56 pub trust: bool,
57
58 #[arg(long, global = true)]
60 pub help_json: bool,
61
62 #[command(subcommand)]
63 pub command: Option<Commands>,
64}
65
66#[derive(Debug, Subcommand)]
67pub enum Commands {
68 Capabilities,
70
71 Env {
73 #[command(subcommand)]
74 command: EnvCommands,
75 },
76
77 Instructions,
79
80 Disclaimer {
82 #[command(subcommand)]
83 command: DisclaimerCommands,
84 },
85
86 Auth {
88 #[command(subcommand)]
89 command: AuthCommands,
90 },
91
92 Accounts {
94 #[command(subcommand)]
95 command: AccountsCommands,
96 },
97
98 Orders {
100 #[command(subcommand)]
101 command: OrdersCommands,
102 },
103
104 Transactions {
106 #[command(subcommand)]
107 command: TransactionsCommands,
108 },
109
110 User {
112 #[command(subcommand)]
113 command: UserCommands,
114 },
115
116 Portfolio {
118 #[command(subcommand)]
119 command: PortfolioCommands,
120 },
121
122 Trade {
124 #[command(subcommand)]
125 command: TradeCommands,
126 },
127
128 Safety {
130 #[command(subcommand)]
131 command: SafetyCommands,
132 },
133
134 Plan {
136 #[command(subcommand)]
137 command: PlanCommands,
138 },
139
140 Market {
142 #[command(subcommand)]
143 command: MarketCommands,
144 },
145
146 Options {
148 #[command(subcommand)]
149 command: OptionsCommands,
150 },
151
152 Agent {
154 #[command(subcommand)]
155 command: AgentCommands,
156 },
157
158 Dashboard {
160 file: Option<PathBuf>,
162 },
163
164 Watch {
166 file: Option<PathBuf>,
168 #[arg(long)]
170 monitor_only: bool,
171 },
172
173 Rules {
175 #[command(subcommand)]
176 command: RulesCommands,
177 },
178}
179
180#[derive(Debug, Subcommand)]
181pub enum EnvCommands {
182 Schema,
184}
185
186#[derive(Debug, Subcommand)]
187pub enum AuthCommands {
188 Login {
190 #[arg(long)]
192 code: Option<String>,
193 },
194 Status,
196 Refresh,
198 Logout,
200}
201
202#[derive(Debug, Subcommand)]
203pub enum AccountsCommands {
204 Numbers,
206 List {
208 #[arg(long)]
209 fields: Option<String>,
210 },
211 Get {
213 account_number: String,
214 #[arg(long)]
215 fields: Option<String>,
216 },
217}
218
219#[derive(Debug, Subcommand)]
220pub enum OrdersCommands {
221 Schema,
223 Validate {
225 #[arg(long)]
227 order: String,
228 #[arg(long)]
230 account_number: Option<String>,
231 },
232 List {
234 account_number: String,
235 #[arg(long)]
236 from_entered_time: Option<String>,
237 #[arg(long)]
238 to_entered_time: Option<String>,
239 #[arg(long)]
240 status: Option<String>,
241 #[arg(long)]
242 max_results: Option<String>,
243 },
244 All {
246 #[arg(long)]
247 from_entered_time: Option<String>,
248 #[arg(long)]
249 to_entered_time: Option<String>,
250 #[arg(long)]
251 status: Option<String>,
252 #[arg(long)]
253 max_results: Option<String>,
254 },
255 Get {
257 account_number: String,
258 order_id: String,
259 },
260 Wait {
262 account_number: String,
263 order_id: String,
264 #[arg(long, default_value = "filled")]
266 until: String,
267 #[arg(long, default_value = "3600")]
269 timeout_seconds: u64,
270 #[arg(long, default_value = "5")]
272 interval_seconds: u64,
273 #[arg(long, default_value = "false")]
275 proceed_on_partial_fill: bool,
276 },
277 Place {
279 account_number: String,
280 #[arg(long)]
282 order: String,
283 },
284 Preview {
286 account_number: String,
287 #[arg(long)]
288 order: String,
289 },
290 Cancel {
292 account_number: String,
293 order_id: String,
294 },
295 Replace {
297 account_number: String,
298 order_id: String,
299 #[arg(long)]
300 order: String,
301 },
302}
303
304#[derive(Debug, Subcommand)]
305pub enum TransactionsCommands {
306 List {
308 account_number: String,
309 #[arg(long)]
310 start_date: Option<String>,
311 #[arg(long)]
312 end_date: Option<String>,
313 #[arg(long)]
314 types: Option<String>,
315 #[arg(long)]
316 symbol: Option<String>,
317 },
318 Get {
320 account_number: String,
321 transaction_id: String,
322 },
323}
324
325#[derive(Debug, Subcommand)]
326pub enum UserCommands {
327 Preference,
329}
330
331#[derive(Debug, Subcommand)]
332pub enum PortfolioCommands {
333 Summary,
335 BuyingPower {
337 #[arg(long)]
339 account_number: String,
340 },
341}
342
343#[derive(Debug, Subcommand)]
344pub enum TradeCommands {
345 Buy {
347 #[arg(long)]
349 account_number: String,
350 #[arg(long)]
352 symbol: String,
353 #[arg(long)]
355 quantity: f64,
356 #[arg(long, default_value = "market")]
358 order_type: String,
359 #[arg(long)]
361 price: Option<f64>,
362 #[arg(long)]
364 duration: Option<String>,
365 #[arg(long)]
367 session: Option<String>,
368 },
369 Sell {
371 #[arg(long)]
372 account_number: String,
373 #[arg(long)]
374 symbol: String,
375 #[arg(long)]
376 quantity: f64,
377 #[arg(long, default_value = "market")]
378 order_type: String,
379 #[arg(long)]
380 price: Option<f64>,
381 #[arg(long)]
382 duration: Option<String>,
383 #[arg(long)]
384 session: Option<String>,
385 },
386}
387
388#[derive(Debug, Subcommand)]
389pub enum SafetyCommands {
390 Show,
392 Init,
394 Path,
396}
397
398#[derive(Debug, Subcommand)]
399pub enum DisclaimerCommands {
400 Show,
402 Accept,
404 Status,
406}
407
408#[derive(Debug, Subcommand)]
409pub enum PlanCommands {
410 Schema,
412 Prompt,
414 Validate {
416 file: PathBuf,
418 },
419 Show { file: PathBuf },
421 Run {
423 file: PathBuf,
424 #[arg(long)]
426 step: Option<String>,
427 #[arg(long)]
429 from_step: Option<String>,
430 },
431}
432
433#[derive(Debug, Subcommand)]
434pub enum MarketCommands {
435 Info {
437 symbol: String,
439 #[arg(long)]
441 no_history: bool,
442 #[arg(long, default_value = "month")]
443 history_period_type: String,
444 #[arg(long, default_value_t = 1)]
445 history_period: u32,
446 #[arg(long, default_value = "daily")]
447 history_frequency_type: String,
448 },
449 Quotes {
451 #[arg(long)]
453 symbols: String,
454 #[arg(long)]
456 fields: Option<String>,
457 #[arg(long)]
458 indicative: Option<bool>,
459 },
460 Quote {
462 symbol: String,
463 #[arg(long)]
464 fields: Option<String>,
465 #[arg(long)]
466 indicative: Option<bool>,
467 },
468 History {
470 symbol: String,
471 #[arg(long)]
472 period_type: Option<String>,
473 #[arg(long)]
474 period: Option<u32>,
475 #[arg(long)]
476 frequency_type: Option<String>,
477 #[arg(long)]
478 frequency: Option<u32>,
479 #[arg(long)]
481 start_date: Option<i64>,
482 #[arg(long)]
483 end_date: Option<i64>,
484 #[arg(long)]
485 need_extended_hours_data: Option<bool>,
486 #[arg(long)]
487 need_previous_close: Option<bool>,
488 },
489 Instrument {
491 #[arg(long)]
493 symbol: String,
494 #[arg(long, default_value = "fundamental")]
496 projection: String,
497 },
498 InstrumentByCusip { cusip: String },
500 Hours {
502 #[arg(long, default_value = "equity")]
504 markets: String,
505 #[arg(long)]
507 date: Option<String>,
508 },
509 HoursFor {
511 market: String,
513 #[arg(long)]
514 date: Option<String>,
515 },
516}
517
518#[derive(Debug, Subcommand)]
519pub enum OptionsCommands {
520 Chain {
522 #[arg(long)]
523 symbol: String,
524 #[arg(long, name = "type")]
526 contract_type: Option<String>,
527 #[arg(long)]
528 strike_count: Option<u32>,
529 #[arg(long)]
530 from_date: Option<String>,
531 #[arg(long)]
532 to_date: Option<String>,
533 },
534 Positions {
536 #[arg(long)]
537 account_number: Option<String>,
538 },
539 Schema,
541 Validate {
543 #[arg(long)]
544 strategy: String,
545 #[arg(long)]
547 params: String,
548 #[arg(long)]
549 account_number: Option<String>,
550 #[arg(long, default_value = "margin")]
551 account_type: Option<String>,
552 },
553 Preview {
555 #[arg(long)]
556 account_number: String,
557 #[arg(long)]
558 strategy: String,
559 #[arg(long)]
560 params: String,
561 },
562 Open {
564 #[arg(long)]
565 account_number: String,
566 #[arg(long)]
567 strategy: String,
568 #[arg(long)]
569 params: String,
570 },
571 Close {
573 #[arg(long)]
574 account_number: String,
575 #[arg(long)]
576 position_id: String,
577 },
578}
579
580#[derive(Debug, Subcommand)]
581pub enum AgentCommands {
582 Schema,
584 Validate { file: PathBuf },
586 Status {
588 #[arg(long)]
589 rules_file: Option<PathBuf>,
590 },
591 Run {
593 file: PathBuf,
594 #[arg(long)]
596 once: bool,
597 #[arg(long)]
599 background: bool,
600 },
601 Stop { file: PathBuf },
603 CloseAll { file: PathBuf },
605 Sim {
607 #[command(subcommand)]
608 command: AgentSimCommands,
609 },
610}
611
612#[derive(Debug, Subcommand)]
613pub enum AgentSimCommands {
614 Stats { file: PathBuf },
616 Report {
618 file: PathBuf,
619 #[arg(long)]
620 output: Option<PathBuf>,
621 },
622 Reset { file: PathBuf },
624}
625
626#[derive(Debug, Subcommand)]
627pub enum RulesCommands {
628 Show { file: Option<PathBuf> },
630 List,
632}
633
634impl Cli {
636 pub fn effective_output(&self) -> OutputFormat {
637 if self.json {
638 OutputFormat::Json
639 } else if self.md {
640 OutputFormat::Md
641 } else {
642 self.output
643 }
644 }
645}