1
This commit is contained in:
parent
6428a401d0
commit
e62d761efb
@ -60,7 +60,9 @@ class PaperOrder(Base):
|
||||
exit_price = Column(Float, nullable=True) # 出场价
|
||||
|
||||
# 仓位信息
|
||||
quantity = Column(Float, default=1000) # 仓位大小 (USDT)
|
||||
quantity = Column(Float, default=1000) # 持仓价值 (USDT)
|
||||
margin = Column(Float, default=50) # 保证金 (USDT)
|
||||
leverage = Column(Integer, default=20) # 杠杆倍数
|
||||
|
||||
# 信号信息
|
||||
signal_grade = Column(SQLEnum(SignalGrade), default=SignalGrade.D)
|
||||
@ -107,7 +109,9 @@ class PaperOrder(Base):
|
||||
'take_profit': self.take_profit,
|
||||
'filled_price': self.filled_price,
|
||||
'exit_price': self.exit_price,
|
||||
'quantity': self.quantity,
|
||||
'quantity': self.quantity, # 持仓价值
|
||||
'margin': getattr(self, 'margin', self.quantity / 20), # 保证金
|
||||
'leverage': getattr(self, 'leverage', 20), # 杠杆倍数
|
||||
'signal_grade': self.signal_grade.value if self.signal_grade else None,
|
||||
'signal_type': self.signal_type,
|
||||
'confidence': self.confidence,
|
||||
|
||||
@ -304,7 +304,9 @@ class PaperTradingService:
|
||||
stop_loss=signal.get('stop_loss', 0),
|
||||
take_profit=signal.get('take_profit', 0),
|
||||
filled_price=filled_price,
|
||||
quantity=quantity,
|
||||
quantity=quantity, # 持仓价值
|
||||
margin=margin, # 保证金
|
||||
leverage=self.leverage, # 杠杆倍数
|
||||
signal_grade=SignalGrade(grade),
|
||||
signal_type=signal.get('signal_type') or signal.get('type', 'swing'),
|
||||
confidence=signal.get('confidence', 0),
|
||||
@ -352,10 +354,15 @@ class PaperTradingService:
|
||||
|
||||
def _calculate_dynamic_position(self, position_size: str, symbol: str) -> tuple:
|
||||
"""
|
||||
根据 LLM 建议的仓位大小计算实际保证金和持仓价值
|
||||
根据 LLM 建议的仓位大小计算实际保证金和持仓价值(复利策略)
|
||||
|
||||
计算逻辑:
|
||||
- 可用保证金 = 余额 - (持仓 + 挂单占用保证金)
|
||||
- 根据 position_size 按可用保证金的百分比分配
|
||||
- 持仓价值 = 保证金 × 杠杆
|
||||
|
||||
Args:
|
||||
position_size: 'heavy' / 'medium' / 'light'
|
||||
position_size: 'heavy' / 'medium' / 'light' / 'micro'
|
||||
symbol: 交易对
|
||||
|
||||
Returns:
|
||||
@ -364,49 +371,45 @@ class PaperTradingService:
|
||||
# 获取当前账户状态
|
||||
account = self.get_account_status()
|
||||
balance = account['current_balance']
|
||||
used_margin = account['used_margin']
|
||||
max_leverage = self.leverage # 最大杠杆 20x
|
||||
used_margin = account['used_margin'] # 已用保证金(持仓+挂单)
|
||||
|
||||
# 计算可用保证金空间
|
||||
# 全仓模式下:最大持仓价值 = 余额 × 最大杠杆
|
||||
max_position_value = balance * max_leverage
|
||||
current_position_value = account['total_position_value']
|
||||
available_position_value = max_position_value - current_position_value
|
||||
# 计算可用保证金(复利核心)
|
||||
available_margin = balance - used_margin
|
||||
|
||||
if available_position_value <= 0:
|
||||
logger.warning(f"已达最大杠杆限制,无法开仓")
|
||||
if available_margin <= 0:
|
||||
logger.warning(f"可用保证金不足,无法开仓(余额: ${balance:.2f}, 已用: ${used_margin:.2f})")
|
||||
return 0, 0
|
||||
|
||||
# 根据 position_size 确定仓位比例
|
||||
# heavy: 可用空间的 30%
|
||||
# medium: 可用空间的 15%
|
||||
# light: 可用空间的 5%
|
||||
# 根据 position_size 确定保证金比例(按可用保证金百分比)
|
||||
# micro: 5%, light: 10%, medium: 15%, heavy: 20%
|
||||
size_ratio = {
|
||||
'heavy': 0.30,
|
||||
'micro': 0.05,
|
||||
'light': 0.10,
|
||||
'medium': 0.15,
|
||||
'light': 0.05
|
||||
}.get(position_size, 0.05)
|
||||
'heavy': 0.20
|
||||
}.get(position_size, 0.10)
|
||||
|
||||
# 计算目标持仓价值
|
||||
target_position_value = available_position_value * size_ratio
|
||||
# 计算目标保证金(直接使用可用保证金的百分比)
|
||||
target_margin = available_margin * size_ratio
|
||||
|
||||
# 设置最小和最大限制
|
||||
min_position_value = 1000 # 最小持仓价值 1000 USDT
|
||||
max_single_position = balance * 5 # 单笔最大不超过 5x 杠杆
|
||||
# 设置最小和最大保证金限制
|
||||
min_margin = 50 # 最小保证金 50 USDT(对应 1000 USDT 持仓价值)
|
||||
max_single_margin = balance * 0.25 # 单笔最大不超过余额的 25%
|
||||
|
||||
position_value = max(min_position_value, min(target_position_value, max_single_position))
|
||||
margin = max(min_margin, min(target_margin, max_single_margin))
|
||||
|
||||
# 确保不超过可用空间
|
||||
position_value = min(position_value, available_position_value)
|
||||
# 确保不超过可用保证金
|
||||
margin = min(margin, available_margin)
|
||||
|
||||
# 修正浮点数精度问题,保留 2 位小数
|
||||
position_value = round(position_value, 2)
|
||||
margin = round(margin, 2)
|
||||
|
||||
# 计算对应的保证金
|
||||
margin = round(position_value / max_leverage, 2)
|
||||
# 计算持仓价值(保证金 × 杠杆)
|
||||
position_value = round(margin * self.leverage, 2)
|
||||
|
||||
logger.info(f"动态仓位计算: {position_size} | 可用空间: ${available_position_value:,.0f} | "
|
||||
f"目标仓位: ${position_value:,.0f} | 保证金: ${margin:,.0f}")
|
||||
logger.info(f"动态仓位计算: {position_size} | 余额: ${balance:.2f} | 已用保证金: ${used_margin:.2f} | "
|
||||
f"可用保证金: ${available_margin:.2f} | 目标保证金: ${margin:.2f} ({size_ratio*100:.0f}%) | "
|
||||
f"持仓价值: ${position_value:.2f}")
|
||||
|
||||
return margin, position_value
|
||||
|
||||
|
||||
108
backend/scripts/migrate_add_margin_leverage.py
Normal file
108
backend/scripts/migrate_add_margin_leverage.py
Normal file
@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
添加 margin 和 leverage 字段到 paper_orders 表
|
||||
|
||||
运行方式:
|
||||
cd backend && python scripts/migrate_add_margin_leverage.py
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
|
||||
# 添加父目录到路径
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from app.services.db_service import db_service
|
||||
from sqlalchemy import text, inspect
|
||||
import logging
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def check_column_exists(table_name, column_name):
|
||||
"""检查列是否存在"""
|
||||
inspector = inspect(db_service.engine)
|
||||
columns = [col['name'] for col in inspector.get_columns(table_name)]
|
||||
return column_name in columns
|
||||
|
||||
|
||||
def migrate():
|
||||
"""执行迁移"""
|
||||
try:
|
||||
with db_service.engine.connect() as conn:
|
||||
# 检查表是否存在
|
||||
inspector = inspect(db_service.engine)
|
||||
if 'paper_orders' not in inspector.get_table_names():
|
||||
logger.error("❌ paper_orders 表不存在")
|
||||
return False
|
||||
|
||||
logger.info("开始迁移 paper_orders 表...")
|
||||
|
||||
# 检查并添加 margin 列
|
||||
if not check_column_exists('paper_orders', 'margin'):
|
||||
logger.info("➕ 添加 margin 列...")
|
||||
conn.execute(text("""
|
||||
ALTER TABLE paper_orders
|
||||
ADD COLUMN margin FLOAT DEFAULT 50
|
||||
"""))
|
||||
conn.commit()
|
||||
logger.info("✅ margin 列添加成功")
|
||||
|
||||
# 为现有记录计算并设置 margin 值
|
||||
logger.info("🔄 为现有记录计算 margin...")
|
||||
conn.execute(text("""
|
||||
UPDATE paper_orders
|
||||
SET margin = ROUND(quantity / 20.0, 2)
|
||||
WHERE margin IS NULL OR margin = 50
|
||||
"""))
|
||||
conn.commit()
|
||||
logger.info("✅ 现有记录的 margin 已更新")
|
||||
else:
|
||||
logger.info("✓ margin 列已存在,跳过")
|
||||
|
||||
# 检查并添加 leverage 列
|
||||
if not check_column_exists('paper_orders', 'leverage'):
|
||||
logger.info("➕ 添加 leverage 列...")
|
||||
conn.execute(text("""
|
||||
ALTER TABLE paper_orders
|
||||
ADD COLUMN leverage INTEGER DEFAULT 20
|
||||
"""))
|
||||
conn.commit()
|
||||
logger.info("✅ leverage 列添加成功")
|
||||
|
||||
# 为现有记录设置 leverage 值
|
||||
logger.info("🔄 为现有记录设置 leverage...")
|
||||
conn.execute(text("""
|
||||
UPDATE paper_orders
|
||||
SET leverage = 20
|
||||
WHERE leverage IS NULL OR leverage = 20
|
||||
"""))
|
||||
conn.commit()
|
||||
logger.info("✅ 现有记录的 leverage 已更新")
|
||||
else:
|
||||
logger.info("✓ leverage 列已存在,跳过")
|
||||
|
||||
logger.info("\n" + "=" * 50)
|
||||
logger.info("✅ 迁移完成!")
|
||||
logger.info("=" * 50)
|
||||
|
||||
# 显示更新后的表结构
|
||||
inspector = inspect(db_service.engine)
|
||||
columns = inspector.get_columns('paper_orders')
|
||||
logger.info("\n📊 paper_orders 表结构:")
|
||||
for col in columns:
|
||||
if col['name'] in ['quantity', 'margin', 'leverage']:
|
||||
logger.info(f" - {col['name']}: {col['type']}")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 迁移失败: {e}")
|
||||
import traceback
|
||||
logger.error(traceback.format_exc())
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = migrate()
|
||||
sys.exit(0 if success else 1)
|
||||
@ -1294,7 +1294,9 @@
|
||||
<th>浮动盈亏</th>
|
||||
<th>止损</th>
|
||||
<th>止盈</th>
|
||||
<th>仓位</th>
|
||||
<th>保证金</th>
|
||||
<th>杠杆</th>
|
||||
<th>持仓价值</th>
|
||||
<th>时间</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
@ -1338,7 +1340,9 @@
|
||||
${{ order.take_profit?.toLocaleString() }}
|
||||
</span>
|
||||
</td>
|
||||
<td>${{ order.quantity }}</td>
|
||||
<td>${{ order.margin?.toFixed(2) || (order.quantity / 20).toFixed(2) }}</td>
|
||||
<td>{{ order.leverage || 20 }}x</td>
|
||||
<td>${{ order.quantity?.toFixed(2) || '-' }}</td>
|
||||
<td>{{ formatTime(order.status === 'open' ? order.opened_at : order.created_at) }}</td>
|
||||
<td class="action-cell">
|
||||
<button class="action-btn danger" @click="closeOrder(order)">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user