2065122777
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
122 lines
4.4 KiB
Python
122 lines
4.4 KiB
Python
import hashlib
|
|
import os
|
|
from datetime import date
|
|
from sqlalchemy import String, Integer, Float, Date, ForeignKey, Boolean, UniqueConstraint
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
from app.database import Base
|
|
|
|
|
|
class Product(Base):
|
|
__tablename__ = "products"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
code: Mapped[str] = mapped_column(String(10), unique=True, index=True)
|
|
name: Mapped[str] = mapped_column(String(20))
|
|
exchange: Mapped[str] = mapped_column(String(10), default="CZCE")
|
|
|
|
contracts: Mapped[list["Contract"]] = relationship(
|
|
back_populates="product", cascade="all, delete-orphan"
|
|
)
|
|
|
|
|
|
class Contract(Base):
|
|
__tablename__ = "contracts"
|
|
__table_args__ = (UniqueConstraint("code"),)
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
product_id: Mapped[int] = mapped_column(ForeignKey("products.id"), index=True)
|
|
code: Mapped[str] = mapped_column(String(10), unique=True, index=True)
|
|
name: Mapped[str] = mapped_column(String(30))
|
|
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
|
|
|
|
product: Mapped["Product"] = relationship(back_populates="contracts")
|
|
|
|
|
|
class DailyBar(Base):
|
|
__tablename__ = "daily_bars"
|
|
__table_args__ = (UniqueConstraint("contract", "date"),)
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
contract: Mapped[str] = mapped_column(String(10), index=True)
|
|
date: Mapped[date] = mapped_column(Date, index=True)
|
|
open: Mapped[float] = mapped_column(Float)
|
|
close: Mapped[float] = mapped_column(Float)
|
|
high: Mapped[float] = mapped_column(Float)
|
|
low: Mapped[float] = mapped_column(Float)
|
|
amp_5d: Mapped[float | None] = mapped_column(Float, nullable=True)
|
|
|
|
@property
|
|
def diff(self) -> float:
|
|
"""最高-最低价差"""
|
|
return self.high - self.low
|
|
|
|
|
|
class PositionRanking(Base):
|
|
__tablename__ = "position_rankings"
|
|
__table_args__ = (UniqueConstraint("contract_code", "institution", "data_type", "date"),)
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
contract_code: Mapped[str] = mapped_column(String(10), index=True)
|
|
institution: Mapped[str] = mapped_column(String(30), index=True)
|
|
data_type: Mapped[str] = mapped_column(String(10), index=True)
|
|
date: Mapped[date] = mapped_column(Date, index=True)
|
|
rank: Mapped[int] = mapped_column(Integer)
|
|
value: Mapped[int] = mapped_column(Integer)
|
|
change: Mapped[int] = mapped_column(Integer)
|
|
|
|
|
|
class User(Base):
|
|
__tablename__ = "users"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
username: Mapped[str] = mapped_column(String(50), unique=True)
|
|
password_hash: Mapped[str] = mapped_column(String(128))
|
|
role: Mapped[str] = mapped_column(String(20), default="user")
|
|
|
|
@staticmethod
|
|
def hash_password(password: str) -> str:
|
|
salt = os.urandom(16)
|
|
dk = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 100000)
|
|
return salt.hex() + ":" + dk.hex()
|
|
|
|
def check_password(self, password: str) -> bool:
|
|
try:
|
|
salt_hex, dk_hex = self.password_hash.split(":")
|
|
salt = bytes.fromhex(salt_hex)
|
|
dk = hashlib.pbkdf2_hmac("sha256", password.encode(), salt, 100000)
|
|
return dk.hex() == dk_hex
|
|
except (ValueError, AttributeError):
|
|
return False
|
|
|
|
|
|
class Round(Base):
|
|
__tablename__ = "rounds"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
contract_code: Mapped[str] = mapped_column(String(10), index=True)
|
|
daily_hands: Mapped[int] = mapped_column(Integer, default=1)
|
|
status: Mapped[str] = mapped_column(String(20), default="active")
|
|
started_at: Mapped[date] = mapped_column(Date)
|
|
|
|
positions: Mapped[list["Position"]] = relationship(
|
|
back_populates="round", cascade="all, delete-orphan"
|
|
)
|
|
|
|
@property
|
|
def total_locks(self) -> int:
|
|
return sum(p.locked_count for p in self.positions)
|
|
|
|
|
|
class Position(Base):
|
|
__tablename__ = "positions"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
round_id: Mapped[int] = mapped_column(ForeignKey("rounds.id"), index=True)
|
|
open_price: Mapped[int] = mapped_column(Integer)
|
|
amp_threshold: Mapped[int] = mapped_column(Integer)
|
|
lock_price: Mapped[int] = mapped_column(Integer)
|
|
hands: Mapped[int] = mapped_column(Integer, default=1)
|
|
locked_count: Mapped[int] = mapped_column(Integer, default=0)
|
|
opened_at: Mapped[date] = mapped_column(Date)
|
|
|
|
round: Mapped["Round"] = relationship(back_populates="positions") |