diff --git a/backend/app/api/endpoints/recommendations.py b/backend/app/api/endpoints/recommendations.py index 7c10f69..57c7f85 100644 --- a/backend/app/api/endpoints/recommendations.py +++ b/backend/app/api/endpoints/recommendations.py @@ -605,11 +605,22 @@ async def execute_auto_trading(db: AsyncSession = Depends(get_db)): total_processed = 0 max_trades = 5 - for rule_group in recommendations_response["recommendations_by_rule"]: + # make sure we iterate a list of rule group dicts + rec_groups = recommendations_response.get("recommendations_by_rule", []) + if isinstance(rec_groups, dict): + normalized = [] + for name, grp in rec_groups.items(): + if isinstance(grp, dict): + normalized.append(grp) + else: + normalized.append({"rule_name": name, "stocks": grp}) + rec_groups = normalized + + for rule_group in rec_groups: if total_processed >= max_trades: break - for stock in rule_group["stocks"][:settings.AUTO_TRADE_TOP_N_PER_RULE]: + for stock in rule_group.get("stocks", [])[:settings.AUTO_TRADE_TOP_N_PER_RULE]: if total_processed >= max_trades: break diff --git a/backend/app/models/models.py b/backend/app/models/models.py index 904b19c..0d99835 100644 --- a/backend/app/models/models.py +++ b/backend/app/models/models.py @@ -128,7 +128,8 @@ class MarketData(Base): stock_code = Column(String(20), nullable=False, index=True) price = Column(Numeric(15, 2), nullable=False) change_rate = Column(Numeric(10, 4)) - volume = Column(Integer) + # real‑time volume may also exceed int32; switch to bigint for safety + volume = Column(BigInteger) high_price = Column(Numeric(15, 2)) low_price = Column(Numeric(15, 2)) open_price = Column(Numeric(15, 2)) @@ -190,7 +191,8 @@ class StockAnalysis(Base): # 가격 정보 current_price = Column(Integer, nullable=False) change_rate = Column(Numeric(10, 4)) - volume = Column(Integer) + # volumes can exceed 32‑bit limits (e.g. 3 billion+), use bigint to avoid overflow + volume = Column(BigInteger) # 기술적 지표 rsi = Column(Numeric(10, 4)) diff --git a/backend/app/services/trade_executor.py b/backend/app/services/trade_executor.py index bde0b06..bf2a004 100644 --- a/backend/app/services/trade_executor.py +++ b/backend/app/services/trade_executor.py @@ -98,8 +98,20 @@ async def execute_auto_trade_background(recommendations_response: dict, db: Asyn result = await db.execute(select(Portfolio).where(Portfolio.quantity > 0)) held_stocks = {p.stock_code for p in result.scalars().all()} + # normalize recommendations_by_rule early so we iterate over a list of groups + rec_groups = recommendations_response.get("recommendations_by_rule", []) + if isinstance(rec_groups, dict): + # older format: {rule_name: [stocks]} + normalized_list = [] + for name, group in rec_groups.items(): + if isinstance(group, dict): + normalized_list.append(group) + else: + normalized_list.append({"rule_name": name, "stocks": group}) + rec_groups = normalized_list + unique_stocks = {} - for rule_group in recommendations_response.get("recommendations_by_rule", []): + for rule_group in rec_groups: # make sure we have a dict with expected keys if not isinstance(rule_group, dict): logger.warning("Invalid rule_group in recommendations_response: %r", rule_group) diff --git a/tests/test_volume_overflow.py b/tests/test_volume_overflow.py new file mode 100644 index 0000000..dc6fd43 --- /dev/null +++ b/tests/test_volume_overflow.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +""" +Verify that StockAnalysis and MarketData volume columns can handle values larger +than 2**31-1. This script will fail prior to applying the bigint migration. +""" +import asyncio +from datetime import date + +from app.core.database import AsyncSessionLocal +from app.models.models import StockAnalysis, MarketData + + +async def test_big_volume(): + async with AsyncSessionLocal() as session: + # create and commit a record with a large volume value + huge_vol = 2962426126 # value that previously triggered overflow + + sa = StockAnalysis( + stock_code="TEST", + stock_name="Test Corp", + analysis_date=date.today(), + current_price=100, + change_rate=0, + volume=huge_vol, + ) + session.add(sa) + + # also test MarketData + md = MarketData( + stock_code="TEST", + price=100, + change_rate=0, + volume=huge_vol, + ) + session.add(md) + + await session.commit() + + print("Inserted records with large volume successfully") + + # fetch back and verify + fetched_sa = await session.get(StockAnalysis, sa.id) + fetched_md = await session.get(MarketData, md.id) + print("StockAnalysis volume type:", type(fetched_sa.volume), fetched_sa.volume) + print("MarketData volume type:", type(fetched_md.volume), fetched_md.volume) + + assert fetched_sa.volume == huge_vol + assert fetched_md.volume == huge_vol + + +if __name__ == "__main__": + asyncio.run(test_big_volume())