Add demo trading page, trader snapshot service and trading tables
Add trading_symbols, trading_analyses, trading_orders and trading_events. The new trader service publishes account, position and open-order snapshots of the Binance demo account to Redis every 3s. The /trading page shows engine status, start/stop, balances, positions, the symbol list with angle selection, open orders and the event timeline.
This commit is contained in:
1 parent
154fe2b687
commit
005943a54c
12 files changed
+1289
-3
No files matched your search
@@ -1,6 +1,8 @@
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
|
||||
from sqlalchemy import Boolean, CheckConstraint, DateTime, ForeignKey, LargeBinary, String, func
|
||||
from sqlalchemy import BigInteger, Boolean, CheckConstraint, DateTime, ForeignKey, Index, Integer, LargeBinary, Numeric, String, Text, func
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from alphapulse.core.db import Base
|
||||
@@ -22,3 +24,104 @@ class TradingAccount(Base):
|
||||
provider_id: Mapped[int | None] = mapped_column(ForeignKey("ai_providers.id", ondelete="SET NULL"))
|
||||
reasoning_effort: Mapped[str | None] = mapped_column(String(8))
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
|
||||
class TradingSymbol(Base):
|
||||
__tablename__ = "trading_symbols"
|
||||
|
||||
symbol: Mapped[str] = mapped_column(String(32), primary_key=True)
|
||||
# short:24h 内平仓;long:12–48h 平仓
|
||||
angle: Mapped[str] = mapped_column(String(8))
|
||||
added_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
# 待执行的分析请求:多个触发在执行前合并为一次
|
||||
analysis_requested_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
analysis_reasons: Mapped[list[str]] = mapped_column(JSONB, default=list)
|
||||
|
||||
|
||||
class TradingAnalysis(Base):
|
||||
"""一次模型分析;action=plan 的行同时是一个交易计划,plan_* 等字段记录其生命周期"""
|
||||
|
||||
__tablename__ = "trading_analyses"
|
||||
__table_args__ = (Index("ix_trading_analyses_symbol_started", "symbol", "started_at"),)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
symbol: Mapped[str] = mapped_column(String(32))
|
||||
angle: Mapped[str] = mapped_column(String(8))
|
||||
reasons: Mapped[list[str]] = mapped_column(JSONB)
|
||||
# 停止状态下的分析只记录计划、不下单
|
||||
dry_run: Mapped[bool] = mapped_column(Boolean)
|
||||
# running / done / failed
|
||||
status: Mapped[str] = mapped_column(String(16), default="running")
|
||||
# plan / keep / exit
|
||||
action: Mapped[str | None] = mapped_column(String(8))
|
||||
# submit_trade_plan 的参数原样保存
|
||||
decision: Mapped[dict | None] = mapped_column(JSONB)
|
||||
# 程序算出的各档数量、风险金额、盈亏比等
|
||||
sizing: Mapped[dict | None] = mapped_column(JSONB)
|
||||
text: Mapped[str | None] = mapped_column(Text)
|
||||
transcript: Mapped[list | None] = mapped_column(JSONB)
|
||||
provider_id: Mapped[int | None] = mapped_column(ForeignKey("ai_providers.id", ondelete="SET NULL"))
|
||||
model: Mapped[str | None] = mapped_column(String(128))
|
||||
reasoning_effort: Mapped[str | None] = mapped_column(String(8))
|
||||
usage: Mapped[dict | None] = mapped_column(JSONB)
|
||||
error: Mapped[str | None] = mapped_column(Text)
|
||||
started_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
# pending(挂单中)/ open(持仓中)/ closed / canceled(未成交即结束)/ replaced(被新计划替换)
|
||||
plan_status: Mapped[str | None] = mapped_column(String(16))
|
||||
valid_until: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
first_fill_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
close_deadline: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
closed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
||||
close_reason: Mapped[str | None] = mapped_column(String(32))
|
||||
# 平仓后的已实现盈亏、手续费
|
||||
result: Mapped[dict | None] = mapped_column(JSONB)
|
||||
|
||||
|
||||
class TradingOrder(Base):
|
||||
"""订单流水:先写行再发单,clientOrderId / clientAlgoId 为 ap{id},超时后按它查询而不是换 id 重发"""
|
||||
|
||||
__tablename__ = "trading_orders"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
analysis_id: Mapped[int | None] = mapped_column(ForeignKey("trading_analyses.id"), index=True)
|
||||
symbol: Mapped[str] = mapped_column(String(32), index=True)
|
||||
# entry / tp / stop / close
|
||||
purpose: Mapped[str] = mapped_column(String(8))
|
||||
# 入场或止盈的档位序号,从 0 开始
|
||||
leg: Mapped[int | None] = mapped_column(Integer)
|
||||
# 条件单走 /fapi/v1/algoOrder
|
||||
is_algo: Mapped[bool] = mapped_column(Boolean)
|
||||
side: Mapped[str] = mapped_column(String(4))
|
||||
type: Mapped[str] = mapped_column(String(24))
|
||||
price: Mapped[Decimal | None] = mapped_column(Numeric)
|
||||
trigger_price: Mapped[Decimal | None] = mapped_column(Numeric)
|
||||
quantity: Mapped[Decimal] = mapped_column(Numeric)
|
||||
reduce_only: Mapped[bool] = mapped_column(Boolean)
|
||||
# submitting / new / partially_filled / filled / canceled / expired / rejected / finished / failed
|
||||
status: Mapped[str] = mapped_column(String(16), default="submitting")
|
||||
# orderId 或 algoId
|
||||
exchange_id: Mapped[str | None] = mapped_column(String(32))
|
||||
filled_qty: Mapped[Decimal] = mapped_column(Numeric, default=0)
|
||||
avg_price: Mapped[Decimal | None] = mapped_column(Numeric)
|
||||
error: Mapped[str | None] = mapped_column(Text)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
|
||||
class TradingEvent(Base):
|
||||
"""只追加的时间线"""
|
||||
|
||||
__tablename__ = "trading_events"
|
||||
__table_args__ = (Index("ix_trading_events_symbol_id", "symbol", "id"),)
|
||||
|
||||
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
# 账户级事件(启停等)为空
|
||||
symbol: Mapped[str | None] = mapped_column(String(32))
|
||||
analysis_id: Mapped[int | None] = mapped_column(ForeignKey("trading_analyses.id"))
|
||||
kind: Mapped[str] = mapped_column(String(32))
|
||||
# info / warn / error
|
||||
level: Mapped[str] = mapped_column(String(8), default="info")
|
||||
message: Mapped[str] = mapped_column(Text)
|
||||
data: Mapped[dict | None] = mapped_column(JSONB)
|
||||
Reference in new issue
Block a user