From a899c17a65456d39e61c2fc4e78af4db056ad55f Mon Sep 17 00:00:00 2001 From: ayuriel Date: Mon, 16 Feb 2026 20:56:12 +0900 Subject: [PATCH] feat: generate transaction records from snapshot diffs in seed script Derive buy/sell transactions by comparing consecutive snapshots and replace existing portfolio on re-run instead of skipping. --- backend/scripts/seed_data.py | 60 +++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/backend/scripts/seed_data.py b/backend/scripts/seed_data.py index 572a1bd..24a078b 100644 --- a/backend/scripts/seed_data.py +++ b/backend/scripts/seed_data.py @@ -8,7 +8,7 @@ Requires: DATABASE_URL environment variable or default dev connection. """ import sys import os -from datetime import date +from datetime import date, datetime from decimal import Decimal # Add backend to path @@ -19,6 +19,7 @@ from app.core.database import SessionLocal from app.models.portfolio import ( Portfolio, PortfolioType, Target, Holding, PortfolioSnapshot, SnapshotHolding, + Transaction, TransactionType, ) from app.models.user import User @@ -131,6 +132,17 @@ SNAPSHOTS = [ {"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.") return - # Check if portfolio already exists + # Delete existing portfolio if present (cascade deletes related records) existing = db.query(Portfolio).filter( Portfolio.user_id == user.id, Portfolio.name == "연금 포트폴리오", ).first() if existing: - print(f"Portfolio '연금 포트폴리오' already exists (id={existing.id}). Skipping.") - return + db.delete(existing) + db.flush() + print(f"Deleted existing portfolio (id={existing.id})") # Create portfolio portfolio = Portfolio( @@ -189,6 +202,45 @@ def seed(db: Session): )) 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 latest = SNAPSHOTS[-1] for h in latest["holdings"]: