futu_mcp/handlers/trade/
positions.rs1use std::sync::Arc;
5
6use anyhow::Result;
7use futu_net::client::FutuClient;
8use futu_trd::currency;
9use serde::Serialize;
10
11use super::helpers::*;
12
13#[derive(Serialize)]
14struct PositionOut {
15 position_id: u64,
16 code: String,
17 name: String,
18 qty: f64,
19 can_sell_qty: f64,
20 price: f64,
21 cost_price: f64,
23 val: f64,
24 pl_val: f64,
25 pl_ratio: f64,
26 #[serde(skip_serializing_if = "Option::is_none")]
30 diluted_cost_price: Option<f64>,
31 #[serde(skip_serializing_if = "Option::is_none")]
32 average_cost_price: Option<f64>,
33 #[serde(skip_serializing_if = "Option::is_none")]
34 average_pl_ratio: Option<f64>,
35 #[serde(skip_serializing_if = "Option::is_none")]
36 currency: Option<i32>,
37 #[serde(skip_serializing_if = "Option::is_none")]
38 trd_market: Option<i32>,
39 cost_basis_method_hint: &'static str,
45}
46
47pub fn derive_cost_basis_method_hint(
55 trd_market: Option<i32>,
56 currency: Option<i32>,
57) -> &'static str {
58 if trd_market == Some(15) {
60 return "average";
61 }
62 if currency == Some(4) {
64 return "average";
65 }
66 "diluted"
68}
69
70pub async fn get_positions(
71 client: &Arc<FutuClient>,
72 env: &str,
73 acc_id: u64,
74 market: &str,
75 requested_currency: Option<&str>,
76 option_strategy_view: bool,
77) -> Result<String> {
78 let header = build_header(env, acc_id, market)?;
79 let currency_int = match requested_currency {
80 Some(s) => Some(currency::parse_currency_label(s)?),
81 None => None,
82 };
83 let list = futu_trd::account::get_position_list_with_options(
84 client,
85 &header,
86 futu_trd::account::PositionListOptions {
87 filter_market: Some(header.trd_market as i32),
88 currency: currency_int,
89 option_strategy_view: option_strategy_view.then_some(true),
90 },
91 )
92 .await?;
93 let out: Vec<PositionOut> = list
94 .iter()
95 .map(|p| PositionOut {
96 position_id: p.position_id,
97 code: p.code.clone(),
98 name: p.name.clone(),
99 qty: p.qty,
100 can_sell_qty: p.can_sell_qty,
101 price: p.price,
102 cost_price: p.cost_price,
103 val: p.val,
104 pl_val: p.pl_val,
105 pl_ratio: p.pl_ratio,
106 diluted_cost_price: p.diluted_cost_price,
108 average_cost_price: p.average_cost_price,
109 average_pl_ratio: p.average_pl_ratio,
110 currency: p.currency,
111 trd_market: p.trd_market,
112 cost_basis_method_hint: derive_cost_basis_method_hint(p.trd_market, p.currency),
113 })
114 .collect();
115 Ok(serde_json::to_string_pretty(&out)?)
116}