update
This commit is contained in:
parent
1c10361a55
commit
6dea177565
@ -162,6 +162,26 @@ async def get_statistics(
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/account")
|
||||
async def get_account_status():
|
||||
"""
|
||||
获取账户状态
|
||||
|
||||
返回账户余额、已用保证金、可用保证金等信息
|
||||
"""
|
||||
try:
|
||||
service = get_paper_trading_service()
|
||||
account = service.get_account_status()
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"account": account
|
||||
}
|
||||
except Exception as e:
|
||||
logger.error(f"获取账户状态失败: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get("/statistics/by-grade")
|
||||
async def get_statistics_by_grade():
|
||||
"""按信号等级获取统计"""
|
||||
|
||||
@ -112,6 +112,11 @@ class Settings(BaseSettings):
|
||||
|
||||
# 模拟交易配置
|
||||
paper_trading_enabled: bool = True # 是否启用模拟交易
|
||||
paper_trading_initial_balance: float = 10000 # 初始本金 (USDT)
|
||||
paper_trading_leverage: int = 10 # 杠杆倍数
|
||||
paper_trading_margin_per_order: float = 1000 # 每单保证金 (USDT)
|
||||
paper_trading_max_orders: int = 10 # 最大持仓+挂单总数
|
||||
# 废弃的配置(保留兼容性)
|
||||
paper_trading_position_a: float = 1000 # A级信号仓位 (USDT)
|
||||
paper_trading_position_b: float = 500 # B级信号仓位 (USDT)
|
||||
paper_trading_position_c: float = 200 # C级信号仓位 (USDT)
|
||||
|
||||
@ -11,15 +11,6 @@ from app.config import get_settings
|
||||
from app.utils.logger import logger
|
||||
|
||||
|
||||
# 仓位大小配置
|
||||
POSITION_SIZE = {
|
||||
'A': 1000, # A级信号 1000 USDT
|
||||
'B': 500, # B级信号 500 USDT
|
||||
'C': 200, # C级信号 200 USDT
|
||||
'D': 0 # D级信号不开仓
|
||||
}
|
||||
|
||||
|
||||
class PaperTradingService:
|
||||
"""模拟交易服务"""
|
||||
|
||||
@ -28,6 +19,12 @@ class PaperTradingService:
|
||||
self.settings = get_settings()
|
||||
self.active_orders: Dict[str, PaperOrder] = {} # 内存缓存活跃订单
|
||||
|
||||
# 合约交易配置
|
||||
self.initial_balance = self.settings.paper_trading_initial_balance # 初始本金
|
||||
self.leverage = self.settings.paper_trading_leverage # 杠杆倍数
|
||||
self.margin_per_order = self.settings.paper_trading_margin_per_order # 每单保证金
|
||||
self.max_orders = self.settings.paper_trading_max_orders # 最大订单数
|
||||
|
||||
# 确保表已创建
|
||||
self._ensure_table_exists()
|
||||
|
||||
@ -89,16 +86,17 @@ class PaperTradingService:
|
||||
entry_price = signal.get('entry_price') or signal.get('price', 0)
|
||||
|
||||
# === 限制检查 ===
|
||||
# 1. 同一交易对同一方向最多 3 个订单
|
||||
# 1. 检查总订单数(持仓+挂单)是否超过最大限制
|
||||
total_orders = len(self.active_orders)
|
||||
if total_orders >= self.max_orders:
|
||||
logger.info(f"订单限制: 已达到最大订单数 {self.max_orders},跳过")
|
||||
return None
|
||||
|
||||
# 2. 检查是否有接近的挂单(价格差距 < 1%)
|
||||
same_direction_orders = [
|
||||
order for order in self.active_orders.values()
|
||||
if order.symbol == symbol and order.side == side
|
||||
]
|
||||
if len(same_direction_orders) >= 3:
|
||||
logger.info(f"订单限制: {symbol} {side.value} 方向已有 {len(same_direction_orders)} 个订单,跳过")
|
||||
return None
|
||||
|
||||
# 2. 检查是否有接近的挂单(价格差距 < 1%)
|
||||
pending_orders = [
|
||||
order for order in same_direction_orders
|
||||
if order.status == OrderStatus.PENDING
|
||||
@ -115,10 +113,10 @@ class PaperTradingService:
|
||||
logger.info(f"D级信号不开仓: {signal.get('symbol')}")
|
||||
return None
|
||||
|
||||
# 确定仓位大小
|
||||
quantity = POSITION_SIZE.get(grade, 0)
|
||||
if quantity == 0:
|
||||
return None
|
||||
# 固定使用保证金(不再根据等级区分)
|
||||
margin = self.margin_per_order # 每单固定 1000 USDT 保证金
|
||||
position_value = margin * self.leverage # 持仓价值 = 保证金 × 杠杆
|
||||
quantity = position_value # 订单数量(以 USDT 计价)
|
||||
|
||||
# 确定入场类型
|
||||
entry_type_str = signal.get('entry_type', 'market')
|
||||
@ -170,7 +168,8 @@ class PaperTradingService:
|
||||
|
||||
entry_type_text = "现价" if entry_type == EntryType.MARKET else "挂单"
|
||||
status_text = "已开仓" if status == OrderStatus.OPEN else "等待触发"
|
||||
logger.info(f"创建模拟订单: {order_id} | {symbol} {side.value} [{entry_type_text}] @ ${entry_price:,.2f} | {status_text} | 仓位: ${quantity}")
|
||||
logger.info(f"创建模拟订单: {order_id} | {symbol} {side.value} [{entry_type_text}] @ ${entry_price:,.2f} | {status_text}")
|
||||
logger.info(f" 保证金: ${margin:,.0f} | 杠杆: {self.leverage}x | 持仓价值: ${position_value:,.0f} | 当前订单数: {len(self.active_orders)}/{self.max_orders}")
|
||||
return order
|
||||
|
||||
except Exception as e:
|
||||
@ -574,6 +573,56 @@ class PaperTradingService:
|
||||
'by_symbol': {}
|
||||
}
|
||||
|
||||
def get_account_status(self) -> Dict[str, Any]:
|
||||
"""
|
||||
获取账户状态
|
||||
|
||||
Returns:
|
||||
账户状态信息,包括余额、已用保证金、可用保证金等
|
||||
"""
|
||||
# 计算已用保证金(每个活跃订单占用固定保证金)
|
||||
active_count = len(self.active_orders)
|
||||
used_margin = active_count * self.margin_per_order
|
||||
|
||||
# 计算已实现盈亏(从历史订单)
|
||||
db = db_service.get_session()
|
||||
try:
|
||||
closed_orders = db.query(PaperOrder).filter(
|
||||
PaperOrder.status.in_([
|
||||
OrderStatus.CLOSED_TP,
|
||||
OrderStatus.CLOSED_SL,
|
||||
OrderStatus.CLOSED_MANUAL
|
||||
])
|
||||
).all()
|
||||
|
||||
realized_pnl = sum(o.pnl_amount for o in closed_orders)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
# 计算当前余额
|
||||
current_balance = self.initial_balance + realized_pnl
|
||||
|
||||
# 计算可用保证金
|
||||
available_margin = current_balance - used_margin
|
||||
|
||||
# 计算可开仓数
|
||||
available_orders = self.max_orders - active_count
|
||||
|
||||
return {
|
||||
'initial_balance': self.initial_balance,
|
||||
'realized_pnl': round(realized_pnl, 2),
|
||||
'current_balance': round(current_balance, 2),
|
||||
'used_margin': round(used_margin, 2),
|
||||
'available_margin': round(available_margin, 2),
|
||||
'leverage': self.leverage,
|
||||
'margin_per_order': self.margin_per_order,
|
||||
'active_orders': active_count,
|
||||
'max_orders': self.max_orders,
|
||||
'available_orders': available_orders,
|
||||
'total_position_value': round(used_margin * self.leverage, 2),
|
||||
'margin_ratio': round((used_margin / current_balance * 100), 2) if current_balance > 0 else 0
|
||||
}
|
||||
|
||||
def _calculate_grade_statistics(self, orders: List[PaperOrder]) -> Dict[str, Any]:
|
||||
"""按信号等级统计"""
|
||||
result = {}
|
||||
|
||||
@ -517,6 +517,46 @@
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">账户余额</div>
|
||||
<div class="stat-value" :class="account.current_balance >= account.initial_balance ? 'positive' : 'negative'">
|
||||
${{ account.current_balance?.toFixed(2) || '0.00' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">已用保证金</div>
|
||||
<div class="stat-value">
|
||||
${{ account.used_margin?.toFixed(2) || '0.00' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">可用保证金</div>
|
||||
<div class="stat-value">
|
||||
${{ account.available_margin?.toFixed(2) || '0.00' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">杠杆 / 保证金率</div>
|
||||
<div class="stat-value">
|
||||
{{ account.leverage }}x / {{ account.margin_ratio?.toFixed(1) || '0' }}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">持仓数 / 最大</div>
|
||||
<div class="stat-value">
|
||||
{{ account.active_orders || 0 }} / {{ account.max_orders || 10 }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">总持仓价值</div>
|
||||
<div class="stat-value">
|
||||
${{ account.total_position_value?.toFixed(2) || '0.00' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 交易统计 -->
|
||||
<div class="stats-grid" style="margin-top: 16px;">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">总交易数</div>
|
||||
<div class="stat-value">{{ stats.total_trades }}</div>
|
||||
@ -526,7 +566,7 @@
|
||||
<div class="stat-value">{{ stats.win_rate.toFixed(1) }}%</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">总盈亏</div>
|
||||
<div class="stat-label">已实现盈亏</div>
|
||||
<div class="stat-value" :class="stats.total_pnl >= 0 ? 'positive' : 'negative'">
|
||||
${{ stats.total_pnl.toFixed(2) }}
|
||||
</div>
|
||||
@ -792,6 +832,20 @@
|
||||
by_grade: {},
|
||||
by_symbol: {}
|
||||
},
|
||||
account: {
|
||||
initial_balance: 10000,
|
||||
current_balance: 10000,
|
||||
used_margin: 0,
|
||||
available_margin: 10000,
|
||||
leverage: 10,
|
||||
margin_per_order: 1000,
|
||||
active_orders: 0,
|
||||
max_orders: 10,
|
||||
available_orders: 10,
|
||||
total_position_value: 0,
|
||||
margin_ratio: 0,
|
||||
realized_pnl: 0
|
||||
},
|
||||
monitorRunning: false,
|
||||
latestPrices: {},
|
||||
refreshInterval: null,
|
||||
@ -829,6 +883,7 @@
|
||||
this.fetchActiveOrders(),
|
||||
this.fetchHistoryOrders(),
|
||||
this.fetchStatistics(),
|
||||
this.fetchAccountStatus(),
|
||||
this.fetchMonitorStatus()
|
||||
]);
|
||||
} catch (e) {
|
||||
@ -907,6 +962,14 @@
|
||||
}
|
||||
},
|
||||
|
||||
async fetchAccountStatus() {
|
||||
const response = await fetch('/api/paper-trading/account');
|
||||
const data = await response.json();
|
||||
if (data.success) {
|
||||
this.account = data.account;
|
||||
}
|
||||
},
|
||||
|
||||
async fetchMonitorStatus() {
|
||||
const response = await fetch('/api/paper-trading/monitor/status');
|
||||
const data = await response.json();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user