schwab-api-cli 0.1.3

schwab-api-cli — agent-first CLI for Charles Schwab Trader API (experimental — use at your own risk)
Documentation
use crate::plan::llm_prompt;
use crate::safety_config::{config_path, SafetyConfig};
use serde_json::{json, Value};

pub fn instructions_json(safety: &SafetyConfig) -> Value {
    json!({
        "role": "Schwab Trader API operator",
        "product": "Trader API - Individual (Accounts and Trading Production v1.0.0)",
        "base_url": schwab_api::TRADER_BASE_URL,
        "discovery_sequence": [
            "schwab --help --json",
            "schwab capabilities --json",
            "schwab env schema --json",
            "schwab instructions --json",
            "schwab disclaimer show",
            "schwab disclaimer accept --yes"
        ],
        "operating_mode": {
            "default": "agent",
            "flags": ["--mode agent", "--mode human"],
            "note": "Always prefer --json or --output json for automation"
        },
        "auth_flow": [
            "Ensure SCHWAB_APP_KEY and SCHWAB_APP_SECRET are set (see env schema)",
            "Run `schwab auth login` once; tokens persist on disk",
            "Use `schwab auth status --json` before API calls",
            "Access tokens expire ~30 minutes; refresh via `schwab auth refresh` or automatic refresh on API call"
        ],
        "account_ids": {
            "rule": "Use encrypted hashValue from `schwab accounts numbers --json` as {accountNumber} in trading endpoints",
            "never": "Do not confuse plain accountNumber with hashValue"
        },
        "output_contract": {
            "envelope_fields": ["success", "command", "inputs", "data", "warnings", "errors", "next_actions", "timestamp"],
            "formats": ["pretty", "json", "md"]
        },
        "trading_safety": {
            "config_path": config_path(),
            "config_command": "schwab safety show --json",
            "disclaimer": {
                "show": "schwab disclaimer show",
                "accept": "schwab disclaimer accept --yes",
                "status": "schwab disclaimer status --json",
                "required_before_live_trading": true,
                "docs": "README.md#disclaimer"
            },
            "hard_limits": safety.limits,
            "require_preview_before_place": safety.require_preview_before_place,
            "agent_rules": safety.agent_rules,
            "safe_mode_default": true,
            "trust_mode": {
                "flag": "--trust",
                "description": "Required with --yes for autonomous agent trading (non-interactive). Human mode uses interactive confirmation.",
                "note": "CLI enforces safety.json limits even in trust mode"
            },
            "autonomous_trade_flags": ["--trust", "--yes"],
            "dry_run_flag": "--dry-run"
        },
        "mutation_safety": {
            "auth_policy": "auth refresh/logout require --yes in non-interactive mode",
            "trading_policy": "orders place/cancel/replace and trade buy/sell require --trust --yes in agent mode, or interactive confirmation in human mode",
            "preview_first": "Use `schwab orders preview --json` or rely on built-in preview before place"
        },
        "recommended_read_path": [
            "schwab market info SGOV --json",
            "schwab market hours --markets equity --json",
            "schwab portfolio summary --json",
            "schwab portfolio buying-power --account-number <hash> --json",
            "schwab accounts numbers --json",
            "schwab orders list <hash> --json"
        ],
        "market_data": {
            "base_url": schwab_api::MARKET_DATA_BASE_URL,
            "requires_portal_product": "Market Data Production",
            "primary_research_command": "schwab market info <SYMBOL> --json",
            "commands": {
                "info": "schwab market info SGOV --json (quote + fundamentals + history + research hints)",
                "info_multi": "schwab market info SGOV,JPST,AAPL --json",
                "quotes": "schwab market quotes --symbols SGOV,JPST --fields quote,fundamental --json",
                "quote": "schwab market quote SGOV --fields all --json",
                "history": "schwab market history AAPL --period-type month --period 1 --frequency-type daily --json",
                "company_info": "schwab market instrument --symbol AAPL --projection fundamental --json",
                "hours": "schwab market hours --markets equity --json"
            },
            "agent_workflow": [
                "schwab market info <symbol> --json for Schwab-side facts",
                "Web search using data.researchHints.recommendedWebQueries for narrative, holdings, news",
                "schwab portfolio summary --json for account context",
                "schwab plan prompt --json → validate → dry-run → run with --trust --yes"
            ],
            "quote_fields": ["all", "quote", "fundamental", "reference", "extended", "regular"],
            "instrument_projections": ["symbol-search", "fundamental", "search", "desc-search"]
        },
        "recommended_trade_path": [
            "schwab portfolio buying-power --account-number <hash> --json",
            "schwab market quotes --symbols <SYMBOL> --fields quote --json",
            "schwab orders schema --json",
            "schwab safety show --json",
            "schwab orders validate --order '<json>' --json",
            "schwab orders preview --account-number <hash> --order '<json>' --json",
            "schwab orders place --account-number <hash> --order '<json>' --trust --yes --json"
        ],
        "buying_power": {
            "rule": "Before any BUY, check cashAvailableForTrading via `schwab portfolio buying-power --account-number <hash> --json`",
            "cli_enforcement": "trade buy and orders place reject buys when estimated cost exceeds available cash",
            "funding_sequence": [
                "If buying power is insufficient, sell source holdings first",
                "Wait for sell fill: `schwab orders wait <hash> <order_id> --until filled --json`",
                "Re-check buying power, then place the buy"
            ]
        },
        "orders": {
            "schema": "schwab orders schema --json",
            "validate": "schwab orders validate --order '<json>' --json",
            "supported_asset_types": ["EQUITY", "OPTION"],
            "complexOrderStrategyType": ["NONE", "VERTICAL", "IRON_CONDOR", "CUSTOM", "..."],
            "conditionalStrategies": ["OCO", "TRIGGER"],
            "safety_flags": {
                "allow_option_orders": "single-leg and spread option legs",
                "allow_complex_orders": "multi-leg spreads (NET_DEBIT/NET_CREDIT)",
                "allow_conditional_orders": "OCO and TRIGGER childOrderStrategies"
            },
            "option_symbology": "UNDERLYING(6) | YYMMDD | C/P | STRIKE(8) — e.g. XYZ   240315C00500000"
        },
        "trade_plans": {
            "description": "LLMs generate YAML/JSON plans; CLI validates and executes them",
            "schema": "schwab plan schema --json",
            "llm_prompt": "schwab plan prompt --json",
            "docs": "plans/TRADE_PLAN.md",
            "llm_schema_reference": "docs/LLM_SCHEMA_REFERENCE.md",
            "workflow_summary": llm_prompt().get("workflow")
        },
        "options_trading": {
            "schema": "schwab options schema --json",
            "chain": "schwab options chain --symbol SPY --json",
            "positions": "schwab options positions --account-number <hash> --json",
            "strategies_v1": ["vertical", "iron_condor"],
            "workflow": [
                "schwab options chain --symbol <UNDERLYING> --json",
                "schwab options validate --strategy vertical --params '<json>' --json",
                "schwab options preview --account-number <hash> --strategy vertical --params '<json>' --json",
                "schwab options open --account-number <hash> --strategy vertical --params '<json>' --trust --yes --json"
            ],
            "skill": ".cursor/skills/schwab-options/SKILL.md",
            "docs": "docs/OPTIONS_RULES.md"
        },
        "options_agent": {
            "rules_schema": "schwab agent schema --json",
            "validate": "schwab agent validate rules.yaml --json",
            "dry_run_tick": "schwab agent run rules.yaml --dry-run --once --json",
            "live_daemon": "schwab agent run rules.yaml --trust --yes",
            "status": "schwab agent status --rules-file rules.yaml --json",
            "example_rules": "rules/options-rules.example.yaml",
            "llm_schema_reference": "docs/LLM_SCHEMA_REFERENCE.md",
            "note": "Agent auto-executes vertical and iron condor entries/exits within safety.json and rules.yaml risk limits"
        },
        "system_prompt": "You operate the `schwab` CLI against Charles Schwab's Trader API. This is experimental software — use at your own risk. Before live trading, ensure the user has run `schwab disclaimer accept --yes`. Discover capabilities before acting. Use JSON output. Authenticate first. Use account hash values for trading. Read schwab safety show --json and obey agent_rules and hard limits. Preview before placement. Never pass --trust unless the user explicitly requests autonomous trading. For live agent trades use --trust --yes together."
    })
}