From 833491b8d09822d6ffed5dcc0554979177837a44 Mon Sep 17 00:00:00 2001 From: fish Date: Tue, 28 Jul 2026 09:49:37 +0800 Subject: [PATCH] =?UTF-8?q?=E5=93=81=E7=A7=8D=E5=A2=9E=E5=8A=A0=E6=AF=8F?= =?UTF-8?q?=E7=82=B9=E4=BB=B7=E5=80=BC=E5=AD=97=E6=AE=B5=EF=BC=8CPNL?= =?UTF-8?q?=E8=AE=A1=E7=AE=97=E6=94=AF=E6=8C=81=E4=B8=8D=E5=90=8C=E5=93=81?= =?UTF-8?q?=E7=A7=8D=E5=B7=AE=E5=BC=82=E5=8C=96=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.7 --- ft-app/app/models.py | 7 +++++-- ft-app/app/routers/admin.py | 4 +++- ft-app/app/routers/option_trades.py | 15 +++++++++++++-- ft-app/app/routers/trades.py | 15 +++++++++++++-- ft-app/app/seed.py | 16 ++++++++++++++++ ft-app/app/templates/admin.html | 7 ++++++- ft-app/app/templates/options.html | 10 +++++----- ft-app/app/templates/trades.html | 10 +++++----- 8 files changed, 66 insertions(+), 18 deletions(-) diff --git a/ft-app/app/models.py b/ft-app/app/models.py index 41e021f..92b0492 100644 --- a/ft-app/app/models.py +++ b/ft-app/app/models.py @@ -13,6 +13,7 @@ class Product(Base): 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") + point_value: Mapped[int] = mapped_column(Integer, default=20) contracts: Mapped[list["Contract"]] = relationship( back_populates="product", cascade="all, delete-orphan" @@ -137,12 +138,13 @@ class Trade(Base): close_price: Mapped[float | None] = mapped_column(Float, nullable=True) close_fee: Mapped[float | None] = mapped_column(Float, nullable=True, default=0) status: Mapped[str] = mapped_column(String(10), default="open") + point_value: Mapped[int] = mapped_column(Integer, default=20) @property def pnl(self) -> float | None: if self.close_price is None: return None - mul = 20 # glass futures point value + mul = self.point_value if self.direction == "long": result = (self.close_price - self.open_price) * mul - (self.open_fee or 0) - (self.close_fee or 0) else: @@ -166,12 +168,13 @@ class OptionTrade(Base): close_price: Mapped[float | None] = mapped_column(Float, nullable=True) close_fee: Mapped[float | None] = mapped_column(Float, nullable=True, default=0) status: Mapped[str] = mapped_column(String(10), default="open") + point_value: Mapped[int] = mapped_column(Integer, default=20) @property def pnl(self) -> float | None: if self.close_price is None: return None - mul = 20 # glass futures point value + mul = self.point_value if self.direction == "sell": result = (self.open_price - self.close_price) * mul - (self.open_fee or 0) - (self.close_fee or 0) else: diff --git a/ft-app/app/routers/admin.py b/ft-app/app/routers/admin.py index dac777e..570a28c 100644 --- a/ft-app/app/routers/admin.py +++ b/ft-app/app/routers/admin.py @@ -48,6 +48,7 @@ def admin_page(request: Request, db: Session = Depends(get_db)): "code": p.code, "name": p.name, "exchange": p.exchange, + "point_value": p.point_value, "contracts": contract_list, }) @@ -71,11 +72,12 @@ def create_product( code: str = Form(...), name: str = Form(...), exchange: str = Form(...), + point_value: int = Form(20), db: Session = Depends(get_db), ): existing = db.query(Product).filter(Product.code == code.upper()).first() if not existing: - p = Product(code=code.upper(), name=name, exchange=exchange) + p = Product(code=code.upper(), name=name, exchange=exchange, point_value=point_value) db.add(p) db.commit() return RedirectResponse("/admin/?tab=product", status_code=303) diff --git a/ft-app/app/routers/option_trades.py b/ft-app/app/routers/option_trades.py index c23d53b..aef3c8f 100644 --- a/ft-app/app/routers/option_trades.py +++ b/ft-app/app/routers/option_trades.py @@ -67,7 +67,12 @@ def open_trade( ): code = contract_code.upper() contract = db.query(Contract).filter(Contract.code == code).first() - product_code = contract.product.code if contract else code[:2] + if contract: + product = contract.product + else: + product = db.query(Product).filter(Product.code == code[:2]).first() + product_code = product.code if product else code[:2] + point_value = product.point_value if product else 20 t = OptionTrade( product_code=product_code, @@ -78,6 +83,7 @@ def open_trade( open_date=date.fromisoformat(open_date), open_price=open_price, open_fee=open_fee, + point_value=point_value, status="open", ) db.add(t) @@ -124,7 +130,12 @@ def edit_trade( if t: code = contract_code.upper() contract = db.query(Contract).filter(Contract.code == code).first() - t.product_code = contract.product.code if contract else code[:2] + if contract: + product = contract.product + else: + product = db.query(Product).filter(Product.code == code[:2]).first() + t.product_code = product.code if product else code[:2] + t.point_value = product.point_value if product else 20 t.contract_code = code t.option_type = option_type t.direction = direction diff --git a/ft-app/app/routers/trades.py b/ft-app/app/routers/trades.py index d07ad7a..b075faa 100644 --- a/ft-app/app/routers/trades.py +++ b/ft-app/app/routers/trades.py @@ -74,7 +74,12 @@ def open_trade( ): code = contract_code.upper() contract = db.query(Contract).filter(Contract.code == code).first() - product_code = contract.product.code if contract else code[:2] + if contract: + product = contract.product + else: + product = db.query(Product).filter(Product.code == code[:2]).first() + product_code = product.code if product else code[:2] + point_value = product.point_value if product else 20 t = Trade( product_code=product_code, @@ -83,6 +88,7 @@ def open_trade( open_date=date.fromisoformat(open_date), open_price=open_price, open_fee=open_fee, + point_value=point_value, status="open", ) db.add(t) @@ -127,7 +133,12 @@ def edit_trade( if t: code = contract_code.upper() contract = db.query(Contract).filter(Contract.code == code).first() - t.product_code = contract.product.code if contract else code[:2] + if contract: + product = contract.product + else: + product = db.query(Product).filter(Product.code == code[:2]).first() + t.product_code = product.code if product else code[:2] + t.point_value = product.point_value if product else 20 t.contract_code = code t.direction = direction t.open_date = date.fromisoformat(open_date) diff --git a/ft-app/app/seed.py b/ft-app/app/seed.py index 03108cf..642f039 100644 --- a/ft-app/app/seed.py +++ b/ft-app/app/seed.py @@ -37,6 +37,22 @@ def _migrate(engine): cols = {row[1] for row in cur.fetchall()} if "max_locks" not in cols: cur.execute("ALTER TABLE rounds ADD COLUMN max_locks INTEGER DEFAULT 3") + + cur.execute("PRAGMA table_info(products)") + cols = {row[1] for row in cur.fetchall()} + if "point_value" not in cols: + cur.execute("ALTER TABLE products ADD COLUMN point_value INTEGER DEFAULT 20") + + cur.execute("PRAGMA table_info(trades)") + cols = {row[1] for row in cur.fetchall()} + if "point_value" not in cols: + cur.execute("ALTER TABLE trades ADD COLUMN point_value INTEGER DEFAULT 20") + + cur.execute("PRAGMA table_info(option_trades)") + cols = {row[1] for row in cur.fetchall()} + if "point_value" not in cols: + cur.execute("ALTER TABLE option_trades ADD COLUMN point_value INTEGER DEFAULT 20") + conn.commit() finally: conn.close() diff --git a/ft-app/app/templates/admin.html b/ft-app/app/templates/admin.html index 682d4ba..254ab23 100644 --- a/ft-app/app/templates/admin.html +++ b/ft-app/app/templates/admin.html @@ -105,6 +105,10 @@ {% endfor %} +
+ + +
@@ -112,12 +116,13 @@ {% if products %}
- + {% for p in products %} +
代码名称交易所合约数操作
代码名称交易所每点合约数操作
{{ p.code }} {{ p.name }} {{ p.exchange }}{{ p.point_value }} {{ p.contracts|length }}
diff --git a/ft-app/app/templates/options.html b/ft-app/app/templates/options.html index fb6d8db..d9e03e0 100644 --- a/ft-app/app/templates/options.html +++ b/ft-app/app/templates/options.html @@ -198,7 +198,7 @@ {% if p > 0 %}+{% endif %}{{ p }} + onclick="showPnlDrawer('{{ t.product_code }} {{ t.contract_code.replace(t.product_code, '', 1) }} {{ t.option_type }}', '{{ t.direction }}', {{ t.open_price }}, {{ t.close_price }}, {{ t.open_fee or 0 }}, {{ t.close_fee or 0 }}, {{ p }}, {{ t.point_value }})" title="计算过程">ⓘ {% endif %}
@@ -333,16 +333,16 @@ function hideCloseForm() {