Files
nofx/scripts/diagnose_orders.go
tinkle-community 799d8b9c2e feat: migrate timestamps to int64 and security improvements
- Convert all time.Time fields to int64 Unix milliseconds (UTC)
- Add PostgreSQL migration to convert timestamp columns to bigint
- Reduce Binance sync window from 7 days to 24 hours
- Fix dashboard trader name visibility (add nofx-text-main color)
- Add position value column to history table
- Remove hardcoded API keys from test files
2026-01-06 15:56:07 +08:00

190 lines
5.5 KiB
Go
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"flag"
"fmt"
"log"
"nofx/store"
"os"
"path/filepath"
"time"
)
func main() {
var dbPath string
var traderID string
flag.StringVar(&dbPath, "db", "./data/data.db", "数据库文件路径")
flag.StringVar(&traderID, "trader", "", "Trader ID可选")
flag.Parse()
// 确保数据库文件存在
absPath, err := filepath.Abs(dbPath)
if err != nil {
log.Fatalf("❌ 无效的数据库路径: %v", err)
}
if _, err := os.Stat(absPath); os.IsNotExist(err) {
log.Fatalf("❌ 数据库文件不存在: %s", absPath)
}
fmt.Printf("📂 数据库路径: %s\n", absPath)
// 打开数据库
s, err := store.New(absPath)
if err != nil {
log.Fatalf("❌ 无法打开数据库: %v", err)
}
defer s.Close()
orderStore := s.Order()
// 如果指定了 traderID获取该 trader 的订单
if traderID == "" {
fmt.Println("\n⚠ 未指定 trader_id使用: --trader <trader_id>")
fmt.Println(" 获取所有 trader 的统计信息...\n")
}
// 获取订单列表
orders, err := orderStore.GetTraderOrders(traderID, 100)
if err != nil {
log.Fatalf("❌ 获取订单失败: %v", err)
}
fmt.Printf("\n📋 找到 %d 条订单记录\n\n", len(orders))
if len(orders) == 0 {
fmt.Println("⚠️ 没有订单数据!可能的原因:")
fmt.Println(" 1. Trader 还没有执行过交易")
fmt.Println(" 2. CreateOrder 插入失败(重复键冲突)")
fmt.Println(" 3. 指定的 trader_id 不存在")
return
}
// 统计数据
var (
totalOrders = len(orders)
filledOrders = 0
withFilledAt = 0
withAvgFillPrice = 0
withOrderAction = 0
missingFilledAt = 0
missingAvgPrice = 0
missingOrderAction = 0
)
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
fmt.Printf("%-15s %-10s %-10s %-15s %-10s %-15s\n", "订单ID", "状态", "动作", "平均成交价", "成交时间", "问题")
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
for _, order := range orders {
issues := []string{}
if order.Status == "FILLED" {
filledOrders++
// 检查 filled_at
if order.FilledAt > 0 {
withFilledAt++
} else {
missingFilledAt++
issues = append(issues, "❌ 缺少成交时间")
}
// 检查 avg_fill_price
if order.AvgFillPrice > 0 {
withAvgFillPrice++
} else {
missingAvgPrice++
issues = append(issues, "❌ 成交价为0")
}
}
// 检查 order_action
if order.OrderAction != "" {
withOrderAction++
} else {
missingOrderAction++
issues = append(issues, "⚠️ 缺少订单动作")
}
issueStr := "✅ 正常"
if len(issues) > 0 {
issueStr = ""
for i, issue := range issues {
if i > 0 {
issueStr += ", "
}
issueStr += issue
}
}
filledAtStr := "N/A"
if order.FilledAt > 0 {
filledAtStr = time.UnixMilli(order.FilledAt).Format("01-02 15:04")
}
fmt.Printf("%-15s %-10s %-10s %-15.2f %-10s %s\n",
order.ExchangeOrderID[:min(15, len(order.ExchangeOrderID))],
order.Status,
order.OrderAction,
order.AvgFillPrice,
filledAtStr,
issueStr,
)
}
fmt.Println("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")
// 统计摘要
fmt.Printf("\n📊 统计摘要:\n")
fmt.Printf(" 总订单数: %d\n", totalOrders)
fmt.Printf(" 已成交订单: %d\n", filledOrders)
fmt.Printf(" 有成交时间: %d / %d (%.1f%%)\n", withFilledAt, filledOrders, float64(withFilledAt)/float64(max(filledOrders, 1))*100)
fmt.Printf(" 有成交价格: %d / %d (%.1f%%)\n", withAvgFillPrice, filledOrders, float64(withAvgFillPrice)/float64(max(filledOrders, 1))*100)
fmt.Printf(" 有订单动作: %d / %d (%.1f%%)\n", withOrderAction, totalOrders, float64(withOrderAction)/float64(max(totalOrders, 1))*100)
fmt.Printf("\n⚠ 问题订单:\n")
if missingFilledAt > 0 {
fmt.Printf(" ❌ %d 条订单缺少成交时间 (filled_at)\n", missingFilledAt)
}
if missingAvgPrice > 0 {
fmt.Printf(" ❌ %d 条订单成交价为 0 (avg_fill_price)\n", missingAvgPrice)
}
if missingOrderAction > 0 {
fmt.Printf(" ⚠️ %d 条订单缺少订单动作 (order_action)\n", missingOrderAction)
}
if missingFilledAt > 0 || missingAvgPrice > 0 {
fmt.Println("\n💡 这些订单无法在图表上显示,因为:")
fmt.Println(" - 缺少成交时间 → 前端无法定位到K线时间轴")
fmt.Println(" - 成交价为 0 → 前端会过滤掉 (line 164: if (!orderPrice || orderPrice === 0) return)")
fmt.Println("\n🔧 可能的原因:")
fmt.Println(" 1. UpdateOrderStatus 没有被正确调用")
fmt.Println(" 2. GetOrderStatus 返回的数据缺少 avgPrice 字段")
fmt.Println(" 3. Lighter 交易所的订单状态查询有问题")
}
if missingFilledAt == 0 && missingAvgPrice == 0 && missingOrderAction == 0 {
fmt.Println("\n✅ 所有订单数据完整!")
fmt.Println(" 如果图表仍然没有显示 B/S 标记,检查:")
fmt.Println(" 1. 前端是否正确调用了 /api/orders API")
fmt.Println(" 2. 浏览器控制台是否有错误")
fmt.Println(" 3. 订单时间是否在图表的时间范围内")
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
func max(a, b int) int {
if a > b {
return a
}
return b
}