export_balance.rs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. use std::collections::{BTreeMap};
  2. use std::str::FromStr;
  3. use chrono::NaiveDateTime;
  4. use rust_decimal::Decimal;
  5. // use rust_decimal::prelude::ToPrimitive;
  6. use rust_decimal_macros::dec;
  7. use tracing::{error, info};
  8. use crate::{export_template};
  9. use crate::swap_bybit::bybit_swap_rest_utils::BybitSwapRest;
  10. use crate::swap_gate::gate_swap_rest_utils::GateSwapRest;
  11. use crate::utils::utils::BalanceConfigInfo;
  12. pub async fn export_balance(config_info: BalanceConfigInfo) {
  13. let config_info_clone = config_info.clone();
  14. let start_time = NaiveDateTime::parse_from_str(&config_info.range_time[0].clone(), "%Y-%m-%d %H:%M:%S").unwrap().timestamp() - 8 * 3600;
  15. let end_time = NaiveDateTime::parse_from_str(&config_info.range_time[1].clone(), "%Y-%m-%d %H:%M:%S").unwrap().timestamp() - 8 * 3600;
  16. let count_start_time = NaiveDateTime::parse_from_str(&config_info.count_range_time[0].clone(), "%Y-%m-%d %H:%M:%S").unwrap().timestamp() - 8 * 3600;
  17. let count_end_time = NaiveDateTime::parse_from_str(&config_info.count_range_time[1].clone(), "%Y-%m-%d %H:%M:%S").unwrap().timestamp() - 8 * 3600;
  18. //获取不同账号的数据
  19. let mut all_account_balance_info: BTreeMap<String, Vec<Vec<String>>> = BTreeMap::new();
  20. let mut account_name_list: Vec<String> = vec![];
  21. if config_info_clone.account_list.len() == 0 {
  22. info!("没有账号信息");
  23. return;
  24. }
  25. for exchange in config_info.exchanges.clone() {
  26. let exchange_up = exchange.to_uppercase();
  27. match exchange_up.as_str() {
  28. "GATE" => {
  29. for account in config_info_clone.account_list.clone() {
  30. let mut account_info: BTreeMap<String, String> = BTreeMap::new();
  31. account_info.insert("account_name".to_string(), account[0].clone());
  32. account_info.insert("access_key".to_string(), account[1].clone());
  33. account_info.insert("secret_key".to_string(), account[2].clone());
  34. let mut gate_exc = GateSwapRest::new(false, account_info.clone());
  35. let data = gate_exc.account_book("usdt".to_string(), start_time, end_time).await;
  36. // info!("请求完成{:?}",data.clone());
  37. if data.code.as_str() == "200" {
  38. let account_name = account_info.get("account_name").unwrap();
  39. account_name_list.push(account_name.clone());
  40. //账号
  41. let json_value: serde_json::Value = serde_json::from_str(&data.data).unwrap();
  42. if let serde_json::Value::Array(array) = json_value {
  43. let mut name_data_all: Vec<Vec<String>> = vec![];
  44. let mut times = 0;
  45. for item in array {
  46. // info!("数据{:?}",item.clone().to_string());
  47. let time = item["time"].as_i64().unwrap();//秒级
  48. if time == times { continue; }
  49. times = time;
  50. let change = item["change"].as_str().unwrap();
  51. let balance = item["balance"].as_str().unwrap();
  52. let type_str = match item["type"].as_str().unwrap() {
  53. "dnw" => { "transfer" }
  54. "pnl" => { "profit" }
  55. "fee" => {
  56. "交易手续费";
  57. continue;
  58. }
  59. "refr" => {
  60. "推荐人返佣";
  61. continue;
  62. }
  63. "fund" => {
  64. "资金费用";
  65. continue;
  66. }
  67. "point_dnw" => {
  68. "点卡转入转出";
  69. continue;
  70. }
  71. "point_fee" => {
  72. "点卡交易手续费";
  73. continue;
  74. }
  75. "point_refr" => {
  76. "点卡推荐人返佣";
  77. continue;
  78. }
  79. _ => {
  80. "未知-变更类型";
  81. continue;
  82. }
  83. };
  84. let text = item["text"].as_str().unwrap();
  85. let contract = item["contract"].as_str().unwrap();
  86. let trade_id = item["trade_id"].as_str().unwrap();
  87. let mut name_data_array: Vec<String> = vec![];
  88. name_data_array.push(time.to_string());
  89. name_data_array.push(trade_id.to_string());
  90. name_data_array.push(change.to_string());
  91. name_data_array.push(balance.to_string());
  92. name_data_array.push(type_str.to_string());
  93. name_data_array.push(contract.to_string());
  94. name_data_array.push(text.to_string());
  95. name_data_all.push(name_data_array.clone());
  96. }
  97. all_account_balance_info.insert(account_name.clone(), name_data_all.clone());
  98. } else {
  99. info!("不是数组 检查数据");
  100. }
  101. }
  102. // break;
  103. };
  104. }
  105. "BYBIT" => {
  106. for account in config_info_clone.account_list.clone() {
  107. let mut account_info: BTreeMap<String, String> = BTreeMap::new();
  108. account_info.insert("account_name".to_string(), account[0].clone());
  109. account_info.insert("access_key".to_string(), account[1].clone());
  110. account_info.insert("secret_key".to_string(), account[2].clone());
  111. let mut bybit_exc = BybitSwapRest::new(false, account_info.clone());
  112. let data = bybit_exc.get_account_transaction_log(start_time * 1000, end_time * 1000).await;
  113. // info!("请求完成{:?}",data.clone());
  114. if data.code.as_str() == "200" {
  115. let account_name = account_info.get("account_name").unwrap();
  116. account_name_list.push(account_name.clone());
  117. //解析
  118. let json_value: serde_json::Value = serde_json::from_str(&data.data).unwrap();
  119. let ret_code = json_value.get("retCode").unwrap().as_i64().unwrap();
  120. let ret_msg = json_value.get("retMsg").unwrap().as_str().unwrap();
  121. if ret_code == 0 && ret_msg == "OK" {
  122. let result = json_value.get("result").unwrap();
  123. let list = result.get("list").unwrap();
  124. if let serde_json::Value::Array(array) = list {
  125. let mut name_data_all: Vec<Vec<String>> = vec![];
  126. for item in array {
  127. let id = item.get("id").unwrap().as_str().unwrap();
  128. let type_str = item.get("type").unwrap().as_str().unwrap();
  129. let change = item.get("change").unwrap().as_str().unwrap();
  130. let cash_balance = item.get("cashBalance").unwrap().as_str().unwrap();
  131. let transaction_time = item.get("transactionTime").unwrap().as_str().unwrap();
  132. let type_str = match type_str {
  133. "TRADE" => { "交易" }
  134. _ => {
  135. "其他-变更类型";
  136. continue;
  137. }
  138. };
  139. let time = transaction_time.parse::<i64>().unwrap() / 1000;
  140. let mut name_data_array: Vec<String> = vec![];
  141. name_data_array.push(time.to_string());
  142. name_data_array.push(id.to_string());
  143. name_data_array.push(change.to_string());
  144. name_data_array.push(cash_balance.to_string());
  145. name_data_array.push(type_str.to_string());
  146. name_data_array.push("".to_string());
  147. name_data_array.push("".to_string());
  148. name_data_all.push(name_data_array.clone());
  149. }
  150. all_account_balance_info.insert(account_name.clone(), name_data_all.clone());
  151. }
  152. } else {
  153. info!("解析失败:{:?}",json_value);
  154. continue;
  155. }
  156. }
  157. // break;
  158. };
  159. }
  160. _ => {
  161. error!("交易所输入错误!");
  162. panic!("交易所输入错误!")
  163. }
  164. };
  165. }
  166. // 根据账户变动填充
  167. let mut count_time_list = vec![];
  168. for (_account_name, balance_info) in all_account_balance_info.clone() {
  169. for value in balance_info {
  170. let time = value[0].clone().parse::<i64>().unwrap();
  171. if !count_time_list.contains(&time) && time > count_start_time && time < count_end_time {
  172. count_time_list.push(time)
  173. }
  174. }
  175. };
  176. count_time_list.sort_by(|a, b| a.cmp(&b));
  177. count_time_list.insert(0, count_start_time);
  178. count_time_list.push(count_end_time);
  179. let count_balance_info = supply_balance(all_account_balance_info.clone(), count_time_list.clone());
  180. let statistic_result = statistic_balance(count_balance_info.clone(), count_start_time.clone(), count_end_time.clone(), config_info.clone());
  181. // 根据小时填充
  182. let mut last_timestamp: i64 = -1;
  183. let mut hour_time_list: Vec<i64> = vec![];
  184. for item in start_time..end_time {
  185. if item / 3600 != last_timestamp / 3600 {
  186. last_timestamp = item;
  187. hour_time_list.push(last_timestamp);
  188. }
  189. }
  190. hour_time_list.push(end_time);
  191. let all_balance_info: Vec<Vec<String>> = supply_balance(all_account_balance_info.clone(), hour_time_list.clone());
  192. account_name_list.push("total_balance".to_string());
  193. match config_info_clone.export_mode {
  194. 1 => export_template::template_balance::export_html(config_info, &account_name_list, &hour_time_list, &all_balance_info, statistic_result),
  195. 2 => export_template::template_balance::export_html(config_info, &account_name_list, &count_time_list, &count_balance_info, statistic_result),
  196. _ => error!("导出模式错误,请检查导出模式!")
  197. };
  198. }
  199. pub fn statistic_balance(balance_info: Vec<Vec<String>>, count_start_time: i64, end_start_time: i64, _config: BalanceConfigInfo) -> String {
  200. let mut max_withdrawal = dec!(0);
  201. let mut max_total_withdrawal = dec!(0);
  202. let mut total_income = dec!(0);
  203. let mut balance_info_map: BTreeMap<String, Vec<Vec<String>>> = BTreeMap::new();
  204. for info in balance_info.clone() {
  205. let mut default_info = balance_info_map.get(&info[0]).unwrap_or(&vec![]).clone();
  206. default_info.push(info.clone());
  207. balance_info_map.insert(info[0].clone(), default_info);
  208. }
  209. for (account_name, balance_info) in balance_info_map.clone() {
  210. let mut max_price = dec!(0);
  211. let mut min_price = dec!(0);
  212. let mut withdrawal = dec!(0);
  213. for (index, info) in balance_info.iter().enumerate() {
  214. let present_price = Decimal::from_str(&info[4]).unwrap();
  215. if index == 0 || max_price < present_price {
  216. max_price = present_price;
  217. min_price = present_price;
  218. continue;
  219. }
  220. let present_withdrawal = ((max_price - min_price) / max_price * dec!(100)).round_dp(2);
  221. if present_withdrawal > withdrawal { withdrawal = present_withdrawal }
  222. if max_price > present_price && present_price < min_price {
  223. min_price = present_price;
  224. }
  225. }
  226. if account_name != "total_balance" {
  227. if max_withdrawal < withdrawal { max_withdrawal = withdrawal }
  228. } else {
  229. max_total_withdrawal = withdrawal;
  230. let present = Decimal::from_str(&balance_info[0][4]).unwrap();
  231. let past = Decimal::from_str(&balance_info[balance_info.len() - 1][4]).unwrap();
  232. total_income = ((past - present) / present * dec!(100)).round_dp(2);
  233. }
  234. }
  235. let start_time = NaiveDateTime::from_timestamp_millis((count_start_time + 8 * 3600) * 1000).unwrap().format("%d日%H点").to_string();
  236. let end_time = NaiveDateTime::from_timestamp_millis((end_start_time + 8 * 3600) * 1000).unwrap().format("%d日%H点").to_string();
  237. format!("{}到{},总收益约为{}%,最大回撤约为{}%,其中单个账号最大回撤约为{}%。", start_time, end_time, total_income, max_total_withdrawal, max_withdrawal)
  238. }
  239. pub fn supply_balance(source_balance_info: BTreeMap<String, Vec<Vec<String>>>, time_slicer: Vec<i64>) -> Vec<Vec<String>> {
  240. let mut balance_info_list: Vec<Vec<String>> = vec![];
  241. for time in time_slicer.clone() {
  242. // 根据时间片去拿数据 ,如果有添加没有,手动添加
  243. let mut total_price = Decimal::ZERO;
  244. for (key, value) in source_balance_info.clone() {
  245. let mut balance_info = value.clone();
  246. balance_info.sort_by(|a, b| a[0].cmp(&b[0]));
  247. let mut new_info: Option<Vec<String>> = None;
  248. for mut info in balance_info.clone() {
  249. let info_time = info[0].parse::<i64>().unwrap();
  250. if info_time == time {
  251. match new_info {
  252. None => {
  253. info.insert(0, key.clone());
  254. new_info = Option::from(info);//记录当前实际
  255. }
  256. Some(ref value) => {
  257. if value[1] < info[1] {
  258. info.insert(0, key.clone());
  259. new_info = Option::from(info);//记录当前实际
  260. }
  261. }
  262. }
  263. break;
  264. }
  265. }
  266. match new_info {
  267. None => {
  268. let balance_info_filter: Vec<Vec<String>> = balance_info.iter().filter(|item| {
  269. item[0].parse::<i64>().unwrap() < time
  270. }).cloned().collect();
  271. let mut last_balance_info = if balance_info_filter.len() == 0 {
  272. balance_info[0].clone()
  273. } else {
  274. balance_info_filter[balance_info_filter.len() - 1].clone()
  275. };
  276. last_balance_info.insert(0, key.clone());
  277. total_price += Decimal::from_str(&last_balance_info[4]).unwrap();
  278. balance_info_list.push(last_balance_info);
  279. }
  280. Some(ref info) => {
  281. total_price += Decimal::from_str(&info[4]).unwrap();
  282. balance_info_list.push(info.clone());
  283. }
  284. }
  285. }
  286. let sum_row: Vec<String> = vec!["total_balance".to_string(), time.to_string(), "".to_string(), "0".to_string(), total_price.round_dp(2).to_string(), "".to_string(), "".to_string(), "".to_string()];
  287. balance_info_list.push(sum_row.clone());
  288. };
  289. balance_info_list
  290. }