11
This commit is contained in:
parent
bfae183e39
commit
cc324eead5
|
|
@ -3,6 +3,7 @@
|
|||
"""
|
||||
from fastapi import APIRouter, HTTPException, Header, Depends
|
||||
from fastapi import Query
|
||||
import asyncio
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import logging
|
||||
|
|
@ -640,6 +641,39 @@ async def get_realtime_positions(account_id: int = Depends(get_account_id)):
|
|||
|
||||
logger.info(f"获取到 {len(positions)} 个持仓")
|
||||
|
||||
# 并发获取所有持仓的挂单信息
|
||||
open_orders_map = {}
|
||||
try:
|
||||
position_symbols = [p.get('symbol') for p in positions if float(p.get('positionAmt', 0)) != 0]
|
||||
if position_symbols:
|
||||
logger.info(f"正在获取挂单信息: {position_symbols}")
|
||||
tasks = [client.get_open_orders(sym) for sym in position_symbols]
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
for sym, orders in zip(position_symbols, results):
|
||||
if isinstance(orders, list):
|
||||
# 过滤出止盈止损单
|
||||
conditional_orders = []
|
||||
for o in orders:
|
||||
o_type = o.get('type')
|
||||
if o_type in ['STOP_MARKET', 'TAKE_PROFIT_MARKET', 'STOP', 'TAKE_PROFIT']:
|
||||
conditional_orders.append({
|
||||
'orderId': o.get('orderId'),
|
||||
'type': o_type,
|
||||
'side': o.get('side'),
|
||||
'stopPrice': float(o.get('stopPrice', 0)),
|
||||
'price': float(o.get('price', 0)),
|
||||
'origType': o.get('origType'),
|
||||
'reduceOnly': o.get('reduceOnly'),
|
||||
'status': o.get('status')
|
||||
})
|
||||
if conditional_orders:
|
||||
open_orders_map[sym] = conditional_orders
|
||||
else:
|
||||
logger.warning(f"获取 {sym} 挂单失败: {orders}")
|
||||
except Exception as e:
|
||||
logger.error(f"批量获取挂单失败: {e}")
|
||||
|
||||
# 格式化持仓数据
|
||||
formatted_positions = []
|
||||
for pos in positions:
|
||||
|
|
@ -754,6 +788,7 @@ async def get_realtime_positions(account_id: int = Depends(get_account_id)):
|
|||
"atr": atr_value,
|
||||
"entry_order_id": entry_order_id,
|
||||
"entry_order_type": entry_order_type, # LIMIT / MARKET(用于仪表板展示“限价/市价”)
|
||||
"open_orders": open_orders_map.get(pos.get('symbol'), []), # 实时挂单信息
|
||||
})
|
||||
|
||||
logger.info(f"格式化后 {len(formatted_positions)} 个有效持仓")
|
||||
|
|
|
|||
|
|
@ -282,7 +282,23 @@ const StatsDashboard = () => {
|
|||
|
||||
const timestamp = new Date().toISOString().slice(0, 19).replace(/:/g, '-')
|
||||
const filename = `持仓记录_${timestamp}.json`
|
||||
const dataStr = JSON.stringify(openTrades, null, 2)
|
||||
|
||||
// 增强导出数据:添加易读的挂单汇总信息
|
||||
const exportData = openTrades.map(trade => {
|
||||
const slOrders = trade.open_orders?.filter(o => o.type.includes('STOP')) || []
|
||||
const tpOrders = trade.open_orders?.filter(o => o.type.includes('TAKE_PROFIT')) || []
|
||||
|
||||
return {
|
||||
...trade,
|
||||
// 添加易读的汇总字段
|
||||
active_sl_orders: slOrders.map(o => `${o.type} @ ${o.stopPrice} (${o.status})`).join('; '),
|
||||
active_tp_orders: tpOrders.map(o => `${o.type} @ ${o.stopPrice || o.price} (${o.status})`).join('; '),
|
||||
// 确保原始数据也在
|
||||
binance_open_orders_raw: trade.open_orders || []
|
||||
}
|
||||
})
|
||||
|
||||
const dataStr = JSON.stringify(exportData, null, 2)
|
||||
const dataBlob = new Blob([dataStr], { type: 'application/json' })
|
||||
const url = URL.createObjectURL(dataBlob)
|
||||
const link = document.createElement('a')
|
||||
|
|
@ -736,6 +752,25 @@ const StatsDashboard = () => {
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 显示币安实际挂单信息 */}
|
||||
{trade.open_orders && trade.open_orders.length > 0 && (
|
||||
<div className="open-orders-info" style={{ marginTop: '8px', borderTop: '1px dashed #eee', paddingTop: '6px' }}>
|
||||
<div style={{ fontSize: '12px', color: '#666', marginBottom: '4px', fontWeight: 'bold' }}>条件单 (Binance):</div>
|
||||
{trade.open_orders.map(order => (
|
||||
<div key={order.orderId} style={{ display: 'flex', justifyContent: 'space-between', fontSize: '12px', marginBottom: '4px', paddingLeft: '8px' }}>
|
||||
<span className={order.side === 'BUY' ? 'positive' : 'negative'} style={{ fontWeight: '500' }}>
|
||||
{order.type.replace('_MARKET', '')}
|
||||
</span>
|
||||
<span style={{ fontFamily: 'monospace' }}>
|
||||
{order.type.includes('STOP') ? `触发价: ${fmtPrice(order.stopPrice)}` : `价格: ${fmtPrice(order.price)}`}
|
||||
</span>
|
||||
<span style={{ color: '#999', fontSize: '11px' }}>{order.status}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="trade-actions">
|
||||
<div className={`trade-pnl ${parseFloat(trade.pnl || 0) >= 0 ? 'positive' : 'negative'}`}>
|
||||
{parseFloat(trade.pnl || 0).toFixed(2)} USDT
|
||||
|
|
|
|||
|
|
@ -2635,6 +2635,44 @@ class PositionManager:
|
|||
import traceback
|
||||
logger.error(f" 错误详情:\n{traceback.format_exc()}")
|
||||
|
||||
# 6. 同步挂单信息 (STOP_MARKET / TAKE_PROFIT_MARKET)
|
||||
if self.active_positions:
|
||||
logger.debug("正在同步持仓挂单信息...")
|
||||
try:
|
||||
tasks = []
|
||||
symbols = list(self.active_positions.keys())
|
||||
for symbol in symbols:
|
||||
tasks.append(self.client.get_open_orders(symbol))
|
||||
|
||||
results = await asyncio.gather(*tasks, return_exceptions=True)
|
||||
|
||||
for symbol, orders in zip(symbols, results):
|
||||
if isinstance(orders, list):
|
||||
# Filter for relevant orders (SL/TP)
|
||||
conditional_orders = []
|
||||
for o in orders:
|
||||
o_type = o.get('type')
|
||||
# 关注止盈止损单
|
||||
if o_type in ['STOP_MARKET', 'TAKE_PROFIT_MARKET', 'STOP', 'TAKE_PROFIT']:
|
||||
conditional_orders.append({
|
||||
'orderId': o.get('orderId'),
|
||||
'type': o_type,
|
||||
'side': o.get('side'),
|
||||
'stopPrice': float(o.get('stopPrice', 0)),
|
||||
'price': float(o.get('price', 0)),
|
||||
'origType': o.get('origType'),
|
||||
'reduceOnly': o.get('reduceOnly'),
|
||||
'status': o.get('status')
|
||||
})
|
||||
|
||||
if symbol in self.active_positions:
|
||||
self.active_positions[symbol]['openOrders'] = conditional_orders
|
||||
logger.debug(f"{symbol} 同步挂单: {len(conditional_orders)} 个")
|
||||
else:
|
||||
logger.warning(f"{symbol} 获取挂单失败: {orders}")
|
||||
except Exception as e:
|
||||
logger.error(f"同步挂单信息失败: {e}")
|
||||
|
||||
logger.info("持仓状态同步完成")
|
||||
|
||||
except Exception as e:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user