futu_mcp/handlers/trade/
positions.rs1use std::sync::Arc;
5
6use anyhow::Result;
7use futu_net::client::FutuClient;
8use serde::Serialize;
9
10use super::helpers::*;
11
12#[derive(Serialize)]
13struct PositionOut {
14 position_id: u64,
15 code: String,
16 name: String,
17 qty: f64,
18 can_sell_qty: f64,
19 price: f64,
20 cost_price: f64,
22 val: f64,
23 pl_val: f64,
24 pl_ratio: f64,
25 #[serde(skip_serializing_if = "Option::is_none")]
29 diluted_cost_price: Option<f64>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 average_cost_price: Option<f64>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 average_pl_ratio: Option<f64>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 currency: Option<i32>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 trd_market: Option<i32>,
38 cost_basis_method_hint: &'static str,
44}
45
46pub fn derive_cost_basis_method_hint(
54 trd_market: Option<i32>,
55 currency: Option<i32>,
56) -> &'static str {
57 if trd_market == Some(15) {
59 return "average";
60 }
61 if currency == Some(4) {
63 return "average";
64 }
65 "diluted"
67}
68
69pub async fn get_positions(
70 client: &Arc<FutuClient>,
71 env: &str,
72 acc_id: u64,
73 market: &str,
74) -> Result<String> {
75 let header = build_header(env, acc_id, market)?;
76 let list = futu_trd::account::get_position_list(client, &header).await?;
77 let out: Vec<PositionOut> = list
78 .iter()
79 .map(|p| PositionOut {
80 position_id: p.position_id,
81 code: p.code.clone(),
82 name: p.name.clone(),
83 qty: p.qty,
84 can_sell_qty: p.can_sell_qty,
85 price: p.price,
86 cost_price: p.cost_price,
87 val: p.val,
88 pl_val: p.pl_val,
89 pl_ratio: p.pl_ratio,
90 diluted_cost_price: p.diluted_cost_price,
92 average_cost_price: p.average_cost_price,
93 average_pl_ratio: p.average_pl_ratio,
94 currency: p.currency,
95 trd_market: p.trd_market,
96 cost_basis_method_hint: derive_cost_basis_method_hint(p.trd_market, p.currency),
97 })
98 .collect();
99 Ok(serde_json::to_string_pretty(&out)?)
100}