|
@@ -1,10 +1,16 @@
|
|
|
-use anyhow::Result;
|
|
|
|
|
|
|
+use std::str::FromStr;
|
|
|
|
|
+use std::sync::Arc;
|
|
|
|
|
+use anyhow::{anyhow, bail, Result};
|
|
|
use rust_decimal::Decimal;
|
|
use rust_decimal::Decimal;
|
|
|
use std::time::{Duration, Instant};
|
|
use std::time::{Duration, Instant};
|
|
|
use rust_decimal_macros::dec;
|
|
use rust_decimal_macros::dec;
|
|
|
|
|
+use serde_json::Value;
|
|
|
|
|
+use tokio::sync::Mutex;
|
|
|
use tokio::time::sleep;
|
|
use tokio::time::sleep;
|
|
|
use tracing::{info, warn};
|
|
use tracing::{info, warn};
|
|
|
use crate::data_manager::DataManager;
|
|
use crate::data_manager::DataManager;
|
|
|
|
|
+use crate::exchange::extended_rest_client::ExtendedRestClient;
|
|
|
|
|
+use crate::utils::response::Response;
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
#[derive(Debug, Clone, PartialEq)]
|
|
|
pub enum StrategyState {
|
|
pub enum StrategyState {
|
|
@@ -27,18 +33,20 @@ pub enum StrategyState {
|
|
|
#[allow(dead_code)]
|
|
#[allow(dead_code)]
|
|
|
pub struct Strategy {
|
|
pub struct Strategy {
|
|
|
state: StrategyState,
|
|
state: StrategyState,
|
|
|
- order_quantity: Decimal, // 写死的订单数量
|
|
|
|
|
- filled_quantity: Decimal, // 成交数量
|
|
|
|
|
- min_order_interval_ms: u128, // 最小下单间隔(毫秒)
|
|
|
|
|
|
|
+ order_quantity: Decimal, // 写死的订单数量
|
|
|
|
|
+ filled_quantity: Decimal, // 成交数量
|
|
|
|
|
+ min_order_interval_ms: u128, // 最小下单间隔(毫秒)
|
|
|
|
|
+ rest_client: Arc<Mutex<ExtendedRestClient>>, // rest客户端
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
impl Strategy {
|
|
impl Strategy {
|
|
|
- pub fn new() -> Strategy {
|
|
|
|
|
|
|
+ pub fn new(client_am: Arc<Mutex<ExtendedRestClient>>) -> Strategy {
|
|
|
Strategy {
|
|
Strategy {
|
|
|
state: StrategyState::Idle,
|
|
state: StrategyState::Idle,
|
|
|
order_quantity: dec!(0.001),
|
|
order_quantity: dec!(0.001),
|
|
|
filled_quantity: Decimal::ZERO,
|
|
filled_quantity: Decimal::ZERO,
|
|
|
min_order_interval_ms: 200,
|
|
min_order_interval_ms: 200,
|
|
|
|
|
+ rest_client: client_am,
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -159,9 +167,9 @@ impl Strategy {
|
|
|
sleep(Duration::from_millis(3000)).await;
|
|
sleep(Duration::from_millis(3000)).await;
|
|
|
|
|
|
|
|
// 撤单后检查是否有成交
|
|
// 撤单后检查是否有成交
|
|
|
- match self.check_order_filled(&order_id).await {
|
|
|
|
|
|
|
+ match self.check_order_partially_filled(&order_id).await {
|
|
|
Ok(true) => {
|
|
Ok(true) => {
|
|
|
- info!("撤单时发现有成交,准备执行市价单");
|
|
|
|
|
|
|
+ info!("撤单后发现有成交,准备执行市价单");
|
|
|
self.state = StrategyState::ExecutingMarketOrder { last_order_time };
|
|
self.state = StrategyState::ExecutingMarketOrder { last_order_time };
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
@@ -204,7 +212,10 @@ impl Strategy {
|
|
|
match self.check_order_filled(&order_id).await {
|
|
match self.check_order_filled(&order_id).await {
|
|
|
Ok(true) => {
|
|
Ok(true) => {
|
|
|
info!("市价单已成交,返回空闲状态");
|
|
info!("市价单已成交,返回空闲状态");
|
|
|
|
|
+
|
|
|
self.state = StrategyState::Idle;
|
|
self.state = StrategyState::Idle;
|
|
|
|
|
+ self.filled_quantity = Decimal::ZERO;
|
|
|
|
|
+
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
Ok(false) => {
|
|
Ok(false) => {
|
|
@@ -233,28 +244,196 @@ impl Strategy {
|
|
|
/// 下限价买单
|
|
/// 下限价买单
|
|
|
async fn place_limit_buy_order(&self, price: Decimal, quantity: Decimal) -> Result<String> {
|
|
async fn place_limit_buy_order(&self, price: Decimal, quantity: Decimal) -> Result<String> {
|
|
|
info!("下限价买单: 价格={}, 数量={}", price, quantity);
|
|
info!("下限价买单: 价格={}, 数量={}", price, quantity);
|
|
|
- // TODO: 实现具体的下单逻辑
|
|
|
|
|
- Ok("order_id_placeholder".to_string())
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let mut client = self.rest_client.lock().await;
|
|
|
|
|
+
|
|
|
|
|
+ // 调用client执行下单
|
|
|
|
|
+ let create_result = client.post_order(
|
|
|
|
|
+ "LIMIT",
|
|
|
|
|
+ "BUY",
|
|
|
|
|
+ quantity.to_string().as_str(),
|
|
|
|
|
+ price.to_string().as_str()).await;
|
|
|
|
|
+
|
|
|
|
|
+ // 解析下单结果并返回
|
|
|
|
|
+ self.match_create_order_result(&create_result)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// 下市价卖单
|
|
/// 下市价卖单
|
|
|
async fn place_market_sell_order(&self, quantity: Decimal) -> Result<String> {
|
|
async fn place_market_sell_order(&self, quantity: Decimal) -> Result<String> {
|
|
|
info!("下市价卖单: 数量={}", quantity);
|
|
info!("下市价卖单: 数量={}", quantity);
|
|
|
- // TODO: 实现具体的下单逻辑
|
|
|
|
|
- Ok("order_id_placeholder".to_string())
|
|
|
|
|
|
|
+ let mut client = self.rest_client.lock().await;
|
|
|
|
|
+
|
|
|
|
|
+ // 调用client执行下单
|
|
|
|
|
+ let create_result = client.post_order(
|
|
|
|
|
+ "MARKET",
|
|
|
|
|
+ "SELL",
|
|
|
|
|
+ quantity.to_string().as_str(),
|
|
|
|
|
+ "-1").await;
|
|
|
|
|
+
|
|
|
|
|
+ // 解析下单结果并返回
|
|
|
|
|
+ self.match_create_order_result(&create_result)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// 撤单
|
|
/// 撤单
|
|
|
async fn cancel_order(&self, order_id: &str) -> Result<()> {
|
|
async fn cancel_order(&self, order_id: &str) -> Result<()> {
|
|
|
info!("撤单: {}", order_id);
|
|
info!("撤单: {}", order_id);
|
|
|
- // TODO: 实现具体的撤单逻辑
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let mut client = self.rest_client.lock().await;
|
|
|
|
|
+
|
|
|
|
|
+ let response = client.cancel_order(order_id).await;
|
|
|
|
|
+ let value = &response.data;
|
|
|
|
|
+
|
|
|
|
|
+ // 预先捕获整个 Value 的字符串表示,用于错误报告
|
|
|
|
|
+ let value_str = serde_json::to_string(&value).unwrap_or_else(|_| "无法序列化 JSON Value".to_string());
|
|
|
|
|
+
|
|
|
|
|
+ // 获取status
|
|
|
|
|
+ let status = value.get("status")
|
|
|
|
|
+ .and_then(|v| v.as_str())
|
|
|
|
|
+ .ok_or_else(|| anyhow!("撤单-获取 'status' 失败,原始JSON:{}", value_str))?;
|
|
|
|
|
+
|
|
|
|
|
+ // 判定status
|
|
|
|
|
+ if status != "OK" {
|
|
|
|
|
+ bail!("撤单失败,状态不为OK,原始JSON:{}", value_str)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
Ok(())
|
|
Ok(())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// 检查订单是否完全成交
|
|
/// 检查订单是否完全成交
|
|
|
- async fn check_order_filled(&self, order_id: &str) -> Result<bool> {
|
|
|
|
|
|
|
+ async fn check_order_filled(&mut self, order_id: &str) -> Result<bool> {
|
|
|
info!("检查订单是否成交: {}", order_id);
|
|
info!("检查订单是否成交: {}", order_id);
|
|
|
- // TODO: 实现具体的查询逻辑
|
|
|
|
|
- Ok(false)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ let query_result = self.get_order_result(order_id).await;
|
|
|
|
|
+
|
|
|
|
|
+ match query_result {
|
|
|
|
|
+ Ok(data) => {
|
|
|
|
|
+ let data_str = serde_json::to_string(&data).unwrap_or_else(|_| "无法序列化 JSON Value".to_string());
|
|
|
|
|
+
|
|
|
|
|
+ // 获取order的状态[NEW, PARTIALLY_FILLED, FILLED, UNTRIGGERED, CANCELLED, REJECTED, EXPIRED, TRIGGERED]
|
|
|
|
|
+ let status = data.get("status")
|
|
|
|
|
+ .and_then(|v| v.as_str())
|
|
|
|
|
+ .ok_or_else(|| anyhow!("查单-获取 'data.status' 失败,原始JSON:{}", data_str))?;
|
|
|
|
|
+
|
|
|
|
|
+ // 只考虑完全成交的
|
|
|
|
|
+ if status == "FILLED" {
|
|
|
|
|
+ // 获取真实成交数量
|
|
|
|
|
+ let filled_qty = data.get("filledQty")
|
|
|
|
|
+ .and_then(|v| v.as_str())
|
|
|
|
|
+ .ok_or_else(|| anyhow!("查单-获取 'data.filledQty' 失败,原始JSON:{}", data_str))
|
|
|
|
|
+ .and_then(|v| Decimal::from_str(v)
|
|
|
|
|
+ .map_err(|e| anyhow!("查单-解析 'data.filledQty' 为 Decimal 失败: {}, 值: {}", e, v))
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ self.filled_quantity = filled_qty;
|
|
|
|
|
+
|
|
|
|
|
+ Ok(true)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Ok(false)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ Err(error) => {
|
|
|
|
|
+ bail!("查单失败: {}", error);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// 检查订单是否有部分成交
|
|
|
|
|
+ async fn check_order_partially_filled(&mut self, order_id: &str) -> Result<bool> {
|
|
|
|
|
+ info!("检查订单是否有部分成交: {}", order_id);
|
|
|
|
|
+
|
|
|
|
|
+ let query_result = self.get_order_result(order_id).await;
|
|
|
|
|
+
|
|
|
|
|
+ match query_result {
|
|
|
|
|
+ Ok(data) => {
|
|
|
|
|
+ let data_str = serde_json::to_string(&data).unwrap_or_else(|_| "无法序列化 JSON Value".to_string());
|
|
|
|
|
+
|
|
|
|
|
+ // 获取order的状态[NEW, PARTIALLY_FILLED, FILLED, UNTRIGGERED, CANCELLED, REJECTED, EXPIRED, TRIGGERED]
|
|
|
|
|
+ let status = data.get("status")
|
|
|
|
|
+ .and_then(|v| v.as_str())
|
|
|
|
|
+ .ok_or_else(|| anyhow!("查单-获取 'data.status' 失败,原始JSON:{}", data_str))?;
|
|
|
|
|
+
|
|
|
|
|
+ // 只考虑完全成交的
|
|
|
|
|
+ if status == "FILLED" || status == "PARTIALLY_FILLED" {
|
|
|
|
|
+ // 获取真实成交数量
|
|
|
|
|
+ let filled_qty = data.get("filledQty")
|
|
|
|
|
+ .and_then(|v| v.as_str())
|
|
|
|
|
+ .ok_or_else(|| anyhow!("查单-获取 'data.filledQty' 失败,原始JSON:{}", data_str))
|
|
|
|
|
+ .and_then(|v| Decimal::from_str(v)
|
|
|
|
|
+ .map_err(|e| anyhow!("查单-解析 'data.filledQty' 为 Decimal 失败: {}, 值: {}", e, v))
|
|
|
|
|
+ )?;
|
|
|
|
|
+
|
|
|
|
|
+ self.filled_quantity = filled_qty;
|
|
|
|
|
+
|
|
|
|
|
+ Ok(true)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ Ok(false)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ Err(error) => {
|
|
|
|
|
+ bail!("查单失败: {}", error);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ async fn get_order_result(&self, id: &str) -> Result<Value> {
|
|
|
|
|
+ let mut client = self.rest_client.lock().await;
|
|
|
|
|
+ let response = client.get_order(id).await;
|
|
|
|
|
+
|
|
|
|
|
+ let value = &response.data;
|
|
|
|
|
+
|
|
|
|
|
+ // 预先捕获整个 Value 的字符串表示,用于错误报告
|
|
|
|
|
+ let value_str = serde_json::to_string(&value).unwrap_or_else(|_| "无法序列化 JSON Value".to_string());
|
|
|
|
|
+
|
|
|
|
|
+ // 获取status
|
|
|
|
|
+ let status = value.get("status")
|
|
|
|
|
+ .and_then(|v| v.as_str())
|
|
|
|
|
+ .ok_or_else(|| anyhow!("查单-获取 'status' 失败,原始JSON:{}", value_str))?;
|
|
|
|
|
+
|
|
|
|
|
+ // 判定status
|
|
|
|
|
+ if status != "OK" {
|
|
|
|
|
+ bail!("查单失败,状态不为OK,原始JSON:{}", value_str)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试获取 data 字段
|
|
|
|
|
+ let data = value.get("data")
|
|
|
|
|
+ .ok_or_else(|| anyhow!("下单-获取 'data' 字段失败,原始 JSON: {}", value_str))?;
|
|
|
|
|
+
|
|
|
|
|
+ // data返回给查单
|
|
|
|
|
+ Ok(data.clone())
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ fn match_create_order_result(&self, create_result: &Result<Response>) -> Result<String> {
|
|
|
|
|
+ match create_result {
|
|
|
|
|
+ Ok(response) => {
|
|
|
|
|
+ let value = &response.data;
|
|
|
|
|
+
|
|
|
|
|
+ // 预先捕获整个 Value 的字符串表示,用于错误报告
|
|
|
|
|
+ let value_str = serde_json::to_string(&value).unwrap_or_else(|_| "无法序列化 JSON Value".to_string());
|
|
|
|
|
+
|
|
|
|
|
+ // 获取status
|
|
|
|
|
+ let status = value.get("status")
|
|
|
|
|
+ .and_then(|v| v.as_str())
|
|
|
|
|
+ .ok_or_else(|| anyhow!("下单-获取 'status' 失败,原始JSON:{}", value_str))?;
|
|
|
|
|
+
|
|
|
|
|
+ // 判定status
|
|
|
|
|
+ if status != "OK" {
|
|
|
|
|
+ bail!("下单失败,状态不为OK,原始JSON:{}", value_str)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 尝试获取 data 字段
|
|
|
|
|
+ let data = value.get("data")
|
|
|
|
|
+ .ok_or_else(|| anyhow!("下单-获取 'data' 字段失败,原始 JSON: {}", value_str))?;
|
|
|
|
|
+
|
|
|
|
|
+ // 获取order的id
|
|
|
|
|
+ let id = data.get("id")
|
|
|
|
|
+ .and_then(|v| v.as_str())
|
|
|
|
|
+ .ok_or_else(|| anyhow!("下单-获取 'data.id' 失败,原始JSON:{}", value_str))?;
|
|
|
|
|
+
|
|
|
|
|
+ Ok(id.to_string())
|
|
|
|
|
+ }
|
|
|
|
|
+ Err(error) => {
|
|
|
|
|
+ bail!("下单失败:{}", error);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|