feat: generate transaction records from snapshot diffs in seed script
All checks were successful
Deploy to Production / deploy (push) Successful in 1m27s

Derive buy/sell transactions by comparing consecutive snapshots and
replace existing portfolio on re-run instead of skipping.
This commit is contained in:
ayuriel 2026-02-16 20:56:12 +09:00
parent 48417a03f5
commit a899c17a65

View File

@ -8,7 +8,7 @@ Requires: DATABASE_URL environment variable or default dev connection.
""" """
import sys import sys
import os import os
from datetime import date from datetime import date, datetime
from decimal import Decimal from decimal import Decimal
# Add backend to path # Add backend to path
@ -19,6 +19,7 @@ from app.core.database import SessionLocal
from app.models.portfolio import ( from app.models.portfolio import (
Portfolio, PortfolioType, Target, Holding, Portfolio, PortfolioType, Target, Holding,
PortfolioSnapshot, SnapshotHolding, PortfolioSnapshot, SnapshotHolding,
Transaction, TransactionType,
) )
from app.models.user import User from app.models.user import User
@ -131,6 +132,17 @@ SNAPSHOTS = [
{"ticker": "411060", "qty": 328, "price": Decimal("29605"), "value": Decimal("9710440")}, {"ticker": "411060", "qty": 328, "price": Decimal("29605"), "value": Decimal("9710440")},
], ],
}, },
{
"date": date(2026, 2, 16),
"total_assets": Decimal("62433665"),
"holdings": [
{"ticker": "069500", "qty": 16, "price": Decimal("81835"), "value": Decimal("1309360")},
{"ticker": "148070", "qty": 133, "price": Decimal("108290"), "value": Decimal("14402570")},
{"ticker": "284430", "qty": 1386, "price": Decimal("19250"), "value": Decimal("26680500")},
{"ticker": "360750", "qty": 385, "price": Decimal("24435"), "value": Decimal("9407475")},
{"ticker": "411060", "qty": 328, "price": Decimal("32420"), "value": Decimal("10633760")},
],
},
] ]
@ -142,14 +154,15 @@ def seed(db: Session):
print("ERROR: No user found in database. Create a user first.") print("ERROR: No user found in database. Create a user first.")
return return
# Check if portfolio already exists # Delete existing portfolio if present (cascade deletes related records)
existing = db.query(Portfolio).filter( existing = db.query(Portfolio).filter(
Portfolio.user_id == user.id, Portfolio.user_id == user.id,
Portfolio.name == "연금 포트폴리오", Portfolio.name == "연금 포트폴리오",
).first() ).first()
if existing: if existing:
print(f"Portfolio '연금 포트폴리오' already exists (id={existing.id}). Skipping.") db.delete(existing)
return db.flush()
print(f"Deleted existing portfolio (id={existing.id})")
# Create portfolio # Create portfolio
portfolio = Portfolio( portfolio = Portfolio(
@ -189,6 +202,45 @@ def seed(db: Session):
)) ))
print(f" Snapshot {snap['date']}: {len(snap['holdings'])} holdings") print(f" Snapshot {snap['date']}: {len(snap['holdings'])} holdings")
# Create transactions by comparing consecutive snapshots
tx_count = 0
for i, snap in enumerate(SNAPSHOTS):
current_holdings = {h["ticker"]: h for h in snap["holdings"]}
if i == 0:
# First snapshot: all holdings are initial buys
prev_holdings = {}
else:
prev_holdings = {h["ticker"]: h for h in SNAPSHOTS[i - 1]["holdings"]}
all_tickers = set(current_holdings.keys()) | set(prev_holdings.keys())
for ticker in all_tickers:
cur_qty = current_holdings[ticker]["qty"] if ticker in current_holdings else 0
prev_qty = prev_holdings[ticker]["qty"] if ticker in prev_holdings else 0
diff = cur_qty - prev_qty
if diff == 0:
continue
if diff > 0:
tx_type = TransactionType.BUY
price = current_holdings[ticker]["price"]
else:
tx_type = TransactionType.SELL
price = prev_holdings[ticker]["price"]
db.add(Transaction(
portfolio_id=portfolio.id,
ticker=ticker,
tx_type=tx_type,
quantity=abs(diff),
price=price,
executed_at=datetime.combine(snap["date"], datetime.min.time()),
))
tx_count += 1
print(f"Created {tx_count} transactions from snapshot diffs")
# Set current holdings from latest snapshot # Set current holdings from latest snapshot
latest = SNAPSHOTS[-1] latest = SNAPSHOTS[-1]
for h in latest["holdings"]: for h in latest["holdings"]: