Crypto trading bot: engine, brokers, backtester, dashboard, deploy
FastAPI backend with a single-strategy async engine, paper and Binance brokers, pure-python indicators, a backtester that shares the engine's portfolio math, and a Vue dashboard with a live WebSocket feed. Adds the Gitea Actions CI/CD workflow that deploys to Traefik via docker_default.
This commit is contained in:
1 parent
98028248c2
commit
9c3bee87ac
73 files changed
+9759
-1
No files matched your search
@@ -0,0 +1,121 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from enum import StrEnum
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import (
|
||||
JSON,
|
||||
DateTime,
|
||||
Float,
|
||||
ForeignKey,
|
||||
Integer,
|
||||
String,
|
||||
)
|
||||
from sqlalchemy import (
|
||||
Enum as SAEnum,
|
||||
)
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
|
||||
|
||||
|
||||
def utcnow() -> datetime:
|
||||
return datetime.now(UTC)
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
pass
|
||||
|
||||
|
||||
class PositionSide(StrEnum):
|
||||
LONG = "long"
|
||||
SHORT = "short"
|
||||
|
||||
|
||||
class PositionStatus(StrEnum):
|
||||
OPEN = "open"
|
||||
CLOSED = "closed"
|
||||
|
||||
|
||||
class OrderSide(StrEnum):
|
||||
BUY = "buy"
|
||||
SELL = "sell"
|
||||
|
||||
|
||||
class StrategyConfig(Base):
|
||||
__tablename__ = "strategy_configs"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
strategy_key: Mapped[str] = mapped_column(String(64), index=True)
|
||||
symbol: Mapped[str] = mapped_column(String(32))
|
||||
timeframe: Mapped[str] = mapped_column(String(16))
|
||||
params: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
|
||||
|
||||
|
||||
class Position(Base):
|
||||
__tablename__ = "positions"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
strategy_key: Mapped[str] = mapped_column(String(64), index=True)
|
||||
symbol: Mapped[str] = mapped_column(String(32), index=True)
|
||||
side: Mapped[PositionSide] = mapped_column(SAEnum(PositionSide))
|
||||
qty: Mapped[float] = mapped_column(Float)
|
||||
entry_price: Mapped[float] = mapped_column(Float)
|
||||
status: Mapped[PositionStatus] = mapped_column(
|
||||
SAEnum(PositionStatus), default=PositionStatus.OPEN, index=True
|
||||
)
|
||||
stop_loss: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
take_profit: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
entry_fee: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
exit_price: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
exit_fee: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
realized_pnl: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
opened_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
|
||||
closed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
orders: Mapped[list[Order]] = relationship(back_populates="position")
|
||||
|
||||
|
||||
class Order(Base):
|
||||
__tablename__ = "orders"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
position_id: Mapped[int | None] = mapped_column(
|
||||
ForeignKey("positions.id"), nullable=True, index=True
|
||||
)
|
||||
strategy_key: Mapped[str] = mapped_column(String(64), index=True)
|
||||
symbol: Mapped[str] = mapped_column(String(32))
|
||||
side: Mapped[OrderSide] = mapped_column(SAEnum(OrderSide))
|
||||
qty: Mapped[float] = mapped_column(Float)
|
||||
price: Mapped[float] = mapped_column(Float)
|
||||
fee: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
reason: Mapped[str] = mapped_column(String(255), default="")
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=utcnow, index=True
|
||||
)
|
||||
|
||||
position: Mapped[Position | None] = relationship(back_populates="orders")
|
||||
|
||||
|
||||
class EquitySnapshot(Base):
|
||||
__tablename__ = "equity_snapshots"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
equity: Mapped[float] = mapped_column(Float)
|
||||
cash: Mapped[float] = mapped_column(Float)
|
||||
price: Mapped[float] = mapped_column(Float)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=utcnow, index=True
|
||||
)
|
||||
|
||||
|
||||
class Event(Base):
|
||||
__tablename__ = "events"
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True)
|
||||
level: Mapped[str] = mapped_column(String(16), default="info")
|
||||
message: Mapped[str] = mapped_column(String(512))
|
||||
strategy_key: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), default=utcnow, index=True
|
||||
)
|
||||
Reference in new issue
Block a user