|
|
@@ -0,0 +1,1072 @@
|
|
|
+// use std::collections::{BTreeMap, HashMap};
|
|
|
+// use std::io::{Error, ErrorKind};
|
|
|
+// use std::str::FromStr;
|
|
|
+// use tokio::sync::mpsc::Sender;
|
|
|
+// use async_trait::async_trait;
|
|
|
+// use futures::stream::FuturesUnordered;
|
|
|
+// use futures::TryStreamExt;
|
|
|
+// use rust_decimal::Decimal;
|
|
|
+// use rust_decimal::prelude::FromPrimitive;
|
|
|
+// use serde::{Deserialize, Serialize};
|
|
|
+// use serde_json::json;
|
|
|
+// use tokio::time::Instant;
|
|
|
+// use tracing::{debug, error};
|
|
|
+// use exchanges::okx_swap_rest::OkxSwapRest;
|
|
|
+// use global::trace_stack::TraceStack;
|
|
|
+// use crate::exchange::ExchangeEnum;
|
|
|
+// use crate::{Account, Market, Order, OrderCommand, Platform, Position, PositionModeEnum, Ticker, utils};
|
|
|
+//
|
|
|
+// /// Okx交易所账户信息请求数据结构
|
|
|
+// /// - 接口`"/api/v5/account/balance"`
|
|
|
+// ///
|
|
|
+// /// struct SwapAccount
|
|
|
+// /// - `adj_eq`: String, 美金层面有效保证金
|
|
|
+// /// - `borrow_froz`: String, 账户美金层面潜在借币占用保证金
|
|
|
+// /// - `details`: Vec<SwapAccountDetails>, 各币种资产详细信息
|
|
|
+// /// - `imr`: String, 美金层面占用保证金
|
|
|
+// /// - `iso_eq`: String, 美金层面逐仓仓位权益
|
|
|
+// /// - `mgn_ratio`: String, 美金层面保证金率
|
|
|
+// /// - `mmr`: String, 美金层面维持保证金
|
|
|
+// /// - `notional_usd`: String, 以美金价值为单位的持仓数量,即仓位美金价值
|
|
|
+// /// - `ord_froz`: String, 美金层面全仓挂单占用保证金
|
|
|
+// /// - `total_eq`: String, 美金层面权益
|
|
|
+// /// - `u_time`: String, 账户信息的更新时间
|
|
|
+// ///
|
|
|
+// /// struct SwapAccountDetails
|
|
|
+// /// - `avail_bal`: Decimal, 可用余额
|
|
|
+// /// - `avail_eq`: Decimal, 可用保证金
|
|
|
+// /// - `cash_bal`: Decimal, 币种余额
|
|
|
+// /// - `ccy`: String, 币种
|
|
|
+// /// - `cross_liab`: String, 币种全仓负债额
|
|
|
+// /// - `dis_eq`: Decimal, 美金层面币种折算权益
|
|
|
+// /// - `eq`: Decimal, 币种总权益
|
|
|
+// /// - `eq_usd`: Decimal, 币种权益美金价值
|
|
|
+// /// - `fixed_bal`: Decimal, 币种冻结金额
|
|
|
+// /// - `frozen_bal`: Decimal, 币种占用金额
|
|
|
+// /// - `interest`:String, 计息,应扣未扣利息。
|
|
|
+// /// - `iso_eq`: Decimal, 币种逐仓仓位权益
|
|
|
+// /// - `iso_liab`: String, 逐仓未实现盈亏
|
|
|
+// /// - `iso_upl`: Decimal, 币种逐仓负债额
|
|
|
+// /// - `liab`: String, 币种负债额
|
|
|
+// /// - `max_loan`: String, 币种最大可借
|
|
|
+// /// - `mgn_ratio`: String, 保证金率
|
|
|
+// /// - `notional_lever`: Decimal, 币种杠杆倍数
|
|
|
+// /// - `ord_frozen`: Decimal, 挂单冻结数量
|
|
|
+// /// - `twap`: Decimal, 当前负债币种触发系统自动换币的风险
|
|
|
+// /// - `u_time`: String, 币种余额信息的更新时间
|
|
|
+// /// - `upl`: Decimal, 未实现盈亏
|
|
|
+// /// - `upl_liab`: String, 由于仓位未实现亏损导致的负债
|
|
|
+// /// - `stgy_eq`: Decimal, 策略权益
|
|
|
+// /// - `spot_in_use_amt`: String, 现货对冲占用数量
|
|
|
+// /// - `borrow_froz`: String, 币种美金层面潜在借币占用保证金
|
|
|
+// #[derive(Debug, Deserialize, Serialize)]
|
|
|
+// #[serde(rename_all = "camelCase")]
|
|
|
+// struct SwapAccount {
|
|
|
+// adj_eq: String,
|
|
|
+// borrow_froz: String,
|
|
|
+// details: Vec<SwapAccountDetails>,
|
|
|
+// imr: String,
|
|
|
+// iso_eq: String,
|
|
|
+// mgn_ratio: String,
|
|
|
+// mmr: String,
|
|
|
+// notional_usd: String,
|
|
|
+// ord_froz: String,
|
|
|
+// total_eq: String,
|
|
|
+// u_time: String,
|
|
|
+// }
|
|
|
+//
|
|
|
+// #[derive(Debug, Clone, Deserialize, Serialize)]
|
|
|
+// #[serde(rename_all = "camelCase")]
|
|
|
+// struct SwapAccountDetails {
|
|
|
+// avail_bal: Decimal,
|
|
|
+// avail_eq: Decimal,
|
|
|
+// cash_bal: Decimal,
|
|
|
+// ccy: String,
|
|
|
+// cross_liab: String,
|
|
|
+// dis_eq: Decimal,
|
|
|
+// eq: Decimal,
|
|
|
+// eq_usd: Decimal,
|
|
|
+// fixed_bal: Decimal,
|
|
|
+// frozen_bal: Decimal,
|
|
|
+// interest: String,
|
|
|
+// iso_eq: Decimal,
|
|
|
+// iso_liab: String,
|
|
|
+// iso_upl: Decimal,
|
|
|
+// liab: String,
|
|
|
+// max_loan: String,
|
|
|
+// mgn_ratio: String,
|
|
|
+// notional_lever: Decimal,
|
|
|
+// ord_frozen: Decimal,
|
|
|
+// twap: Decimal,
|
|
|
+// u_time: String,
|
|
|
+// upl: Decimal,
|
|
|
+// upl_liab: String,
|
|
|
+// stgy_eq: Decimal,
|
|
|
+// spot_in_use_amt: String,
|
|
|
+// borrow_froz: String,
|
|
|
+// }
|
|
|
+//
|
|
|
+// /// Okx交易所持仓信息请求数据结构
|
|
|
+// /// - 接口`"/api/v5/account/positions"`
|
|
|
+// ///
|
|
|
+// /// struct SwapPosition
|
|
|
+// /// - `adl`: Decimal, 信号区
|
|
|
+// /// - `avail_pos`: Decimal, 可平仓数量,适用于 币币杠杆,交割/永续(开平仓模式)
|
|
|
+// /// - `avg_px`: Decimal, 开仓平均价
|
|
|
+// /// - `c_time`: String, 持仓创建时间
|
|
|
+// /// - `ccy`: String, 占用保证金的币种
|
|
|
+// /// - `delta_b_s`: String, 美金本位持仓仓位delta,仅适用于期权
|
|
|
+// /// - `delta_p_a`: String, 币本位持仓仓位delta,仅适用于期权
|
|
|
+// /// - `gamma_b_s`: String, 美金本位持仓仓位gamma,仅适用于期权
|
|
|
+// /// - `gamma_p_a`: String, 币本位持仓仓位gamma,仅适用于期权
|
|
|
+// /// - `imr`: String, 初始保证金,仅适用于全仓
|
|
|
+// /// - `inst_id`: String, 产品ID
|
|
|
+// /// - `inst_type`: String, 产品类型
|
|
|
+// /// - `interest`: Decimal, 利息,已经生成的未扣利息
|
|
|
+// /// - `idx_px`: Decimal, 最新指数价格
|
|
|
+// /// - `last`: Decimal, 最新成交价
|
|
|
+// /// - `usd_px`: String, 美金价格
|
|
|
+// /// - `be_px`: Decimal, 盈亏平衡价
|
|
|
+// /// - `lever`: Decimal, 杠杆倍数,不适用于期权
|
|
|
+// /// - `liab`: String, 负债额,仅适用于币币杠杆
|
|
|
+// /// - `liab_ccy`: String, 负债币种,仅适用于币币杠杆
|
|
|
+// /// - `liq_px`: Decimal, 预估强平价
|
|
|
+// /// - `mark_px`: Decimal, 最新标记价格
|
|
|
+// /// - `margin`: Decimal, 保证金余额,可增减,仅适用于逐仓
|
|
|
+// /// - `mgn_mode`: Decimal, 保证金模式
|
|
|
+// /// - `mgn_ratio`: Decimal, 保证金率
|
|
|
+// /// - `mmr`: Decimal, 维持保证金
|
|
|
+// /// - `notional_usd`: Decimal, 以美金价值为单位的持仓数量
|
|
|
+// /// - `opt_val`: String, 期权市值,仅适用于期权
|
|
|
+// /// - `p_time`: String,
|
|
|
+// /// - `pos`: Decimal, 持仓数量,逐仓自主划转模式下,转入保证金后会产生pos为0的仓位
|
|
|
+// /// - `pos_ccy`: String, 仓位资产币种,仅适用于币币杠杆仓位
|
|
|
+// /// - `pos_id`: Decimal, 持仓ID
|
|
|
+// /// - `pos_side`: String, 持仓方向
|
|
|
+// /// - `spot_in_use_amt`: String,
|
|
|
+// /// - `spot_in_use_ccy`: String,
|
|
|
+// /// - `theta_b_s`: String, 美金本位持仓仓位theta,仅适用于期权
|
|
|
+// /// - `theta_p_a`: String, 币本位持仓仓位theta,仅适用于期权
|
|
|
+// /// - `trade_id`: Decimal, 最新成交ID
|
|
|
+// /// - `biz_ref_id`: String, 外部业务id
|
|
|
+// /// - `biz_ref_type`: String, 外部业务类型
|
|
|
+// /// - `quote_bal`: Decimal, 计价币余额 ,适用于 币币杠杆(逐仓自主划转模式 和 一键借币模式)
|
|
|
+// /// - `base_bal`: Decimal, 交易币余额,适用于 币币杠杆(逐仓自主划转模式 和 一键借币模式)
|
|
|
+// /// - `base_borrowed`: String, 交易币已借,适用于 币币杠杆(逐仓一键借币模式)
|
|
|
+// /// - `base_interest`: String, 交易币计息,适用于 币币杠杆(逐仓一键借币模式)
|
|
|
+// /// - `quote_borrowed`: String, 计价币已借,适用于 币币杠杆(逐仓一键借币模式)
|
|
|
+// /// - `quote_interest`: String, 计价币计息,适用于 币币杠杆(逐仓一键借币模式)
|
|
|
+// /// - `u_time`: String, 最近一次持仓更新时间
|
|
|
+// /// - `upl`: Decimal, 未实现收益(以标记价格计算)
|
|
|
+// /// - `upl_last_px`: Decimal, 以最新成交价格计算的未实现收益,主要做展示使用,实际值还是 upl
|
|
|
+// /// - `upl_ratio`: Decimal, 未实现收益率(以标记价格计算
|
|
|
+// /// - `upl_ratio_last_px`: Decimal, 以最新成交价格计算的未实现收益率
|
|
|
+// /// - `vega_b_s`: String, 美金本位持仓仓位vega,仅适用于期权
|
|
|
+// /// - `vega_p_a`: String, 币本位持仓仓位vega,仅适用于期权
|
|
|
+// /// - `realized_pnl`: Decimal, 已实现收益
|
|
|
+// /// - `pnl`: Decimal, 平仓订单累计收益额
|
|
|
+// /// - `fee`: Decimal, 累计手续费金额,正数代表平台返佣 ,负数代表平台扣除
|
|
|
+// /// - `funding_fee`: Decimal, 累计资金费用
|
|
|
+// /// - `liq_penalty`: Decimal, 累计爆仓罚金,有值时为负数。
|
|
|
+// /// - `close_order_algo`: Vec<SwapPositionCloseOrderAlgo>, 平仓策略委托订单
|
|
|
+// ///
|
|
|
+// /// struct SwapPositionCloseOrderAlgo
|
|
|
+// ///
|
|
|
+// /// - `algo_id`: String, 策略委托单ID
|
|
|
+// /// - `sl_trigger_px`: Decimal, 止损触发价
|
|
|
+// /// - `sl_trigger_px_type`: String, 止损触发价类型
|
|
|
+// /// - `tp_trigger_px`: Decimal, 止盈委托价
|
|
|
+// /// - `tp_trigger_px_type`: String, 止盈触发价类型
|
|
|
+// /// - `close_fraction`: Decimal, 策略委托触发时
|
|
|
+// #[derive(Debug, Deserialize, Serialize)]
|
|
|
+// #[serde(rename_all = "camelCase")]
|
|
|
+// pub struct SwapPosition {
|
|
|
+// pub adl: String,
|
|
|
+// pub avail_pos: String,
|
|
|
+// pub avg_px: Decimal,
|
|
|
+// pub c_time: String,
|
|
|
+// pub ccy: String,
|
|
|
+// pub delta_b_s: String,
|
|
|
+// pub delta_p_a: String,
|
|
|
+// pub gamma_b_s: String,
|
|
|
+// pub gamma_p_a: String,
|
|
|
+// pub imr: String,
|
|
|
+// pub inst_id: String,
|
|
|
+// pub inst_type: String,
|
|
|
+// pub interest: String,
|
|
|
+// pub idx_px: String,
|
|
|
+// pub last: String,
|
|
|
+// pub usd_px: String,
|
|
|
+// pub be_px: String,
|
|
|
+// pub lever: Decimal,
|
|
|
+// pub liab: String,
|
|
|
+// pub liab_ccy: String,
|
|
|
+// pub liq_px: String,
|
|
|
+// pub mark_px: String,
|
|
|
+// pub margin: String,
|
|
|
+// pub mgn_mode: String,
|
|
|
+// pub mgn_ratio: String,
|
|
|
+// pub mmr: String,
|
|
|
+// pub notional_usd: String,
|
|
|
+// pub opt_val: String,
|
|
|
+// pub pos: Decimal,
|
|
|
+// pub pos_ccy: String,
|
|
|
+// pub pos_id: String,
|
|
|
+// pub pos_side: String,
|
|
|
+// pub spot_in_use_amt: String,
|
|
|
+// pub spot_in_use_ccy: String,
|
|
|
+// pub theta_b_s: String,
|
|
|
+// pub theta_p_a: String,
|
|
|
+// pub trade_id: String,
|
|
|
+// pub biz_ref_id: String,
|
|
|
+// pub biz_ref_type: String,
|
|
|
+// pub quote_bal: String,
|
|
|
+// pub base_bal: String,
|
|
|
+// pub base_borrowed: String,
|
|
|
+// pub base_interest: String,
|
|
|
+// pub quote_borrowed: String,
|
|
|
+// pub quote_interest: String,
|
|
|
+// pub u_time: String,
|
|
|
+// pub upl: Decimal,
|
|
|
+// pub upl_last_px: String,
|
|
|
+// pub upl_ratio: String,
|
|
|
+// pub upl_ratio_last_px: String,
|
|
|
+// pub vega_b_s: String,
|
|
|
+// pub vega_p_a: String,
|
|
|
+// pub realized_pnl: String,
|
|
|
+// pub pnl: String,
|
|
|
+// pub fee: String,
|
|
|
+// pub funding_fee: String,
|
|
|
+// pub liq_penalty: String,
|
|
|
+// pub close_order_algo: Vec<SwapPositionCloseOrderAlgo>,
|
|
|
+// }
|
|
|
+//
|
|
|
+// #[derive(Debug, Deserialize, Serialize)]
|
|
|
+// #[serde(rename_all = "camelCase")]
|
|
|
+// pub struct SwapPositionCloseOrderAlgo {
|
|
|
+// pub algo_id: String,
|
|
|
+// pub sl_trigger_px: String,
|
|
|
+// pub sl_trigger_px_type: String,
|
|
|
+// pub tp_trigger_px: String,
|
|
|
+// pub tp_trigger_px_type: String,
|
|
|
+// pub close_fraction: String,
|
|
|
+// }
|
|
|
+//
|
|
|
+// /// Okx交易所行情信息请求数据结构
|
|
|
+// /// - 接口`"/api/v5/market/ticker"`
|
|
|
+// ///
|
|
|
+// /// struct SwapTicker
|
|
|
+// /// - `inst_type`: String, 产品类型
|
|
|
+// /// - `inst_id`: String, 产品ID
|
|
|
+// /// - `last`: Decimal, 最新成交价
|
|
|
+// /// - `last_sz`: Decimal, 最新成交的数量
|
|
|
+// /// - `ask_px`: Decimal, 卖一价
|
|
|
+// /// - `ask_sz`: Decimal, 卖一价的挂单数数量
|
|
|
+// /// - `bid_px`: Decimal, 买一价
|
|
|
+// /// - `bid_sz`: Decimal, 买一价的挂单数量
|
|
|
+// /// - `open24h`: Decimal, 24小时开盘价
|
|
|
+// /// - `high24h`: Decimal, 24小时最高价
|
|
|
+// /// - `low24h`: Decimal, 24小时最低价
|
|
|
+// /// - `vol_ccy24h`: Decimal, 24小时成交量,以币为单位
|
|
|
+// /// - `vol24h`: Decimal, 24小时成交量,以张为单位
|
|
|
+// /// - `ts`: String, ticker数据产生时间
|
|
|
+// /// - `sod_utc0`: Decimal, UTC 0 时开盘价
|
|
|
+// /// - `sod_utc8`: Decimal, UTC+8 时开盘价
|
|
|
+// #[derive(Debug, Clone, Deserialize, Serialize)]
|
|
|
+// #[serde(rename_all = "camelCase")]
|
|
|
+// struct SwapTicker {
|
|
|
+// inst_type: String,
|
|
|
+// inst_id: String,
|
|
|
+// last: Decimal,
|
|
|
+// last_sz: Decimal,
|
|
|
+// ask_px: Decimal,
|
|
|
+// ask_sz: Decimal,
|
|
|
+// bid_px: Decimal,
|
|
|
+// bid_sz: Decimal,
|
|
|
+// open24h: Decimal,
|
|
|
+// high24h: Decimal,
|
|
|
+// low24h: Decimal,
|
|
|
+// vol_ccy24h: Decimal,
|
|
|
+// vol24h: Decimal,
|
|
|
+// ts: String,
|
|
|
+// sod_utc0: Decimal,
|
|
|
+// sod_utc8: Decimal,
|
|
|
+// }
|
|
|
+//
|
|
|
+// /// Okx交易所市场信息请求数据结构
|
|
|
+// /// - 接口`"/api/v5/public/instruments"`
|
|
|
+// ///
|
|
|
+// /// struct SwapMarket
|
|
|
+// /// - `alias`: String,
|
|
|
+// /// - `base_ccy`: String,
|
|
|
+// /// - `category`: String,
|
|
|
+// /// - `ct_mult`: Decimal,
|
|
|
+// /// - `ct_type`: String,
|
|
|
+// /// - `ct_val`: Decimal,
|
|
|
+// /// - `ct_val_ccy`: String,
|
|
|
+// /// - `exp_time`: String,
|
|
|
+// /// - `inst_family`: String,
|
|
|
+// /// - `inst_id`: String,
|
|
|
+// /// - `inst_type`: String,
|
|
|
+// /// - `lever`: Decimal,
|
|
|
+// /// - `list_time`: String,
|
|
|
+// /// - `lot_sz`: Decimal,
|
|
|
+// /// - `max_iceberg_sz`: Decimal,
|
|
|
+// /// - `max_lmt_sz`: Decimal,
|
|
|
+// /// - `max_mkt_sz`: Decimal,
|
|
|
+// /// - `max_stop_sz`: Decimal,
|
|
|
+// /// - `max_trigger_sz`: Decimal,
|
|
|
+// /// - `max_twap_sz`: Decimal,
|
|
|
+// /// - `min_sz`: Decimal,
|
|
|
+// /// - `opt_type`: String,
|
|
|
+// /// - `quote_ccy`: String,
|
|
|
+// /// - `settle_ccy`: String,
|
|
|
+// /// - `state`: String,
|
|
|
+// /// - `stk`: String,
|
|
|
+// /// - `tick_sz`: Decimal,
|
|
|
+// /// - `uly`: String,
|
|
|
+// #[derive(Debug, Clone, Deserialize, Serialize)]
|
|
|
+// #[serde(rename_all = "camelCase")]
|
|
|
+// struct SwapMarket {
|
|
|
+// alias: String,
|
|
|
+// base_ccy: String,
|
|
|
+// category: String,
|
|
|
+// ct_mult: Decimal,
|
|
|
+// ct_type: String,
|
|
|
+// ct_val: Decimal,
|
|
|
+// ct_val_ccy: String,
|
|
|
+// exp_time: String,
|
|
|
+// inst_family: String,
|
|
|
+// inst_id: String,
|
|
|
+// inst_type: String,
|
|
|
+// lever: Decimal,
|
|
|
+// list_time: String,
|
|
|
+// lot_sz: Decimal,
|
|
|
+// max_iceberg_sz: Decimal,
|
|
|
+// max_lmt_sz: Decimal,
|
|
|
+// max_mkt_sz: Decimal,
|
|
|
+// max_stop_sz: Decimal,
|
|
|
+// max_trigger_sz: Decimal,
|
|
|
+// max_twap_sz: Decimal,
|
|
|
+// min_sz: Decimal,
|
|
|
+// opt_type: String,
|
|
|
+// quote_ccy: String,
|
|
|
+// settle_ccy: String,
|
|
|
+// state: String,
|
|
|
+// stk: String,
|
|
|
+// tick_sz: Decimal,
|
|
|
+// uly: String,
|
|
|
+// }
|
|
|
+//
|
|
|
+// /// Okx交易所订单信息请求数据结构
|
|
|
+// /// - 接口`"/api/v5/trade/order"`
|
|
|
+// ///
|
|
|
+// /// struct SwapOrder
|
|
|
+// /// - `inst_type`: String, 产品类型
|
|
|
+// /// - `inst_id`: String, 产品ID
|
|
|
+// /// - `ccy`: String, 保证金币种
|
|
|
+// /// - `ord_id`: String, 订单ID
|
|
|
+// /// - `cl_ord_id`: String, 客户自定义订单ID
|
|
|
+// /// - `tag`: String, 订单标签
|
|
|
+// /// - `px`: Decimal, 委托价格
|
|
|
+// /// - `px_usd`: String, 期权价格
|
|
|
+// /// - `px_vol`: String, 期权订单的隐含波动率
|
|
|
+// /// - `px_type`: String, 期权的价格类型
|
|
|
+// /// - `sz`: Decimal, 委托数量
|
|
|
+// /// - `pnl`: Decimal, 收益
|
|
|
+// /// - `ord_type`: String, 订单类型
|
|
|
+// /// - `side`: String, 订单方向
|
|
|
+// /// - `pos_side`: String, 持仓方向
|
|
|
+// /// - `td_mode`: String, 交易模式
|
|
|
+// /// - `acc_fill_sz`: Decimal, 累计成交数量
|
|
|
+// /// - `fill_px`: String, 最新成交价格,如果成交数量为0,该字段为""
|
|
|
+// /// - `trade_id`: String, 最新成交ID
|
|
|
+// /// - `fill_sz`: Decimal, 最新成交数量
|
|
|
+// /// - `fill_time`: String, 最新成交时间
|
|
|
+// /// - `source`: String, 订单来源
|
|
|
+// /// - `state`: String, 订单状态
|
|
|
+// /// - `avg_px`: Decimal, 成交均价,如果成交数量为0,该字段也为""
|
|
|
+// /// - `lever`: Decimal, 杠杆倍数
|
|
|
+// /// - `attach_algo_cl_ord_id`: String, 下单附带止盈止损时,客户自定义的策略订单ID
|
|
|
+// /// - `tp_trigger_px`: Decimal, 止盈触发价
|
|
|
+// /// - `tp_trigger_px_type`: String, 止盈触发价类型
|
|
|
+// /// - `tp_ord_px`: Decimal, 止盈委托价
|
|
|
+// /// - `sl_trigger_px`: Decimal, 止损触发价
|
|
|
+// /// - `sl_trigger_px_type`: String, 止损触发价类型
|
|
|
+// /// - `sl_ord_px`: Decimal, 止损委托价
|
|
|
+// /// - `stp_id`: String, 自成交保护ID
|
|
|
+// /// - `stp_mode`: String, 自成交保护模式
|
|
|
+// /// - `fee_ccy`: String, 交易手续费币种
|
|
|
+// /// - `fee`: Decimal, 手续费与返佣
|
|
|
+// /// - `rebate_ccy`: String, 返佣金币种
|
|
|
+// /// - `rebate`: String, 返佣金额,仅适用于币币和杠杆
|
|
|
+// /// - `tgt_ccy`: String, 币币市价单委托数量sz的单位
|
|
|
+// /// - `category`: String, 订单种类
|
|
|
+// /// - `reduce_only`: String, 是否只减仓
|
|
|
+// /// - `cancel_source`: String, 订单取消来源的原因枚举值代码
|
|
|
+// /// - `cancel_source_reason`: String, 订单取消来源的对应具体原因
|
|
|
+// /// - `quick_mgn_type`: String, 一键借币类型,仅适用于杠杆逐仓的一键借币模式
|
|
|
+// /// - `algo_cl_ord_id`: String, 客户自定义策略订单ID
|
|
|
+// /// - `algo_id`: String, 策略委托单ID,策略订单触发时有值,否则为""
|
|
|
+// /// - `u_time`: String, 订单状态更新时间
|
|
|
+// /// - `c_time`: String, 订单创建时间
|
|
|
+// #[derive(Debug, Clone, Deserialize, Serialize)]
|
|
|
+// #[serde(rename_all = "camelCase")]
|
|
|
+// pub struct SwapOrder {
|
|
|
+// inst_type: String,
|
|
|
+// inst_id: String,
|
|
|
+// ccy: String,
|
|
|
+// ord_id: String,
|
|
|
+// cl_ord_id: String,
|
|
|
+// tag: String,
|
|
|
+// px: Decimal,
|
|
|
+// px_usd: String,
|
|
|
+// px_vol: String,
|
|
|
+// px_type: String,
|
|
|
+// sz: Decimal,
|
|
|
+// pnl: String,
|
|
|
+// ord_type: String,
|
|
|
+// side: String,
|
|
|
+// pos_side: String,
|
|
|
+// td_mode: String,
|
|
|
+// acc_fill_sz: Decimal,
|
|
|
+// fill_px: String,
|
|
|
+// trade_id: String,
|
|
|
+// fill_sz: String,
|
|
|
+// fill_time: String,
|
|
|
+// source: String,
|
|
|
+// state: String,
|
|
|
+// avg_px: String,
|
|
|
+// lever: String,
|
|
|
+// attach_algo_cl_ord_id: String,
|
|
|
+// tp_trigger_px: String,
|
|
|
+// tp_trigger_px_type: String,
|
|
|
+// tp_ord_px: String,
|
|
|
+// sl_trigger_px: String,
|
|
|
+// sl_trigger_px_type: String,
|
|
|
+// sl_ord_px: String,
|
|
|
+// stp_id: String,
|
|
|
+// stp_mode: String,
|
|
|
+// fee_ccy: String,
|
|
|
+// fee: String,
|
|
|
+// rebate_ccy: String,
|
|
|
+// rebate: String,
|
|
|
+// tgt_ccy: String,
|
|
|
+// category: String,
|
|
|
+// reduce_only: String,
|
|
|
+// cancel_source: String,
|
|
|
+// cancel_source_reason: String,
|
|
|
+// quick_mgn_type: String,
|
|
|
+// algo_cl_ord_id: String,
|
|
|
+// algo_id: String,
|
|
|
+// u_time: String,
|
|
|
+// c_time: String,
|
|
|
+// }
|
|
|
+//
|
|
|
+// #[allow(dead_code)]
|
|
|
+// #[derive(Clone)]
|
|
|
+// pub struct OkxSwap {
|
|
|
+// exchange: ExchangeEnum,
|
|
|
+// symbol: String,
|
|
|
+// is_colo: bool,
|
|
|
+// params: BTreeMap<String, String>,
|
|
|
+// request: OkxSwapRest,
|
|
|
+// market: Market,
|
|
|
+// order_sender: Sender<Order>,
|
|
|
+// error_sender: Sender<Error>,
|
|
|
+// }
|
|
|
+//
|
|
|
+// impl OkxSwap {
|
|
|
+// pub async fn new(symbol: String, is_colo: bool, params: BTreeMap<String, String>, order_sender: Sender<Order>, error_sender: Sender<Error>) -> OkxSwap {
|
|
|
+// let market = Market::new();
|
|
|
+// let mut okx_swap = OkxSwap {
|
|
|
+// exchange: ExchangeEnum::OkxSwap,
|
|
|
+// symbol: symbol.to_uppercase(),
|
|
|
+// is_colo,
|
|
|
+// params: params.clone(),
|
|
|
+// request: OkxSwapRest::new(is_colo, params.clone()),
|
|
|
+// market,
|
|
|
+// order_sender,
|
|
|
+// error_sender,
|
|
|
+// };
|
|
|
+// okx_swap.market = OkxSwap::get_market(&mut okx_swap).await.unwrap_or(okx_swap.market);
|
|
|
+// return okx_swap;
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// #[async_trait]
|
|
|
+// impl Platform for OkxSwap {
|
|
|
+// fn clone_box(&self) -> Box<dyn Platform + Send + Sync> { Box::new(self.clone()) }
|
|
|
+//
|
|
|
+// fn get_self_exchange(&self) -> ExchangeEnum { ExchangeEnum::OkxSwap }
|
|
|
+//
|
|
|
+// fn get_self_symbol(&self) -> String { self.symbol.clone() }
|
|
|
+//
|
|
|
+// fn get_self_is_colo(&self) -> bool { self.is_colo }
|
|
|
+//
|
|
|
+// fn get_self_params(&self) -> BTreeMap<String, String> { self.params.clone() }
|
|
|
+//
|
|
|
+// fn get_self_market(&self) -> Market { self.market.clone() }
|
|
|
+//
|
|
|
+// fn get_request_delays(&self) -> Vec<i64> { self.request.get_delays() }
|
|
|
+//
|
|
|
+// fn get_request_avg_delay(&self) -> Decimal { self.request.get_avg_delay() }
|
|
|
+//
|
|
|
+// fn get_request_max_delay(&self) -> i64 { self.request.get_max_delay() }
|
|
|
+//
|
|
|
+// async fn get_server_time(&mut self) -> Result<String, Error> {
|
|
|
+// let res_data = self.request.get_server_time().await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let res_data_json: serde_json::Value = serde_json::from_str(res_data_str).unwrap();
|
|
|
+// let result = res_data_json[0]["ts"].as_str().unwrap().to_string();
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_account(&mut self) -> Result<Account, Error> {
|
|
|
+// let symbol_array: Vec<&str> = self.symbol.split("_").collect();
|
|
|
+// let res_data = self.request.get_balance(symbol_array[1].to_string()).await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let balance_info_list: Vec<SwapAccount> = serde_json::from_str(res_data_str).unwrap();
|
|
|
+// let detail = balance_info_list[0].details.iter().find(|&item| item.ccy == symbol_array[1]);
|
|
|
+// match detail {
|
|
|
+// None => {
|
|
|
+// error!("Okx:获取Account信息错误!\nhandle_swap_account:res_data={:?}", res_data);
|
|
|
+// panic!("Okx:获取Account信息错误!\nhandle_swap_account:res_data={:?}", res_data)
|
|
|
+// }
|
|
|
+// Some(value) => {
|
|
|
+// let result = Account {
|
|
|
+// coin: value.ccy.to_uppercase(),
|
|
|
+// balance: value.avail_bal + value.fixed_bal,
|
|
|
+// available_balance: value.avail_bal,
|
|
|
+// frozen_balance: value.fixed_bal,
|
|
|
+// stocks: Decimal::ZERO,
|
|
|
+// available_stocks: Decimal::ZERO,
|
|
|
+// frozen_stocks: Decimal::ZERO,
|
|
|
+// };
|
|
|
+// Ok(result)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_spot_account(&mut self) -> Result<Vec<Account>, Error> {
|
|
|
+// Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string()))
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_position(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
+// let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.to_string(), "-"));
|
|
|
+// let ct_val = self.market.ct_val;
|
|
|
+// let res_data = self.request.get_positions("SWAP".to_string()).await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let data_list: Vec<SwapPosition> = serde_json::from_str(&res_data_str).unwrap();
|
|
|
+// let position_info: Vec<&SwapPosition> = data_list.iter().filter(|item| item.inst_id == symbol_format).collect();
|
|
|
+// let result = position_info.iter().map(|item| format_position_item(item, ct_val)).collect();
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_positions(&mut self) -> Result<Vec<Position>, Error> {
|
|
|
+// let res_data = self.request.get_positions("SWAP".to_string()).await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let data_list: Vec<SwapPosition> = serde_json::from_str(&res_data_str).unwrap();
|
|
|
+// let result = data_list.iter().map(|item| format_position_item(item, Decimal::ONE)).collect();
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_ticker(&mut self) -> Result<Ticker, Error> {
|
|
|
+// let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
|
|
|
+// let res_data = self.request.get_ticker(symbol_format.clone()).await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let ticker_info_list: Vec<SwapTicker> = serde_json::from_str(&res_data_str).unwrap();
|
|
|
+// let ticker_info = ticker_info_list.iter().find(|item| item.inst_id == symbol_format);
|
|
|
+// match ticker_info {
|
|
|
+// None => {
|
|
|
+// error!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str);
|
|
|
+// panic!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str)
|
|
|
+// }
|
|
|
+// Some(value) => {
|
|
|
+// let result = Ticker {
|
|
|
+// time: value.ts.parse().unwrap(),
|
|
|
+// high: value.high24h,
|
|
|
+// low: value.low24h,
|
|
|
+// sell: value.ask_px,
|
|
|
+// buy: value.bid_px,
|
|
|
+// last: value.last,
|
|
|
+// volume: value.last_sz,
|
|
|
+// };
|
|
|
+// Ok(result)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_ticker_symbol(&mut self, symbol: String) -> Result<Ticker, Error> {
|
|
|
+// let symbol_format = format!("{}-SWAP", utils::format_symbol(symbol.clone(), "-"));
|
|
|
+// let res_data = self.request.get_ticker(symbol_format.clone()).await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let ticker_info_list: Vec<SwapTicker> = serde_json::from_str(&res_data_str).unwrap();
|
|
|
+// let ticker_info = ticker_info_list.iter().find(|item| item.inst_id == symbol_format);
|
|
|
+// match ticker_info {
|
|
|
+// None => {
|
|
|
+// error!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str);
|
|
|
+// panic!("okx_swap:获取Ticker信息错误!\nget_ticker:res_data={:?}", res_data_str)
|
|
|
+// }
|
|
|
+// Some(value) => {
|
|
|
+// let result = Ticker {
|
|
|
+// time: value.ts.parse().unwrap(),
|
|
|
+// high: value.high24h,
|
|
|
+// low: value.low24h,
|
|
|
+// sell: value.ask_px,
|
|
|
+// buy: value.bid_px,
|
|
|
+// last: value.last,
|
|
|
+// volume: value.last_sz,
|
|
|
+// };
|
|
|
+// Ok(result)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_market(&mut self) -> Result<Market, Error> {
|
|
|
+// let symbol_format = utils::format_symbol(self.symbol.clone(), "-");
|
|
|
+// let res_data = self.request.get_instruments().await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let res_data_json: Vec<SwapMarket> = serde_json::from_str(res_data_str).unwrap();
|
|
|
+// let market_info = res_data_json.iter().find(|item| item.inst_id == format!("{}-SWAP", symbol_format));
|
|
|
+// match market_info {
|
|
|
+// None => {
|
|
|
+// error!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str);
|
|
|
+// panic!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str)
|
|
|
+// }
|
|
|
+// Some(value) => {
|
|
|
+// println!("{:?}", value);
|
|
|
+// let symbol_array: Vec<&str> = value.inst_family.split("-").collect();
|
|
|
+// let result = Market {
|
|
|
+// symbol: format!("{}_{}", symbol_array[0], symbol_array[1]),
|
|
|
+// base_asset: symbol_array[0].to_string(),
|
|
|
+// quote_asset: symbol_array[1].to_string(),
|
|
|
+// tick_size: value.tick_sz,
|
|
|
+// amount_size: value.min_sz * value.ct_val,
|
|
|
+// price_precision: Decimal::from_u32(value.tick_sz.scale()).unwrap(),
|
|
|
+// amount_precision: Decimal::from_u32((value.min_sz * value.ct_val).scale()).unwrap(),
|
|
|
+// min_qty: value.min_sz,
|
|
|
+// max_qty: value.max_lmt_sz,
|
|
|
+// min_notional: value.min_sz * value.ct_val,
|
|
|
+// max_notional: value.max_lmt_sz * value.ct_val,
|
|
|
+// ct_val: value.ct_val,
|
|
|
+// };
|
|
|
+// Ok(result)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_market_symbol(&mut self, symbol: String) -> Result<Market, Error> {
|
|
|
+// let symbol_format = utils::format_symbol(symbol.clone(), "-");
|
|
|
+// let res_data = self.request.get_instruments().await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let res_data_json: Vec<SwapMarket> = serde_json::from_str(res_data_str).unwrap();
|
|
|
+// let market_info = res_data_json.iter().find(|item| item.inst_id == format!("{}-SWAP", symbol_format));
|
|
|
+// match market_info {
|
|
|
+// None => {
|
|
|
+// error!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str);
|
|
|
+// panic!("okx_swap:获取Market信息错误!\nget_market:res_data={:?}", res_data_str)
|
|
|
+// }
|
|
|
+// Some(value) => {
|
|
|
+// let symbol_array: Vec<&str> = value.inst_family.split("-").collect();
|
|
|
+// let result = Market {
|
|
|
+// symbol: format!("{}_{}", symbol_array[0], symbol_array[1]),
|
|
|
+// base_asset: symbol_array[0].to_string(),
|
|
|
+// quote_asset: symbol_array[1].to_string(),
|
|
|
+// tick_size: value.tick_sz,
|
|
|
+// amount_size: value.min_sz * value.ct_val,
|
|
|
+// price_precision: Decimal::from_u32(value.tick_sz.scale()).unwrap(),
|
|
|
+// amount_precision: Decimal::from_u32((value.min_sz * value.ct_val).scale()).unwrap(),
|
|
|
+// min_qty: value.min_sz,
|
|
|
+// max_qty: value.max_lmt_sz,
|
|
|
+// min_notional: value.min_sz * value.ct_val,
|
|
|
+// max_notional: value.max_lmt_sz * value.ct_val,
|
|
|
+// ct_val: value.ct_mult,
|
|
|
+// };
|
|
|
+// Ok(result)
|
|
|
+// }
|
|
|
+// }
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_order_detail(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
|
|
|
+// let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
|
|
|
+// let ct_val = self.market.ct_val;
|
|
|
+// let res_data = self.request.get_order(symbol_format, order_id.to_string(), custom_id.to_string()).await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let res_data_json: Vec<SwapOrder> = serde_json::from_str(res_data_str).unwrap();
|
|
|
+// let order_info = res_data_json[0].clone();
|
|
|
+// let result = format_order_item(order_info, ct_val);
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn get_orders_list(&mut self, _status: &str) -> Result<Vec<Order>, Error> {
|
|
|
+// let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
|
|
|
+// let ct_val = self.market.ct_val;
|
|
|
+// let res_data = self.request.get_incomplete_order(symbol_format.clone()).await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let res_data_json: Vec<SwapOrder> = serde_json::from_str(res_data_str).unwrap();
|
|
|
+// let result = res_data_json.iter().map(|item| format_order_item(item.clone(), ct_val)).collect();
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn take_order(&mut self, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
|
|
|
+// let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
|
|
|
+// let ct_val = self.market.ct_val;
|
|
|
+// let mut params = json!({
|
|
|
+// "tdMode": "cross",
|
|
|
+// "clOrdId": custom_id.to_string(),
|
|
|
+// "instId": symbol_format,
|
|
|
+// "px": price.to_string(),
|
|
|
+// });
|
|
|
+// params["ordType"] = if price.eq(&Decimal::ZERO) { json!("market") } else { json!("limit") };
|
|
|
+// let size = (amount / ct_val).floor();
|
|
|
+// params["sz"] = json!(size);
|
|
|
+// match origin_side {
|
|
|
+// "kd" => {
|
|
|
+// params["side"] = json!("buy");
|
|
|
+// params["posSide"] = json!("long");
|
|
|
+// }
|
|
|
+// "pd" => {
|
|
|
+// params["side"] = json!("sell");
|
|
|
+// params["posSide"] = json!("long");
|
|
|
+// }
|
|
|
+// "kk" => {
|
|
|
+// params["side"] = json!("sell");
|
|
|
+// params["posSide"] = json!("short");
|
|
|
+// }
|
|
|
+// "pk" => {
|
|
|
+// params["side"] = json!("buy");
|
|
|
+// params["posSide"] = json!("short");
|
|
|
+// }
|
|
|
+// _ => { error!("下单参数错误"); }
|
|
|
+// };
|
|
|
+// let res_data = self.request.swap_order(params).await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
|
|
|
+// let order_info = res_data_json[0].clone();
|
|
|
+// let id = order_info["ordId"].as_str().unwrap().to_string();
|
|
|
+// let custom_id = order_info["clOrdId"].as_str().unwrap().to_string();
|
|
|
+// let result = Order {
|
|
|
+// id,
|
|
|
+// custom_id,
|
|
|
+// price,
|
|
|
+// amount,
|
|
|
+// deal_amount: Decimal::ZERO,
|
|
|
+// avg_price: Decimal::ZERO,
|
|
|
+// status: "NEW".to_string(),
|
|
|
+// order_type: "".to_string(),
|
|
|
+// trace_stack: TraceStack::new(0, Instant::now()).on_special("799 okx_swap".to_string()),
|
|
|
+// };
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn take_order_symbol(&mut self, symbol: String, ct_val: Decimal, custom_id: &str, origin_side: &str, price: Decimal, amount: Decimal) -> Result<Order, Error> {
|
|
|
+// let symbol_format = format!("{}-SWAP", utils::format_symbol(symbol.clone(), "-"));
|
|
|
+// let mut params = json!({
|
|
|
+// "tdMode": "cross",
|
|
|
+// "clOrdId": custom_id.to_string(),
|
|
|
+// "instId": symbol_format,
|
|
|
+// "px": price.to_string(),
|
|
|
+// });
|
|
|
+// params["ordType"] = if price.eq(&Decimal::ZERO) { json!("market") } else { json!("limit") };
|
|
|
+// let size = (amount / ct_val).floor();
|
|
|
+// params["sz"] = json!(size);
|
|
|
+// match origin_side {
|
|
|
+// "kd" => {
|
|
|
+// params["side"] = json!("buy");
|
|
|
+// params["posSide"] = json!("long");
|
|
|
+// }
|
|
|
+// "pd" => {
|
|
|
+// params["side"] = json!("sell");
|
|
|
+// params["posSide"] = json!("long");
|
|
|
+// }
|
|
|
+// "kk" => {
|
|
|
+// params["side"] = json!("sell");
|
|
|
+// params["posSide"] = json!("short");
|
|
|
+// }
|
|
|
+// "pk" => {
|
|
|
+// params["side"] = json!("buy");
|
|
|
+// params["posSide"] = json!("short");
|
|
|
+// }
|
|
|
+// _ => { error!("下单参数错误"); }
|
|
|
+// };
|
|
|
+// let res_data = self.request.swap_order(params).await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
|
|
|
+// let order_info = res_data_json[0].clone();
|
|
|
+// let id = order_info["ordId"].as_str().unwrap().to_string();
|
|
|
+// let custom_id = order_info["clOrdId"].as_str().unwrap().to_string();
|
|
|
+// let result = Order {
|
|
|
+// id,
|
|
|
+// custom_id,
|
|
|
+// price,
|
|
|
+// amount,
|
|
|
+// deal_amount: Decimal::ZERO,
|
|
|
+// avg_price: Decimal::ZERO,
|
|
|
+// status: "NEW".to_string(),
|
|
|
+// order_type: "".to_string(),
|
|
|
+// trace_stack: TraceStack::new(0, Instant::now()).on_special("853 okx_swap".to_string()),
|
|
|
+// };
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn cancel_order(&mut self, order_id: &str, custom_id: &str) -> Result<Order, Error> {
|
|
|
+// let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
|
|
|
+// let res_data = self.request.cancel_order(symbol_format, order_id.to_string(), custom_id.to_string()).await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let res_data_json: Vec<serde_json::Value> = serde_json::from_str(res_data_str).unwrap();
|
|
|
+// let order_info = res_data_json[0].clone();
|
|
|
+// let id = order_info["ordId"].as_str().unwrap().to_string();
|
|
|
+// let custom_id = order_info["clOrdId"].as_str().unwrap().to_string();
|
|
|
+// let result = Order {
|
|
|
+// id,
|
|
|
+// custom_id,
|
|
|
+// price: Decimal::ZERO,
|
|
|
+// amount: Decimal::ZERO,
|
|
|
+// deal_amount: Decimal::ZERO,
|
|
|
+// avg_price: Decimal::ZERO,
|
|
|
+// status: "REMOVE".to_string(),
|
|
|
+// order_type: "".to_string(),
|
|
|
+// trace_stack: TraceStack::new(0, Instant::now()).on_special("879 okx_swap".to_string()),
|
|
|
+// };
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn cancel_orders(&mut self) -> Result<Vec<Order>, Error> {
|
|
|
+// Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string()))
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn cancel_orders_all(&mut self) -> Result<Vec<Order>, Error> {
|
|
|
+// Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string()))
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn set_dual_mode(&mut self, _coin: &str, _is_dual_mode: bool) -> Result<String, Error> {
|
|
|
+// let res_data = self.request.set_position_mode().await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let result = res_data_str.clone();
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn set_dual_leverage(&mut self, leverage: &str) -> Result<String, Error> {
|
|
|
+// let symbol_format = format!("{}-SWAP", utils::format_symbol(self.symbol.clone(), "-"));
|
|
|
+// let res_data = self.request.set_leverage(symbol_format, leverage.to_string()).await;
|
|
|
+// if res_data.code == "200" {
|
|
|
+// let res_data_str = &res_data.data;
|
|
|
+// let result = res_data_str.clone();
|
|
|
+// Ok(result)
|
|
|
+// } else {
|
|
|
+// Err(Error::new(ErrorKind::Other, res_data.to_string()))
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// async fn set_auto_deposit_status(&mut self, _status: bool) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string())) }
|
|
|
+//
|
|
|
+// async fn wallet_transfers(&mut self, _coin: &str, _from: &str, _to: &str, _amount: Decimal) -> Result<String, Error> { Err(Error::new(ErrorKind::NotFound, "okx_swap:该交易所方法未实现".to_string())) }
|
|
|
+//
|
|
|
+// async fn command_order(&mut self, order_command: OrderCommand, trace_stack: TraceStack) {
|
|
|
+// let mut handles = vec![];
|
|
|
+// // 撤销订单
|
|
|
+// let cancel = order_command.cancel;
|
|
|
+// for item in cancel.keys() {
|
|
|
+// let mut self_clone = self.clone();
|
|
|
+// let cancel_clone = cancel.clone();
|
|
|
+// let item_clone = item.clone();
|
|
|
+// let order_id = cancel_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
|
|
|
+// let custom_id = cancel_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
|
|
|
+// let result_sd = self.order_sender.clone();
|
|
|
+// let err_sd = self.error_sender.clone();
|
|
|
+// let handle = tokio::spawn(async move {
|
|
|
+// if order_id != "" {
|
|
|
+// let result = self_clone.cancel_order(&order_id, &custom_id).await;
|
|
|
+// match result {
|
|
|
+// Ok(_) => {
|
|
|
+// // result_sd.send(result).await.unwrap();
|
|
|
+// }
|
|
|
+// Err(error) => {
|
|
|
+// // 取消失败去查订单。
|
|
|
+// let query_rst = self_clone.get_order_detail(&order_id, &custom_id).await;
|
|
|
+// match query_rst {
|
|
|
+// Ok(order) => {
|
|
|
+// result_sd.send(order).await.unwrap();
|
|
|
+// }
|
|
|
+// Err(query_err) => {
|
|
|
+// error!(?query_err);
|
|
|
+// error!("撤单失败,而且查单也失败了,okx_swap,oid={}, cid={}。", order_id.clone(), custom_id.clone());
|
|
|
+// }
|
|
|
+// }
|
|
|
+// err_sd.send(error).await.unwrap();
|
|
|
+// }
|
|
|
+// }
|
|
|
+// }
|
|
|
+// });
|
|
|
+// handles.push(handle)
|
|
|
+// }
|
|
|
+// // 下单指令
|
|
|
+// let mut limits = HashMap::new();
|
|
|
+// limits.extend(order_command.limits_open);
|
|
|
+// limits.extend(order_command.limits_close);
|
|
|
+// for item in limits.keys() {
|
|
|
+// let mut self_clone = self.clone();
|
|
|
+// let limits_clone = limits.clone();
|
|
|
+// let item_clone = item.clone();
|
|
|
+// let result_sd = self.order_sender.clone();
|
|
|
+// let err_sd = self.error_sender.clone();
|
|
|
+// let ts = trace_stack.clone();
|
|
|
+//
|
|
|
+// let handle = tokio::spawn(async move {
|
|
|
+// let value = limits_clone[&item_clone].clone();
|
|
|
+// let amount = Decimal::from_str(value.get(0).unwrap_or(&"0".to_string())).unwrap();
|
|
|
+// let side = value.get(1).unwrap();
|
|
|
+// let price = Decimal::from_str(value.get(2).unwrap_or(&"0".to_string())).unwrap();
|
|
|
+// let cid = value.get(3).unwrap();
|
|
|
+//
|
|
|
+// // order_name: [数量,方向,价格,c_id]
|
|
|
+// let result = self_clone.take_order(cid, side, price, amount).await;
|
|
|
+// match result {
|
|
|
+// Ok(mut result) => {
|
|
|
+// // ts.on_after_send();
|
|
|
+// result.trace_stack = ts.clone();
|
|
|
+//
|
|
|
+// result_sd.send(result).await.unwrap();
|
|
|
+// }
|
|
|
+// Err(error) => {
|
|
|
+// let mut err_order = Order::new();
|
|
|
+// err_order.custom_id = cid.clone();
|
|
|
+// err_order.status = "REMOVE".to_string();
|
|
|
+//
|
|
|
+// result_sd.send(err_order).await.unwrap();
|
|
|
+// err_sd.send(error).await.unwrap();
|
|
|
+// }
|
|
|
+// }
|
|
|
+// });
|
|
|
+// handles.push(handle)
|
|
|
+// }
|
|
|
+// // 检查订单指令
|
|
|
+// let check = order_command.check;
|
|
|
+// for item in check.keys() {
|
|
|
+// let mut self_clone = self.clone();
|
|
|
+// let check_clone = check.clone();
|
|
|
+// let item_clone = item.clone();
|
|
|
+// let order_id = check_clone[&item_clone].get(1).unwrap_or(&"".to_string()).clone();
|
|
|
+// let custom_id = check_clone[&item_clone].get(0).unwrap_or(&"".to_string()).clone();
|
|
|
+// let result_sd = self.order_sender.clone();
|
|
|
+// let err_sd = self.error_sender.clone();
|
|
|
+// let handle = tokio::spawn(async move {
|
|
|
+// let result = self_clone.get_order_detail(&order_id, &custom_id).await;
|
|
|
+// match result {
|
|
|
+// Ok(result) => {
|
|
|
+// result_sd.send(result).await.unwrap();
|
|
|
+// }
|
|
|
+// Err(error) => {
|
|
|
+// err_sd.send(error).await.unwrap();
|
|
|
+// }
|
|
|
+// }
|
|
|
+// });
|
|
|
+// handles.push(handle)
|
|
|
+// }
|
|
|
+//
|
|
|
+// let futures = FuturesUnordered::from_iter(handles);
|
|
|
+// let _: Result<Vec<_>, _> = futures.try_collect().await;
|
|
|
+// }
|
|
|
+// }
|
|
|
+//
|
|
|
+// pub fn format_order_item(data: SwapOrder, ct_val: Decimal) -> Order {
|
|
|
+// debug!("format-order-start, okx_swap");
|
|
|
+// debug!(?data);
|
|
|
+// let custom_status = if ["canceled", "filled", "mmp_canceled"].contains(&data.state.as_str()) {
|
|
|
+// "REMOVE".to_string()
|
|
|
+// } else if ["live", "partially_filled"].contains(&data.state.as_str()) {
|
|
|
+// "NEW".to_string()
|
|
|
+// } else {
|
|
|
+// "NULL".to_string()
|
|
|
+// };
|
|
|
+//
|
|
|
+// let result = Order {
|
|
|
+// id: data.ord_id,
|
|
|
+// custom_id: data.cl_ord_id,
|
|
|
+// price: data.px,
|
|
|
+// amount: data.sz * ct_val,
|
|
|
+// deal_amount: data.acc_fill_sz * ct_val,
|
|
|
+// avg_price: if data.avg_px != "" { Decimal::from_str(&data.avg_px).unwrap() } else { Decimal::ZERO },
|
|
|
+// status: custom_status,
|
|
|
+// order_type: data.ord_type,
|
|
|
+// trace_stack: TraceStack::new(0, Instant::now()).on_special("1049 okx_swap".to_string()),
|
|
|
+// };
|
|
|
+// debug!(?result);
|
|
|
+// debug!("format-order-end, okx_swap");
|
|
|
+// result
|
|
|
+// }
|
|
|
+//
|
|
|
+// pub fn format_position_item(value: &SwapPosition, ct_val: Decimal) -> Position {
|
|
|
+// let position_mode = match value.pos_side.as_str() {
|
|
|
+// "long" => { PositionModeEnum::Long }
|
|
|
+// "short" => { PositionModeEnum::Short }
|
|
|
+// _ => { PositionModeEnum::Both }
|
|
|
+// };
|
|
|
+// Position {
|
|
|
+// symbol: value.inst_id.replace("-SWAP", ""),
|
|
|
+// margin_level: value.lever,
|
|
|
+// amount: value.pos * ct_val,
|
|
|
+// frozen_amount: Decimal::ZERO,
|
|
|
+// price: value.avg_px,
|
|
|
+// profit: value.upl,
|
|
|
+// position_mode,
|
|
|
+// margin: if value.margin != "" { Decimal::from_str(&value.margin).unwrap() } else { Decimal::ZERO },
|
|
|
+// }
|
|
|
+// }
|