챠트 데이터 웹소켓으로 연결
This commit is contained in:
@@ -605,11 +605,22 @@ async def execute_auto_trading(db: AsyncSession = Depends(get_db)):
|
|||||||
total_processed = 0
|
total_processed = 0
|
||||||
max_trades = 5
|
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:
|
if total_processed >= max_trades:
|
||||||
break
|
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:
|
if total_processed >= max_trades:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,8 @@ class MarketData(Base):
|
|||||||
stock_code = Column(String(20), nullable=False, index=True)
|
stock_code = Column(String(20), nullable=False, index=True)
|
||||||
price = Column(Numeric(15, 2), nullable=False)
|
price = Column(Numeric(15, 2), nullable=False)
|
||||||
change_rate = Column(Numeric(10, 4))
|
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))
|
high_price = Column(Numeric(15, 2))
|
||||||
low_price = Column(Numeric(15, 2))
|
low_price = Column(Numeric(15, 2))
|
||||||
open_price = Column(Numeric(15, 2))
|
open_price = Column(Numeric(15, 2))
|
||||||
@@ -190,7 +191,8 @@ class StockAnalysis(Base):
|
|||||||
# 가격 정보
|
# 가격 정보
|
||||||
current_price = Column(Integer, nullable=False)
|
current_price = Column(Integer, nullable=False)
|
||||||
change_rate = Column(Numeric(10, 4))
|
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))
|
rsi = Column(Numeric(10, 4))
|
||||||
|
|||||||
@@ -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))
|
result = await db.execute(select(Portfolio).where(Portfolio.quantity > 0))
|
||||||
held_stocks = {p.stock_code for p in result.scalars().all()}
|
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 = {}
|
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
|
# make sure we have a dict with expected keys
|
||||||
if not isinstance(rule_group, dict):
|
if not isinstance(rule_group, dict):
|
||||||
logger.warning("Invalid rule_group in recommendations_response: %r", rule_group)
|
logger.warning("Invalid rule_group in recommendations_response: %r", rule_group)
|
||||||
|
|||||||
52
tests/test_volume_overflow.py
Normal file
52
tests/test_volume_overflow.py
Normal file
@@ -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())
|
||||||
Reference in New Issue
Block a user