Skip to main content

futucli/cmd/trade_ext/
max_qtys.rs

1use anyhow::Result;
2use serde::Serialize;
3use tabled::Tabled;
4
5use crate::cmd::account::{parse_trd_env, parse_trd_market_for_write};
6use crate::common::connect_gateway;
7use crate::output::OutputFormat;
8use futu_trd::misc::{MaxTrdQtysParams, get_max_trd_qtys};
9use futu_trd::types::TrdHeader;
10
11use super::parsers::parse_order_type;
12
13// v1.4.98 external reviewer BUG-005 fix (P2, 2026-04-27): _output 之前 unused, 用户传
14// `-o json` 仍打文本. 加 format-aware output.
15#[derive(Serialize, Tabled)]
16struct MaxQtysRow {
17    field: String,
18    value: String,
19}
20
21#[derive(Serialize)]
22struct MaxQtysJson {
23    code: String,
24    price: f64,
25    max_cash_buy: f64,
26    max_cash_and_margin_buy: f64,
27    max_position_sell: f64,
28    max_sell_short: f64,
29    max_buy_back: f64,
30}
31
32pub struct MaxQtysCommand<'a> {
33    pub gateway: &'a str,
34    pub env: &'a str,
35    pub acc_id: u64,
36    pub market: &'a str,
37    pub order_type: &'a str,
38    pub code: &'a str,
39    pub price: f64,
40    pub jp_acc_type: Option<i32>,
41    pub output: OutputFormat,
42}
43
44pub async fn run_max_qtys(input: MaxQtysCommand<'_>) -> Result<()> {
45    let env_p = parse_trd_env(input.env)?;
46    let market_p = parse_trd_market_for_write(input.market)?;
47    let ot_p = parse_order_type(input.order_type)?;
48    let params = MaxTrdQtysParams {
49        header: TrdHeader {
50            trd_env: env_p,
51            acc_id: input.acc_id,
52            trd_market: market_p,
53            jp_acc_type: input.jp_acc_type,
54        },
55        order_type: ot_p as i32,
56        code: input.code.to_string(),
57        price: input.price,
58        order_id: None,
59    };
60    let (client, _push_rx) = connect_gateway(input.gateway, "futucli-trade-ext").await?;
61    let s2c = get_max_trd_qtys(&client, &params).await?;
62    let q = s2c
63        .max_trd_qtys
64        .ok_or_else(|| anyhow::anyhow!("missing max_trd_qtys"))?;
65    let max_cash_buy = q.max_cash_buy;
66    let max_cash_and_margin = q.max_cash_and_margin_buy.unwrap_or(0.0);
67    let max_position_sell = q.max_position_sell;
68    let max_sell_short = q.max_sell_short.unwrap_or(0.0);
69    let max_buy_back = q.max_buy_back.unwrap_or(0.0);
70    let rows = vec![
71        MaxQtysRow {
72            field: "max_cash_buy".into(),
73            value: max_cash_buy.to_string(),
74        },
75        MaxQtysRow {
76            field: "max_cash_and_margin_buy".into(),
77            value: max_cash_and_margin.to_string(),
78        },
79        MaxQtysRow {
80            field: "max_position_sell".into(),
81            value: max_position_sell.to_string(),
82        },
83        MaxQtysRow {
84            field: "max_sell_short".into(),
85            value: max_sell_short.to_string(),
86        },
87        MaxQtysRow {
88            field: "max_buy_back".into(),
89            value: max_buy_back.to_string(),
90        },
91    ];
92    let json = MaxQtysJson {
93        code: input.code.to_string(),
94        price: input.price,
95        max_cash_buy,
96        max_cash_and_margin_buy: max_cash_and_margin,
97        max_position_sell,
98        max_sell_short,
99        max_buy_back,
100    };
101    if matches!(input.output, OutputFormat::Table) {
102        println!("max trd qtys for {} @ {}:", input.code, input.price);
103    }
104    input.output.print_rows(&rows, &[json])?;
105    Ok(())
106}