1use anyhow::{Result, bail};
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::{get_margin_ratio, get_order_fee};
9use futu_trd::types::TrdHeader;
10
11#[derive(Tabled)]
12struct MarginRatioRow {
13 #[tabled(rename = "Code")]
14 code: String,
15 #[tabled(rename = "Long?")]
16 long: &'static str,
17 #[tabled(rename = "Short?")]
18 short: &'static str,
19 #[tabled(rename = "IM Long%")]
20 im_long: String,
21 #[tabled(rename = "IM Short%")]
22 im_short: String,
23 #[tabled(rename = "Short Pool")]
24 short_pool: String,
25 #[tabled(rename = "Short Fee%")]
26 short_fee: String,
27}
28
29#[derive(Serialize)]
30struct MarginRatioJson {
31 code: String,
32 is_long_permit: bool,
33 is_short_permit: bool,
34 im_long_ratio: f64,
35 im_short_ratio: f64,
36 short_pool_remain: f64,
37 short_fee_rate: f64,
38}
39
40pub async fn run_margin_ratio(
45 gateway: &str,
46 env: &str,
47 acc_id: u64,
48 market: &str,
49 symbols: &[String],
50 format: OutputFormat,
51) -> Result<()> {
52 if symbols.is_empty() {
53 bail!("no symbols");
54 }
55 let env_p = parse_trd_env(env)?;
56 let market_p = parse_trd_market_for_write(market)?;
57 let header = TrdHeader {
58 trd_env: env_p,
59 acc_id,
60 trd_market: market_p,
61 jp_acc_type: None,
62 };
63 let secs: Vec<(i32, String)> = symbols
65 .iter()
66 .map(|s| {
67 let sec = crate::common::parse_symbol(s)?;
68 Ok::<_, anyhow::Error>((sec.market as i32, sec.code))
70 })
71 .collect::<Result<Vec<_>>>()?;
72 let (client, _push_rx) = connect_gateway(gateway, "futucli-margin-ratio").await?;
73 let list = get_margin_ratio(&client, &header, &secs).await?;
74
75 let rows: Vec<MarginRatioRow> = list
77 .iter()
78 .map(|r| MarginRatioRow {
79 code: r.code.clone(),
80 long: if r.is_long_permit { "yes" } else { "no" },
81 short: if r.is_short_permit { "yes" } else { "no" },
82 im_long: format!("{:.2}", r.im_long_ratio),
83 im_short: format!("{:.2}", r.im_short_ratio),
84 short_pool: format!("{:.0}", r.short_pool_remain),
85 short_fee: format!("{:.3}", r.short_fee_rate),
86 })
87 .collect();
88 let jsons: Vec<MarginRatioJson> = list
89 .iter()
90 .map(|r| MarginRatioJson {
91 code: r.code.clone(),
92 is_long_permit: r.is_long_permit,
93 is_short_permit: r.is_short_permit,
94 im_long_ratio: r.im_long_ratio,
95 im_short_ratio: r.im_short_ratio,
96 short_pool_remain: r.short_pool_remain,
97 short_fee_rate: r.short_fee_rate,
98 })
99 .collect();
100 format.print_rows(&rows, &jsons)?;
101 Ok(())
102}
103
104#[derive(Tabled)]
105struct OrderFeeRow {
106 #[tabled(rename = "OrderID")]
107 order_id_ex: String,
108 #[tabled(rename = "Total Fee")]
109 total: String,
110 #[tabled(rename = "Items")]
111 items: String,
112}
113
114#[derive(Serialize)]
115struct OrderFeeItemJson {
116 title: String,
117 value: f64,
118}
119
120#[derive(Serialize)]
121struct OrderFeeJson {
122 order_id_ex: String,
123 fee_amount: f64,
124 fee_list: Vec<OrderFeeItemJson>,
125}
126
127pub async fn run_order_fee(
133 gateway: &str,
134 env: &str,
135 acc_id: u64,
136 market: &str,
137 order_ids: &[String],
138 format: OutputFormat,
139) -> Result<()> {
140 if order_ids.is_empty() {
141 bail!("need at least one --order-id");
142 }
143 let env_p = parse_trd_env(env)?;
144 let market_p = parse_trd_market_for_write(market)?;
145 let header = TrdHeader {
146 trd_env: env_p,
147 acc_id,
148 trd_market: market_p,
149 jp_acc_type: None,
150 };
151 let (client, _push_rx) = connect_gateway(gateway, "futucli-order-fee").await?;
152 let list = get_order_fee(&client, &header, order_ids).await?;
153
154 let rows: Vec<OrderFeeRow> = list
155 .iter()
156 .map(|f| OrderFeeRow {
157 order_id_ex: f.order_id_ex.clone(),
158 total: format!("{:.2}", f.fee_amount),
159 items: f
160 .fee_list
161 .iter()
162 .map(|i| format!("{}={:.2}", i.title, i.value))
163 .collect::<Vec<_>>()
164 .join(" | "),
165 })
166 .collect();
167 let jsons: Vec<OrderFeeJson> = list
168 .iter()
169 .map(|f| OrderFeeJson {
170 order_id_ex: f.order_id_ex.clone(),
171 fee_amount: f.fee_amount,
172 fee_list: f
173 .fee_list
174 .iter()
175 .map(|i| OrderFeeItemJson {
176 title: i.title.clone(),
177 value: i.value,
178 })
179 .collect(),
180 })
181 .collect();
182 format.print_rows(&rows, &jsons)?;
183 Ok(())
184}