From 45ae0f714e6d4f20c5f2ab97ff86124b177ac342 Mon Sep 17 00:00:00 2001 From: SkywalkerJi Date: Thu, 30 Oct 2025 12:53:27 +0800 Subject: [PATCH 1/4] Smooth the account equity curve. --- web/src/components/EquityChart.tsx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/web/src/components/EquityChart.tsx b/web/src/components/EquityChart.tsx index 3a90f628..4022c112 100644 --- a/web/src/components/EquityChart.tsx +++ b/web/src/components/EquityChart.tsx @@ -60,7 +60,10 @@ export function EquityChart({ traderId }: EquityChartProps) { ); } - if (!history || history.length === 0) { + // 过滤掉无效数据:total_equity为0或小于1的数据点(API失败导致) + const validHistory = history?.filter(point => point.total_equity > 1) || []; + + if (!validHistory || validHistory.length === 0) { return (

{t('accountEquityCurve', language)}

@@ -76,12 +79,12 @@ export function EquityChart({ traderId }: EquityChartProps) { // 限制显示最近的数据点(性能优化) // 如果数据超过2000个点,只显示最近2000个 const MAX_DISPLAY_POINTS = 2000; - const displayHistory = history.length > MAX_DISPLAY_POINTS - ? history.slice(-MAX_DISPLAY_POINTS) - : history; + const displayHistory = validHistory.length > MAX_DISPLAY_POINTS + ? validHistory.slice(-MAX_DISPLAY_POINTS) + : validHistory; - // 计算初始余额(使用第一个数据点,如果无数据则从account获取,最后才用默认值) - const initialBalance = history[0]?.total_equity + // 计算初始余额(使用第一个有效数据点,如果无数据则从account获取,最后才用默认值) + const initialBalance = validHistory[0]?.total_equity || account?.total_equity || 100; // 默认值改为100,与常见配置一致 @@ -250,12 +253,13 @@ export function EquityChart({ traderId }: EquityChartProps) { }} /> 50 ? false : { fill: '#F0B90B', r: 3 }} activeDot={{ r: 6, fill: '#FCD535', stroke: '#F0B90B', strokeWidth: 2 }} + connectNulls={true} /> @@ -277,12 +281,12 @@ export function EquityChart({ traderId }: EquityChartProps) {
{t('historicalCycles', language)}
-
{history.length} {t('cycles', language)}
+
{validHistory.length} {t('cycles', language)}
{t('displayRange', language)}
- {history.length > MAX_DISPLAY_POINTS + {validHistory.length > MAX_DISPLAY_POINTS ? `${t('recent', language)} ${MAX_DISPLAY_POINTS}` : t('allData', language) } From a51861ea7889e6b33e7687fe6acec385efeb4f73 Mon Sep 17 00:00:00 2001 From: yiplee Date: Thu, 30 Oct 2025 13:00:17 +0800 Subject: [PATCH 2/4] Change health check endpoint to accept any HTTP method for improved flexibility. --- api/server.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/server.go b/api/server.go index 32d24c51..875aeae0 100644 --- a/api/server.go +++ b/api/server.go @@ -57,7 +57,7 @@ func corsMiddleware() gin.HandlerFunc { // setupRoutes 设置路由 func (s *Server) setupRoutes() { // 健康检查 - s.router.GET("/health", s.handleHealth) + s.router.Any("/health", s.handleHealth) // API路由组 api := s.router.Group("/api") From 234c4ab3c38b701049a1a779b2b25f721c73edef Mon Sep 17 00:00:00 2001 From: mxyhi Date: Thu, 30 Oct 2025 13:06:14 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E2=9C=A8=20feat(trader):=20aster=E5=B9=B3?= =?UTF-8?q?=E4=BB=93=E5=90=8E=E8=87=AA=E5=8A=A8=E5=8F=96=E6=B6=88=E6=8C=82?= =?UTF-8?q?=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整 CloseLong/CloseShort 逻辑, 在平仓后调用 CancelAllOrders 清理挂单 --- trader/aster_trader.go | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/trader/aster_trader.go b/trader/aster_trader.go index 9aaf078c..e3a96aed 100644 --- a/trader/aster_trader.go +++ b/trader/aster_trader.go @@ -195,11 +195,11 @@ func (t *AsterTrader) formatQuantity(symbol string, quantity float64) (float64, func (t *AsterTrader) formatFloatWithPrecision(value float64, precision int) string { // 使用指定精度格式化 formatted := strconv.FormatFloat(value, 'f', precision, 64) - + // 去除末尾的0和小数点(如果有) formatted = strings.TrimRight(formatted, "0") formatted = strings.TrimRight(formatted, ".") - + return formatted } @@ -556,7 +556,7 @@ func (t *AsterTrader) OpenLong(symbol string, quantity float64, leverage int) (m priceStr := t.formatFloatWithPrecision(formattedPrice, prec.PricePrecision) qtyStr := t.formatFloatWithPrecision(formattedQty, prec.QuantityPrecision) - log.Printf(" 📏 精度处理: 价格 %.8f -> %s (精度=%d), 数量 %.8f -> %s (精度=%d)", + log.Printf(" 📏 精度处理: 价格 %.8f -> %s (精度=%d), 数量 %.8f -> %s (精度=%d)", limitPrice, priceStr, prec.PricePrecision, quantity, qtyStr, prec.QuantityPrecision) params := map[string]interface{}{ @@ -618,7 +618,7 @@ func (t *AsterTrader) OpenShort(symbol string, quantity float64, leverage int) ( priceStr := t.formatFloatWithPrecision(formattedPrice, prec.PricePrecision) qtyStr := t.formatFloatWithPrecision(formattedQty, prec.QuantityPrecision) - log.Printf(" 📏 精度处理: 价格 %.8f -> %s (精度=%d), 数量 %.8f -> %s (精度=%d)", + log.Printf(" 📏 精度处理: 价格 %.8f -> %s (精度=%d), 数量 %.8f -> %s (精度=%d)", limitPrice, priceStr, prec.PricePrecision, quantity, qtyStr, prec.QuantityPrecision) params := map[string]interface{}{ @@ -693,7 +693,7 @@ func (t *AsterTrader) CloseLong(symbol string, quantity float64) (map[string]int priceStr := t.formatFloatWithPrecision(formattedPrice, prec.PricePrecision) qtyStr := t.formatFloatWithPrecision(formattedQty, prec.QuantityPrecision) - log.Printf(" 📏 精度处理: 价格 %.8f -> %s (精度=%d), 数量 %.8f -> %s (精度=%d)", + log.Printf(" 📏 精度处理: 价格 %.8f -> %s (精度=%d), 数量 %.8f -> %s (精度=%d)", limitPrice, priceStr, prec.PricePrecision, quantity, qtyStr, prec.QuantityPrecision) params := map[string]interface{}{ @@ -717,6 +717,12 @@ func (t *AsterTrader) CloseLong(symbol string, quantity float64) (map[string]int } log.Printf("✓ 平多仓成功: %s 数量: %s", symbol, qtyStr) + + // 平仓后取消该币种的所有挂单(止损止盈单) + if err := t.CancelAllOrders(symbol); err != nil { + log.Printf(" ⚠ 取消挂单失败: %v", err) + } + return result, nil } @@ -770,7 +776,7 @@ func (t *AsterTrader) CloseShort(symbol string, quantity float64) (map[string]in priceStr := t.formatFloatWithPrecision(formattedPrice, prec.PricePrecision) qtyStr := t.formatFloatWithPrecision(formattedQty, prec.QuantityPrecision) - log.Printf(" 📏 精度处理: 价格 %.8f -> %s (精度=%d), 数量 %.8f -> %s (精度=%d)", + log.Printf(" 📏 精度处理: 价格 %.8f -> %s (精度=%d), 数量 %.8f -> %s (精度=%d)", limitPrice, priceStr, prec.PricePrecision, quantity, qtyStr, prec.QuantityPrecision) params := map[string]interface{}{ @@ -794,6 +800,12 @@ func (t *AsterTrader) CloseShort(symbol string, quantity float64) (map[string]in } log.Printf("✓ 平空仓成功: %s 数量: %s", symbol, qtyStr) + + // 平仓后取消该币种的所有挂单(止损止盈单) + if err := t.CancelAllOrders(symbol); err != nil { + log.Printf(" ⚠ 取消挂单失败: %v", err) + } + return result, nil } From 908236640abfa336eb698788acb430117cf2f7dd Mon Sep 17 00:00:00 2001 From: mxyhi Date: Thu, 30 Oct 2025 13:08:26 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=90=9B=20fix(order):=20=E5=BC=80?= =?UTF-8?q?=E4=BB=93=E5=89=8D=E5=85=88=E6=92=A4=E9=94=80=E6=89=80=E6=9C=89?= =?UTF-8?q?=E6=8C=82=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 先在开仓前取消所有挂单,防止残留挂单导致仓位叠加 - 取消挂单失败时记录警告,但仍继续开仓 --- trader/aster_trader.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/trader/aster_trader.go b/trader/aster_trader.go index e3a96aed..b821be61 100644 --- a/trader/aster_trader.go +++ b/trader/aster_trader.go @@ -522,6 +522,11 @@ func (t *AsterTrader) GetPositions() ([]map[string]interface{}, error) { // OpenLong 开多单 func (t *AsterTrader) OpenLong(symbol string, quantity float64, leverage int) (map[string]interface{}, error) { + // 开仓前先取消所有挂单,防止残留挂单导致仓位叠加 + if err := t.CancelAllOrders(symbol); err != nil { + log.Printf(" ⚠ 取消挂单失败(继续开仓): %v", err) + } + // 先设置杠杆 if err := t.SetLeverage(symbol, leverage); err != nil { return nil, fmt.Errorf("设置杠杆失败: %w", err) @@ -584,6 +589,11 @@ func (t *AsterTrader) OpenLong(symbol string, quantity float64, leverage int) (m // OpenShort 开空单 func (t *AsterTrader) OpenShort(symbol string, quantity float64, leverage int) (map[string]interface{}, error) { + // 开仓前先取消所有挂单,防止残留挂单导致仓位叠加 + if err := t.CancelAllOrders(symbol); err != nil { + log.Printf(" ⚠ 取消挂单失败(继续开仓): %v", err) + } + // 先设置杠杆 if err := t.SetLeverage(symbol, leverage); err != nil { return nil, fmt.Errorf("设置杠杆失败: %w", err)