This commit is contained in:
aaron 2026-02-21 19:45:18 +08:00
parent a76c82b9b1
commit d5b6b316dd
5 changed files with 0 additions and 618 deletions

View File

@ -1,124 +0,0 @@
# 异常订单检查工具
用于检查和修复数据库中导致错误平仓消息的异常订单。
## 问题原因
根据之前的分析,"莫名其妙出现的平仓消息"通常是由以下异常订单导致的:
1. **OPEN 状态但成交价为 0 的订单**:这些订单被系统认为是"已成交"但价格无效,导致平仓计算时显示 `$0.00`
2. **PENDING 状态但有成交价的订单**:状态不一致,可能导致重复处理
## 使用方法
### 方法 1: Python 脚本(推荐)
在服务器上执行:
```bash
cd /path/to/Stock_Agent/backend
# 检查异常订单(不会修改数据)
python3 check_abnormal_orders.py
# 自动修复异常订单(会删除/修改数据,需要确认)
python3 fix_abnormal_orders.py
```
### 方法 2: SQL 查询
如果有 SQLite 命令行工具:
```bash
cd /path/to/Stock_Agent/backend
# 需要先确认数据库路径
sqlite3 /path/to/your/database.db < check_abnormal_orders.sql
```
或者直接进入 SQLite
```bash
sqlite3 /path/to/your/database.db
# 然后复制粘贴 check_abnormal_orders.sql 中的查询语句
```
### 方法 3: Bash 脚本
```bash
cd /path/to/Stock_Agent/backend
# 需要指定数据库路径
./check_orders.sh /path/to/your/database.db
```
## 检查结果说明
脚本会检查以下问题:
| 问题 | 说明 | 影响 |
|------|------|------|
| OPEN 状态但成交价无效 | 订单已成交但 `filled_price` 为 0 或 NULL | 可能导致 `$0.00` 平仓消息 |
| PENDING 状态但有成交价 | 状态应该是 OPEN 但实际是 PENDING | 状态不一致 |
| 平仓订单价格异常 | 出场价或成交价为 0 | 历史记录错误 |
| 重复订单 | 同一交易对短时间内创建多个订单 | 可能是重复信号 |
## 修复操作
确认有异常订单后,可以:
1. **手动删除异常订单**(最安全):
```sql
-- 删除 OPEN 状态但成交价无效的订单
DELETE FROM paper_orders
WHERE status = 'OPEN'
AND (filled_price IS NULL OR filled_price = 0);
```
2. **使用 Python 自动修复**
```bash
python3 fix_abnormal_orders.py
# 脚本会要求确认,输入 yes 确认删除/修复
```
3. **重启服务**(推荐):
修复后重启服务,让系统重新加载正确的活跃订单:
```bash
# 重启 FastAPI 服务
systemctl restart stock-agent # 或你的服务名称
```
## 防止再次出现
代码中已添加防御性检查(在 `paper_trading_service.py` 中):
1. `_load_active_orders()` 会跳过异常订单
2. `_check_order_trigger()` 会检查成交价有效性
3. `_close_order()` 会防止无效订单平仓
但建议定期运行检查脚本,确保数据库状态正常。
## 紧急处理
如果正在出现大量错误消息,可以:
1. **临时停止服务**
```bash
systemctl stop stock-agent
```
2. **运行检查和修复**
```bash
python3 check_abnormal_orders.py
python3 fix_abnormal_orders.py
```
3. **重启服务**
```bash
systemctl start stock-agent
```

View File

@ -1,210 +0,0 @@
#!/usr/bin/env python3
"""
检查数据库中的异常订单
用于调试莫名奇妙出现的平仓消息问题
"""
import sys
import os
# 添加项目路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from app.services.db_service import db_service
from app.models.paper_trading import PaperOrder, OrderStatus
from sqlalchemy import text
from datetime import datetime, timedelta
def check_abnormal_orders():
"""检查所有异常订单"""
db = db_service.get_session()
print("=" * 80)
print("异常订单检查报告")
print("=" * 80)
print(f"检查时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print()
# 1. 检查 OPEN 状态但 filled_price 为 0 或 None 的订单
print("【1】OPEN 状态但成交价无效的订单(可能导致错误平仓):")
print("-" * 80)
invalid_filled_orders = db.query(PaperOrder).filter(
PaperOrder.status == OrderStatus.OPEN,
(PaperOrder.filled_price == None) | (PaperOrder.filled_price == 0)
).all()
if invalid_filled_orders:
for order in invalid_filled_orders:
print(f" ⚠️ 订单ID: {order.order_id}")
print(f" 交易对: {order.symbol}")
print(f" 方向: {order.side.value}")
print(f" 状态: {order.status.value}")
print(f" 入场价: {order.entry_price}")
print(f" 成交价: {order.filled_price} ❌ 无效")
print(f" 创建时间: {order.created_at}")
print()
else:
print(" ✅ 无异常订单")
print()
# 2. 检查 PENDING 状态但已有成交价的订单
print("【2】PENDING 状态但有成交价的订单(状态不一致):")
print("-" * 80)
pending_with_filled = db.query(PaperOrder).filter(
PaperOrder.status == OrderStatus.PENDING,
PaperOrder.filled_price != None,
PaperOrder.filled_price > 0
).all()
if pending_with_filled:
for order in pending_with_filled:
print(f" ⚠️ 订单ID: {order.order_id}")
print(f" 交易对: {order.symbol}")
print(f" 成交价: {order.filled_price}")
print(f" 状态: PENDING应该是 OPEN")
print()
else:
print(" ✅ 无异常订单")
print()
# 3. 检查最近1小时内平仓的订单入场价或出场价为0
print("【3】最近平仓订单中价格异常的记录:")
print("-" * 80)
one_hour_ago = datetime.now() - timedelta(hours=1)
closed_abnormal = db.query(PaperOrder).filter(
PaperOrder.status.in_([
OrderStatus.CLOSED_TP,
OrderStatus.CLOSED_SL,
OrderStatus.CLOSED_BE,
OrderStatus.CLOSED_MANUAL
]),
PaperOrder.closed_at >= one_hour_ago,
(
(PaperOrder.filled_price == None) | (PaperOrder.filled_price == 0) |
(PaperOrder.exit_price == None) | (PaperOrder.exit_price == 0)
)
).all()
if closed_abnormal:
for order in closed_abnormal:
print(f" ⚠️ 订单ID: {order.order_id}")
print(f" 交易对: {order.symbol}")
print(f" 成交价: {order.filled_price}")
print(f" 出场价: {order.exit_price}")
print(f" 平仓时间: {order.closed_at}")
print(f" 平仓原因: {order.status.value}")
print()
else:
print(" ✅ 无异常记录")
print()
# 4. 检查所有活跃订单的状态统计
print("【4】活跃订单状态统计:")
print("-" * 80)
active_stats = db.execute(text("""
SELECT
status,
COUNT(*) as count,
SUM(CASE WHEN filled_price IS NULL OR filled_price = 0 THEN 1 ELSE 0 END) as invalid_count
FROM paper_orders
WHERE status IN ('PENDING', 'OPEN')
GROUP BY status
"""))
for row in active_stats:
status_name = row[0]
count = row[1]
invalid = row[2]
print(f" {status_name}: {count} 个(异常: {invalid} 个)")
print()
# 5. 检查重复订单
print("【5】可能重复的订单同一交易对、同一方向、相近创建时间:")
print("-" * 80)
duplicates = db.execute(text("""
SELECT symbol, side, created_at, order_id, entry_price
FROM paper_orders
WHERE status IN ('PENDING', 'OPEN')
AND created_at >= datetime('now', '-1 hour')
ORDER BY symbol, side, created_at DESC
""")).fetchall()
# 简单的重复检测:同一交易对+方向在1分钟内创建
seen = {}
duplicate_found = False
for row in duplicates:
key = (row[0], row[1]) # symbol + side
if key in seen:
prev_time = seen[key]
if abs((row[2] - prev_time).total_seconds()) < 60:
print(f" ⚠️ 可能重复: {row[0]} {row[1]}")
print(f" 订单ID: {row[3]}")
print(f" 时间: {row[2]}")
duplicate_found = True
seen[key] = row[2]
if not duplicate_found:
print(" ✅ 无明显重复")
print()
# 6. 最近的活动日志
print("【6】最近10条订单变更记录按时间倒序:")
print("-" * 80)
# 使用 COALESCE 来优先显示 closed_at其次 opened_at最后 created_at
recent_orders = db.execute(text("""
SELECT
COALESCE(closed_at, opened_at, created_at) as last_time,
symbol,
side,
status,
entry_price,
filled_price
FROM paper_orders
ORDER BY last_time DESC
LIMIT 10
""")).fetchall()
for order in recent_orders:
last_time = order[0]
symbol = order[1]
side = order[2]
status = order[3]
entry_price = order[4]
filled_price = order[5]
# last_time 是字符串,直接格式化
if last_time:
if isinstance(last_time, str):
time_str = last_time[11:19] # 提取 HH:MM:SS 部分
else:
time_str = last_time.strftime('%H:%M:%S')
else:
time_str = 'N/A'
filled_str = f"{filled_price:.2f}" if filled_price else "NULL"
entry_str = f"{entry_price:.2f}" if entry_price else "NULL"
print(f" {time_str} | {symbol} | {side:4s} | {status:15s} | "
f"入场:{entry_str} 成交:{filled_str}")
print()
print("=" * 80)
print("检查完成")
print("=" * 80)
# 输出清理建议
if invalid_filled_orders:
print()
print("🔧 发现异常订单!建议执行以下操作:")
print()
print("1. 删除 OPEN 状态但成交价无效的订单:")
for order in invalid_filled_orders:
print(f" DELETE FROM paper_orders WHERE order_id = '{order.order_id}';")
print()
print("2. 或者运行 python fix_abnormal_orders.py 自动修复")
db.close()
if __name__ == "__main__":
check_abnormal_orders()

View File

@ -1,91 +0,0 @@
-- ============================================================
-- 异常订单检查 SQL
-- 可以直接在 SQLite 命令行或数据库管理工具中执行
-- ============================================================
-- 1. 检查 OPEN 状态但成交价无效的订单(最可能的问题来源)
SELECT
order_id,
symbol,
side,
status,
entry_price,
filled_price,
created_at,
'OPEN状态但成交价无效' as issue
FROM paper_orders
WHERE status = 'OPEN'
AND (filled_price IS NULL OR filled_price = 0);
-- 2. 检查 PENDING 状态但有成交价的订单(状态不一致)
SELECT
order_id,
symbol,
side,
status,
entry_price,
filled_price,
created_at,
'PENDING状态但有成交价' as issue
FROM paper_orders
WHERE status = 'PENDING'
AND filled_price IS NOT NULL
AND filled_price > 0;
-- 3. 检查最近平仓订单中价格异常的记录
SELECT
order_id,
symbol,
side,
status,
entry_price,
filled_price,
exit_price,
closed_at,
'平仓订单价格异常' as issue
FROM paper_orders
WHERE status IN ('CLOSED_TP', 'CLOSED_SL', 'CLOSED_BE', 'CLOSED_MANUAL')
AND closed_at >= datetime('now', '-1 hour')
AND (
filled_price IS NULL OR filled_price = 0 OR
exit_price IS NULL OR exit_price = 0
);
-- 4. 活跃订单状态统计
SELECT
status,
COUNT(*) as total,
SUM(CASE WHEN filled_price IS NULL OR filled_price = 0 THEN 1 ELSE 0 END) as invalid_count
FROM paper_orders
WHERE status IN ('PENDING', 'OPEN')
GROUP BY status;
-- 5. 检查可能重复的订单(同一交易对+方向,最近创建)
SELECT
symbol,
side,
COUNT(*) as count,
MIN(created_at) as first_created,
MAX(created_at) as last_created
FROM paper_orders
WHERE status IN ('PENDING', 'OPEN')
AND created_at >= datetime('now', '-1 hour')
GROUP BY symbol, side
HAVING COUNT(*) > 1;
-- ============================================================
-- 清理异常订单的 SQL谨慎执行
-- ============================================================
-- 删除 OPEN 状态但成交价无效的订单
-- WARNING: 执行前请先确认上面的查询结果!
-- DELETE FROM paper_orders
-- WHERE status = 'OPEN'
-- AND (filled_price IS NULL OR filled_price = 0);
-- 修复 PENDING 状态但有成交价的订单
-- UPDATE paper_orders
-- SET status = 'OPEN'
-- WHERE status = 'PENDING'
-- AND filled_price IS NOT NULL
-- AND filled_price > 0;

View File

@ -1,88 +0,0 @@
#!/bin/bash
# ============================================================
# 异常订单快速检查脚本
# 用法: ./check_orders.sh [database_path]
# ============================================================
DB_PATH="${1:-/path/to/your/database.db}"
echo "=========================================="
echo "异常订单检查报告"
echo "=========================================="
echo "数据库: $DB_PATH"
echo "检查时间: $(date '+%Y-%m-%d %H:%M:%S')"
echo ""
if [ ! -f "$DB_PATH" ]; then
echo "错误: 找不到数据库文件 $DB_PATH"
echo "请指定正确的数据库路径"
exit 1
fi
# 1. 检查 OPEN 状态但成交价无效的订单
echo "【1】OPEN 状态但成交价无效的订单:"
echo "----------------------------------------"
sqlite3 "$DB_PATH" "
SELECT
' ' || substr(order_id, 1, 8) || '...' ||
' | ' || symbol ||
' | ' || side ||
' | 成交价:' || COALESCE(CAST(filled_price AS TEXT), 'NULL')
FROM paper_orders
WHERE status = 'OPEN'
AND (filled_price IS NULL OR filled_price = 0)
LIMIT 10;
" || echo " ✅ 无异常订单"
echo ""
# 2. 检查 PENDING 状态但有成交价的订单
echo "【2】PENDING 状态但有成交价的订单:"
echo "----------------------------------------"
sqlite3 "$DB_PATH" "
SELECT
' ' || substr(order_id, 1, 8) || '...' ||
' | ' || symbol ||
' | 状态应该是OPEN'
FROM paper_orders
WHERE status = 'PENDING'
AND filled_price IS NOT NULL
AND filled_price > 0
LIMIT 10;
" || echo " ✅ 无异常订单"
echo ""
# 3. 活跃订单统计
echo "【3】活跃订单统计:"
echo "----------------------------------------"
sqlite3 "$DB_PATH" "
SELECT
' ' || status || ': ' || COUNT(*) || ' 个'
FROM paper_orders
WHERE status IN ('PENDING', 'OPEN')
GROUP BY status;
"
echo ""
# 4. 最近异常平仓记录
echo "【4】最近1小时内价格异常的平仓记录:"
echo "----------------------------------------"
sqlite3 "$DB_PATH" "
SELECT
' ' || substr(order_id, 1, 8) || '...' ||
' | ' || symbol ||
' | 成交:' || COALESCE(CAST(filled_price AS TEXT), 'NULL') ||
' | 出场:' || COALESCE(CAST(exit_price AS TEXT), 'NULL')
FROM paper_orders
WHERE status IN ('CLOSED_TP', 'CLOSED_SL', 'CLOSED_BE', 'CLOSED_MANUAL')
AND closed_at >= datetime('now', '-1 hour')
AND (
filled_price IS NULL OR filled_price = 0 OR
exit_price IS NULL OR exit_price = 0
)
LIMIT 10;
" || echo " ✅ 无异常记录"
echo ""
echo "=========================================="
echo "检查完成"
echo "=========================================="

View File

@ -1,105 +0,0 @@
#!/usr/bin/env python3
"""
自动修复数据库中的异常订单
"""
import sys
import os
# 添加项目路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from app.services.db_service import db_service
from app.models.paper_trading import PaperOrder, OrderStatus
from datetime import datetime
def fix_abnormal_orders():
"""修复异常订单"""
db = db_service.get_session()
print("=" * 80)
print("异常订单自动修复")
print("=" * 80)
print()
fixed_count = 0
# 1. 删除 OPEN 状态但 filled_price 为 0 或 None 的订单
print("【1】清理 OPEN 状态但成交价无效的订单...")
invalid_orders = db.query(PaperOrder).filter(
PaperOrder.status == OrderStatus.OPEN,
(PaperOrder.filled_price == None) | (PaperOrder.filled_price == 0)
).all()
if invalid_orders:
print(f" 发现 {len(invalid_orders)} 个异常订单:")
for order in invalid_orders:
print(f" - {order.order_id} | {order.symbol} | {order.side.value} | 成交价: {order.filled_price}")
# 确认删除
confirm = input("\n 确认删除这些订单? (yes/no): ")
if confirm.lower() == 'yes':
for order in invalid_orders:
db.delete(order)
fixed_count += 1
db.commit()
print(f" ✅ 已删除 {len(invalid_orders)} 个异常订单")
else:
print(" ⏭️ 跳过删除")
else:
print(" ✅ 无异常订单")
print()
# 2. 修复 PENDING 状态但有成交价的订单
print("【2】修复 PENDING 状态但有成交价的订单...")
pending_orders = db.query(PaperOrder).filter(
PaperOrder.status == OrderStatus.PENDING,
PaperOrder.filled_price != None,
PaperOrder.filled_price > 0
).all()
if pending_orders:
print(f" 发现 {len(pending_orders)} 个状态不一致的订单:")
for order in pending_orders:
print(f" - {order.order_id} | {order.symbol} | 状态: PENDING → OPEN")
confirm = input("\n 确认修复这些订单? (yes/no): ")
if confirm.lower() == 'yes':
for order in pending_orders:
order.status = OrderStatus.OPEN
fixed_count += 1
db.commit()
print(f" ✅ 已修复 {len(pending_orders)} 个订单")
else:
print(" ⏭️ 跳过修复")
else:
print(" ✅ 无异常订单")
print()
# 3. 删除无用的测试数据(可选)
print("【3】检查是否有测试数据需要清理...")
test_orders = db.query(PaperOrder).filter(
PaperOrder.symbol.like('%TEST%')
).count()
if test_orders > 0:
print(f" 发现 {test_orders} 个测试订单")
confirm = input(" 是否删除测试数据? (yes/no): ")
if confirm.lower() == 'yes':
db.execute("DELETE FROM paper_orders WHERE symbol LIKE '%TEST%'")
db.commit()
print(f" ✅ 已删除测试数据")
else:
print(" ✅ 无测试数据")
print()
print("=" * 80)
print(f"修复完成!共处理 {fixed_count} 个异常订单")
print("=" * 80)
db.close()
if __name__ == "__main__":
fix_abnormal_orders()