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 trust: bool,
53
54 #[arg(long, global = true)]
56 pub help_json: bool,
57
58 #[command(subcommand)]
59 pub command: Option<Commands>,
60}
61
62#[derive(Debug, Subcommand)]
63pub enum Commands {
64 Capabilities,
66
67 Env {
69 #[command(subcommand)]
70 command: EnvCommands,
71 },
72
73 Instructions,
75
76 Disclaimer {
78 #[command(subcommand)]
79 command: DisclaimerCommands,
80 },
81
82 Auth {
84 #[command(subcommand)]
85 command: AuthCommands,
86 },
87
88 Accounts {
90 #[command(subcommand)]
91 command: AccountsCommands,
92 },
93
94 Orders {
96 #[command(subcommand)]
97 command: OrdersCommands,
98 },
99
100 Transactions {
102 #[command(subcommand)]
103 command: TransactionsCommands,
104 },
105
106 User {
108 #[command(subcommand)]
109 command: UserCommands,
110 },
111
112 Portfolio {
114 #[command(subcommand)]
115 command: PortfolioCommands,
116 },
117
118 Trade {
120 #[command(subcommand)]
121 command: TradeCommands,
122 },
123
124 Safety {
126 #[command(subcommand)]
127 command: SafetyCommands,
128 },
129
130 Plan {
132 #[command(subcommand)]
133 command: PlanCommands,
134 },
135
136 Market {
138 #[command(subcommand)]
139 command: MarketCommands,
140 },
141
142 Options {
144 #[command(subcommand)]
145 command: OptionsCommands,
146 },
147
148 Agent {
150 #[command(subcommand)]
151 command: AgentCommands,
152 },
153
154 Dashboard {
156 file: Option<PathBuf>,
158 },
159
160 Watch {
162 file: Option<PathBuf>,
164 #[arg(long)]
166 monitor_only: bool,
167 },
168
169 Rules {
171 #[command(subcommand)]
172 command: RulesCommands,
173 },
174}
175
176#[derive(Debug, Subcommand)]
177pub enum EnvCommands {
178 Schema,
180}
181
182#[derive(Debug, Subcommand)]
183pub enum AuthCommands {
184 Login {
186 #[arg(long)]
188 code: Option<String>,
189 },
190 Status,
192 Refresh,
194 Logout,
196}
197
198#[derive(Debug, Subcommand)]
199pub enum AccountsCommands {
200 Numbers,
202 List {
204 #[arg(long)]
205 fields: Option<String>,
206 },
207 Get {
209 account_number: String,
210 #[arg(long)]
211 fields: Option<String>,
212 },
213}
214
215#[derive(Debug, Subcommand)]
216pub enum OrdersCommands {
217 Schema,
219 Validate {
221 #[arg(long)]
223 order: String,
224 #[arg(long)]
226 account_number: Option<String>,
227 },
228 List {
230 account_number: String,
231 #[arg(long)]
232 from_entered_time: Option<String>,
233 #[arg(long)]
234 to_entered_time: Option<String>,
235 #[arg(long)]
236 status: Option<String>,
237 #[arg(long)]
238 max_results: Option<String>,
239 },
240 All {
242 #[arg(long)]
243 from_entered_time: Option<String>,
244 #[arg(long)]
245 to_entered_time: Option<String>,
246 #[arg(long)]
247 status: Option<String>,
248 #[arg(long)]
249 max_results: Option<String>,
250 },
251 Get {
253 account_number: String,
254 order_id: String,
255 },
256 Wait {
258 account_number: String,
259 order_id: String,
260 #[arg(long, default_value = "filled")]
262 until: String,
263 #[arg(long, default_value = "3600")]
265 timeout_seconds: u64,
266 #[arg(long, default_value = "5")]
268 interval_seconds: u64,
269 #[arg(long, default_value = "false")]
271 proceed_on_partial_fill: bool,
272 },
273 Place {
275 account_number: String,
276 #[arg(long)]
278 order: String,
279 },
280 Preview {
282 account_number: String,
283 #[arg(long)]
284 order: String,
285 },
286 Cancel {
288 account_number: String,
289 order_id: String,
290 },
291 Replace {
293 account_number: String,
294 order_id: String,
295 #[arg(long)]
296 order: String,
297 },
298}
299
300#[derive(Debug, Subcommand)]
301pub enum TransactionsCommands {
302 List {
304 account_number: String,
305 #[arg(long)]
306 start_date: Option<String>,
307 #[arg(long)]
308 end_date: Option<String>,
309 #[arg(long)]
310 types: Option<String>,
311 #[arg(long)]
312 symbol: Option<String>,
313 },
314 Get {
316 account_number: String,
317 transaction_id: String,
318 },
319}
320
321#[derive(Debug, Subcommand)]
322pub enum UserCommands {
323 Preference,
325}
326
327#[derive(Debug, Subcommand)]
328pub enum PortfolioCommands {
329 Summary,
331 BuyingPower {
333 #[arg(long)]
335 account_number: String,
336 },
337}
338
339#[derive(Debug, Subcommand)]
340pub enum TradeCommands {
341 Buy {
343 #[arg(long)]
345 account_number: String,
346 #[arg(long)]
348 symbol: String,
349 #[arg(long)]
351 quantity: f64,
352 #[arg(long, default_value = "market")]
354 order_type: String,
355 #[arg(long)]
357 price: Option<f64>,
358 #[arg(long)]
360 duration: Option<String>,
361 #[arg(long)]
363 session: Option<String>,
364 },
365 Sell {
367 #[arg(long)]
368 account_number: String,
369 #[arg(long)]
370 symbol: String,
371 #[arg(long)]
372 quantity: f64,
373 #[arg(long, default_value = "market")]
374 order_type: String,
375 #[arg(long)]
376 price: Option<f64>,
377 #[arg(long)]
378 duration: Option<String>,
379 #[arg(long)]
380 session: Option<String>,
381 },
382}
383
384#[derive(Debug, Subcommand)]
385pub enum SafetyCommands {
386 Show,
388 Init,
390 Path,
392}
393
394#[derive(Debug, Subcommand)]
395pub enum DisclaimerCommands {
396 Show,
398 Accept,
400 Status,
402}
403
404#[derive(Debug, Subcommand)]
405pub enum PlanCommands {
406 Schema,
408 Prompt,
410 Validate {
412 file: PathBuf,
414 },
415 Show { file: PathBuf },
417 Run {
419 file: PathBuf,
420 #[arg(long)]
422 step: Option<String>,
423 #[arg(long)]
425 from_step: Option<String>,
426 },
427}
428
429#[derive(Debug, Subcommand)]
430pub enum MarketCommands {
431 Info {
433 symbol: String,
435 #[arg(long)]
437 no_history: bool,
438 #[arg(long, default_value = "month")]
439 history_period_type: String,
440 #[arg(long, default_value_t = 1)]
441 history_period: u32,
442 #[arg(long, default_value = "daily")]
443 history_frequency_type: String,
444 },
445 Quotes {
447 #[arg(long)]
449 symbols: String,
450 #[arg(long)]
452 fields: Option<String>,
453 #[arg(long)]
454 indicative: Option<bool>,
455 },
456 Quote {
458 symbol: String,
459 #[arg(long)]
460 fields: Option<String>,
461 #[arg(long)]
462 indicative: Option<bool>,
463 },
464 History {
466 symbol: String,
467 #[arg(long)]
468 period_type: Option<String>,
469 #[arg(long)]
470 period: Option<u32>,
471 #[arg(long)]
472 frequency_type: Option<String>,
473 #[arg(long)]
474 frequency: Option<u32>,
475 #[arg(long)]
477 start_date: Option<i64>,
478 #[arg(long)]
479 end_date: Option<i64>,
480 #[arg(long)]
481 need_extended_hours_data: Option<bool>,
482 #[arg(long)]
483 need_previous_close: Option<bool>,
484 },
485 Instrument {
487 #[arg(long)]
489 symbol: String,
490 #[arg(long, default_value = "fundamental")]
492 projection: String,
493 },
494 InstrumentByCusip { cusip: String },
496 Hours {
498 #[arg(long, default_value = "equity")]
500 markets: String,
501 #[arg(long)]
503 date: Option<String>,
504 },
505 HoursFor {
507 market: String,
509 #[arg(long)]
510 date: Option<String>,
511 },
512}
513
514#[derive(Debug, Subcommand)]
515pub enum OptionsCommands {
516 Chain {
518 #[arg(long)]
519 symbol: String,
520 #[arg(long, name = "type")]
522 contract_type: Option<String>,
523 #[arg(long)]
524 strike_count: Option<u32>,
525 #[arg(long)]
526 from_date: Option<String>,
527 #[arg(long)]
528 to_date: Option<String>,
529 },
530 Positions {
532 #[arg(long)]
533 account_number: Option<String>,
534 },
535 Schema,
537 Validate {
539 #[arg(long)]
540 strategy: String,
541 #[arg(long)]
543 params: String,
544 #[arg(long)]
545 account_number: Option<String>,
546 #[arg(long, default_value = "margin")]
547 account_type: Option<String>,
548 },
549 Preview {
551 #[arg(long)]
552 account_number: String,
553 #[arg(long)]
554 strategy: String,
555 #[arg(long)]
556 params: String,
557 },
558 Open {
560 #[arg(long)]
561 account_number: String,
562 #[arg(long)]
563 strategy: String,
564 #[arg(long)]
565 params: String,
566 },
567 Close {
569 #[arg(long)]
570 account_number: String,
571 #[arg(long)]
572 position_id: String,
573 },
574}
575
576#[derive(Debug, Subcommand)]
577pub enum AgentCommands {
578 Schema,
580 Validate { file: PathBuf },
582 Status {
584 #[arg(long)]
585 rules_file: Option<PathBuf>,
586 },
587 Run {
589 file: PathBuf,
590 #[arg(long)]
592 once: bool,
593 #[arg(long)]
595 background: bool,
596 },
597 Stop { file: PathBuf },
599}
600
601#[derive(Debug, Subcommand)]
602pub enum RulesCommands {
603 Show { file: Option<PathBuf> },
605 List,
607}
608
609impl Cli {
611 pub fn effective_output(&self) -> OutputFormat {
612 if self.json {
613 OutputFormat::Json
614 } else if self.md {
615 OutputFormat::Md
616 } else {
617 self.output
618 }
619 }
620}