1use anyhow::Result;
2
3use super::super::commands::{
4 AccCashFlowArgs, CancelOrderArgs, HistoryDealsArgs, HistoryOrdersArgs, MarginRatioArgs,
5 MaxQtysArgs, ModifyOrderArgs, OrderFeeArgs, PlaceOrderArgs, ProtoJsonArgs, ReconfirmOrderArgs,
6};
7use crate::cmd;
8use crate::output::OutputFormat;
9
10pub(super) struct CancelAllOrderDispatchArgs {
11 pub(super) acc_id: Option<u64>,
12 pub(super) acc_id_file: Option<std::path::PathBuf>,
13 pub(super) card_num: Option<String>,
14 pub(super) env: String,
15 pub(super) market: Option<String>,
16 pub(super) jp_acc_type: Option<i32>,
17 pub(super) confirm: bool,
18}
19
20pub(super) async fn dispatch_place_order_group(
21 gateway: &str,
22 output: OutputFormat,
23 args: ProtoJsonArgs,
24) -> Result<()> {
25 cmd::proto_json::run_place_order_group(gateway, &args.c2s_json, output).await
26}
27
28pub(super) async fn dispatch_modify_order_group(
29 gateway: &str,
30 output: OutputFormat,
31 args: ProtoJsonArgs,
32) -> Result<()> {
33 cmd::proto_json::run_modify_order_group(gateway, &args.c2s_json, output).await
34}
35
36pub(super) async fn dispatch_cancel_order_group(
37 gateway: &str,
38 output: OutputFormat,
39 args: ProtoJsonArgs,
40) -> Result<()> {
41 cmd::proto_json::run_cancel_order_group(gateway, &args.c2s_json, output).await
42}
43
44pub(super) async fn dispatch_delete_order_group(
45 gateway: &str,
46 output: OutputFormat,
47 args: ProtoJsonArgs,
48) -> Result<()> {
49 cmd::proto_json::run_delete_order_group(gateway, &args.c2s_json, output).await
50}
51
52pub(super) async fn dispatch_place_algo_order(
53 gateway: &str,
54 output: OutputFormat,
55 args: ProtoJsonArgs,
56) -> Result<()> {
57 cmd::proto_json::run_place_algo_order(gateway, &args.c2s_json, output).await
58}
59
60pub(super) async fn dispatch_modify_algo_order(
61 gateway: &str,
62 output: OutputFormat,
63 args: ProtoJsonArgs,
64) -> Result<()> {
65 cmd::proto_json::run_modify_algo_order(gateway, &args.c2s_json, output).await
66}
67
68pub(super) async fn dispatch_algo_order_logs(
69 gateway: &str,
70 output: OutputFormat,
71 args: ProtoJsonArgs,
72) -> Result<()> {
73 cmd::proto_json::run_algo_order_logs(gateway, &args.c2s_json, output).await
74}
75
76pub(super) async fn dispatch_reverse_position(
77 gateway: &str,
78 output: OutputFormat,
79 args: ProtoJsonArgs,
80) -> Result<()> {
81 cmd::proto_json::run_reverse_position(gateway, &args.c2s_json, output).await
82}
83
84pub(super) async fn dispatch_roll_position(
85 gateway: &str,
86 output: OutputFormat,
87 args: ProtoJsonArgs,
88) -> Result<()> {
89 cmd::proto_json::run_roll_position(gateway, &args.c2s_json, output).await
90}
91
92pub(super) async fn dispatch_clear_futures_positions(
93 gateway: &str,
94 output: OutputFormat,
95 args: ProtoJsonArgs,
96) -> Result<()> {
97 cmd::proto_json::run_clear_futures_positions(gateway, &args.c2s_json, output).await
98}
99
100pub(super) async fn dispatch_batch_close_positions(
101 gateway: &str,
102 output: OutputFormat,
103 args: ProtoJsonArgs,
104) -> Result<()> {
105 cmd::proto_json::run_batch_close_positions(gateway, &args.c2s_json, output).await
106}
107
108fn csv_list(value: Option<String>) -> Vec<String> {
109 value
110 .map(|s| {
111 s.split(',')
112 .map(|x| x.trim().to_string())
113 .filter(|x| !x.is_empty())
114 .collect()
115 })
116 .unwrap_or_default()
117}
118
119pub(super) async fn dispatch_place_order(
120 gateway: &str,
121 output: OutputFormat,
122 args: PlaceOrderArgs,
123) -> Result<()> {
124 let acc_id = cmd::account::resolve_account_locator(
125 gateway,
126 args.acc_id,
127 args.acc_id_file.as_deref(),
128 args.card_num.as_deref(),
129 "place-order",
130 )
131 .await?;
132 cmd::trade_ext::run_place_order(cmd::trade_ext::PlaceOrderCommand {
133 gateway,
134 env: &args.env,
135 acc_id,
136 market: &args.market,
137 side: &args.side,
138 order_type: &args.order_type,
139 code: &args.code,
140 qty: args.qty,
141 price: args.price,
142 amount: args.amount,
143 pred_side: args.pred_side,
144 time_in_force: args.time_in_force.as_deref(),
145 fill_outside_rth: args.fill_outside_rth,
146 session: args.session.as_deref(),
147 expire_time: args.expire_time.as_deref(),
148 jp_acc_type: args.jp_acc_type,
149 confirm: args.confirm,
150 idempotency_key: args.idempotency_key,
151 stop_price: args.stop_price,
152 trail_type: args.trail_type,
153 trail_value: args.trail_value,
154 trail_spread: args.trail_spread,
155 output,
156 })
157 .await
158}
159
160pub(super) async fn dispatch_modify_order(
161 gateway: &str,
162 output: OutputFormat,
163 args: ModifyOrderArgs,
164) -> Result<()> {
165 let acc_id = cmd::account::resolve_account_locator(
166 gateway,
167 args.acc_id,
168 args.acc_id_file.as_deref(),
169 args.card_num.as_deref(),
170 "modify-order",
171 )
172 .await?;
173 cmd::trade_ext::run_modify_order(cmd::trade_ext::ModifyOrderCommand {
174 gateway,
175 env: &args.env,
176 acc_id,
177 market: &args.market,
178 order_id: args.order_id,
179 op: &args.op,
180 qty: args.qty,
181 price: args.price,
182 jp_acc_type: args.jp_acc_type,
183 confirm: args.confirm,
184 idempotency_key: args.idempotency_key,
185 output,
186 })
187 .await
188}
189
190pub(super) async fn dispatch_cancel_order(
191 gateway: &str,
192 output: OutputFormat,
193 args: CancelOrderArgs,
194) -> Result<()> {
195 let acc_id = cmd::account::resolve_account_locator(
196 gateway,
197 args.acc_id,
198 args.acc_id_file.as_deref(),
199 args.card_num.as_deref(),
200 "cancel-order",
201 )
202 .await?;
203 cmd::trade_ext::run_cancel_order(cmd::trade_ext::CancelOrderCommand {
204 gateway,
205 env: &args.env,
206 acc_id,
207 market: &args.market,
208 order_id: args.order_id,
209 jp_acc_type: args.jp_acc_type,
210 confirm: args.confirm,
211 idempotency_key: args.idempotency_key,
212 output,
213 })
214 .await
215}
216
217pub(super) async fn dispatch_reconfirm_order(
218 gateway: &str,
219 output: OutputFormat,
220 args: ReconfirmOrderArgs,
221) -> Result<()> {
222 let acc_id = cmd::account::resolve_account_locator(
223 gateway,
224 args.acc_id,
225 args.acc_id_file.as_deref(),
226 args.card_num.as_deref(),
227 "reconfirm-order",
228 )
229 .await?;
230 cmd::trade_ext::run_reconfirm_order(cmd::trade_ext::ReconfirmOrderCommand {
231 gateway,
232 env: &args.env,
233 acc_id,
234 market: &args.market,
235 order_id: args.order_id,
236 reason: args.reason,
237 jp_acc_type: args.jp_acc_type,
238 confirm: args.confirm,
239 output,
240 })
241 .await
242}
243
244pub(super) async fn dispatch_history_orders(
245 gateway: &str,
246 output: OutputFormat,
247 args: HistoryOrdersArgs,
248) -> Result<()> {
249 let acc_id = cmd::account::resolve_account_locator(
250 gateway,
251 args.acc_id,
252 args.acc_id_file.as_deref(),
253 args.card_num.as_deref(),
254 "history-orders",
255 )
256 .await?;
257 cmd::trade_ext::run_history_orders(cmd::trade_ext::HistoryOrdersCommand {
258 gateway,
259 env: &args.env,
260 acc_id,
261 market: &args.market,
262 codes: csv_list(args.codes),
263 begin: args.begin,
264 end: args.end,
265 output,
266 })
267 .await
268}
269
270pub(super) async fn dispatch_history_deals(
271 gateway: &str,
272 output: OutputFormat,
273 args: HistoryDealsArgs,
274) -> Result<()> {
275 let acc_id = cmd::account::resolve_account_locator(
276 gateway,
277 args.acc_id,
278 args.acc_id_file.as_deref(),
279 args.card_num.as_deref(),
280 "history-deals",
281 )
282 .await?;
283 cmd::trade_ext::run_history_deals(cmd::trade_ext::HistoryDealsCommand {
284 gateway,
285 env: &args.env,
286 acc_id,
287 market: &args.market,
288 codes: csv_list(args.codes),
289 begin: args.begin,
290 end: args.end,
291 output,
292 })
293 .await
294}
295
296pub(super) async fn dispatch_max_qtys(
297 gateway: &str,
298 output: OutputFormat,
299 args: MaxQtysArgs,
300) -> Result<()> {
301 let acc_id = cmd::account::resolve_account_locator(
302 gateway,
303 args.acc_id,
304 args.acc_id_file.as_deref(),
305 args.card_num.as_deref(),
306 "max-trd-qtys",
307 )
308 .await?;
309 cmd::trade_ext::run_max_qtys(cmd::trade_ext::MaxQtysCommand {
310 gateway,
311 env: &args.env,
312 acc_id,
313 market: &args.market,
314 order_type: &args.order_type,
315 code: &args.code,
316 price: args.price,
317 jp_acc_type: args.jp_acc_type,
318 output,
319 })
320 .await
321}
322
323pub(super) async fn dispatch_margin_ratio(
324 gateway: &str,
325 output: OutputFormat,
326 args: MarginRatioArgs,
327) -> Result<()> {
328 let acc_id = cmd::account::resolve_account_locator(
329 gateway,
330 args.acc_id,
331 args.acc_id_file.as_deref(),
332 args.card_num.as_deref(),
333 "margin-ratio",
334 )
335 .await?;
336 let symbols = args.symbols.or(args.symbols_arg).ok_or_else(|| {
337 anyhow::anyhow!("margin-ratio: 需要位置参数 <SYMBOLS> 或 --code / --symbols")
338 })?;
339 let syms: Vec<String> = symbols.split(',').map(|s| s.trim().to_string()).collect();
340 cmd::trade_ext::run_margin_ratio(gateway, &args.env, acc_id, &args.market, &syms, output).await
341}
342
343pub(super) async fn dispatch_order_fee(
344 gateway: &str,
345 output: OutputFormat,
346 args: OrderFeeArgs,
347) -> Result<()> {
348 let acc_id = cmd::account::resolve_account_locator(
349 gateway,
350 args.acc_id,
351 args.acc_id_file.as_deref(),
352 args.card_num.as_deref(),
353 "order-fee",
354 )
355 .await?;
356 let ids: Vec<String> = args
357 .order_ids
358 .split(',')
359 .map(|s| s.trim().to_string())
360 .collect();
361 cmd::trade_ext::run_order_fee(gateway, &args.env, acc_id, &args.market, &ids, output).await
362}
363
364pub(super) async fn dispatch_cancel_all_order(
365 gateway: &str,
366 output: OutputFormat,
367 args: CancelAllOrderDispatchArgs,
368) -> Result<()> {
369 let acc_id = cmd::account::resolve_account_locator(
370 gateway,
371 args.acc_id,
372 args.acc_id_file.as_deref(),
373 args.card_num.as_deref(),
374 "cancel-all-order",
375 )
376 .await?;
377 cmd::trade_ext::run_cancel_all_order(
378 gateway,
379 acc_id,
380 &args.env,
381 args.market.as_deref(),
382 args.jp_acc_type,
383 args.confirm,
384 output,
385 )
386 .await
387}
388
389pub(super) async fn dispatch_acc_cash_flow(
390 gateway: &str,
391 output: OutputFormat,
392 args: AccCashFlowArgs,
393) -> Result<()> {
394 let acc_id = cmd::account::resolve_account_locator(
395 gateway,
396 args.acc_id.or(args.acc_id_arg),
397 args.acc_id_file.as_deref(),
398 args.card_num.as_deref(),
399 "acc-cash-flow",
400 )
401 .await?;
402 if let Some(range) = args.date_range {
404 let (from_s, to_s) = range.split_once("..").ok_or_else(|| {
406 anyhow::anyhow!("--date-range 格式应为 `YYYY-MM-DD..YYYY-MM-DD`,当前:{range}")
407 })?;
408 let from = chrono::NaiveDate::parse_from_str(from_s, "%Y-%m-%d")
409 .map_err(|e| anyhow::anyhow!("start date parse: {e}"))?;
410 let to = chrono::NaiveDate::parse_from_str(to_s, "%Y-%m-%d")
411 .map_err(|e| anyhow::anyhow!("end date parse: {e}"))?;
412 cmd::trade_ext::run_acc_cash_flow_range(cmd::trade_ext::AccCashFlowRangeCommand {
413 gateway,
414 acc_id,
415 date_from: from,
416 date_to: to,
417 env: &args.env,
418 market: &args.market,
419 direction: args.direction,
420 })
421 .await
422 } else if let Some(d) = args.date {
423 cmd::trade_ext::run_acc_cash_flow(
424 gateway,
425 acc_id,
426 &d,
427 &args.env,
428 &args.market,
429 args.direction,
430 output,
431 )
432 .await
433 } else {
434 anyhow::bail!("需传 --date <YYYY-MM-DD> 或 --date-range <FROM..TO>")
435 }
436}