feat(stock): holdings intel API (intel/history/run)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import json
|
|||||||
import logging
|
import logging
|
||||||
from datetime import date as date_type
|
from datetime import date as date_type
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from fastapi import FastAPI, Query, Header, Depends, HTTPException
|
from fastapi import FastAPI, Query, Header, Depends, HTTPException, BackgroundTasks
|
||||||
from fastapi.responses import JSONResponse
|
from fastapi.responses import JSONResponse
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
import requests
|
import requests
|
||||||
@@ -27,6 +27,7 @@ from .price_fetcher import get_current_prices, get_current_prices_detail
|
|||||||
from .ai_summarizer import summarize_news, OllamaError
|
from .ai_summarizer import summarize_news, OllamaError
|
||||||
from .auth import verify_webai_key
|
from .auth import verify_webai_key
|
||||||
from . import webai_cache
|
from . import webai_cache
|
||||||
|
from . import holdings_intel
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
install_access_log(app)
|
install_access_log(app)
|
||||||
@@ -652,5 +653,25 @@ def remove_sell_history(record_id: int):
|
|||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
# --- Holdings Intelligence API ---
|
||||||
|
|
||||||
|
@app.get("/api/stock/holdings/intel")
|
||||||
|
def holdings_intel_brief():
|
||||||
|
"""보유종목 인텔리전스 브리핑 (최신 시그널 + 포트 건강)"""
|
||||||
|
return holdings_intel.build_holdings_brief()
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/stock/holdings/intel/history")
|
||||||
|
def holdings_intel_history(ticker: str, days: int = 30):
|
||||||
|
"""종목별 시그널 이력 조회"""
|
||||||
|
from . import db
|
||||||
|
return {"ticker": ticker, "history": db.get_holdings_signal_history(ticker, days)}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/api/stock/holdings/intel/run")
|
||||||
|
def holdings_intel_run(background_tasks: BackgroundTasks, use_llm: bool = True):
|
||||||
|
"""보유종목 시그널 계산 트리거 (BackgroundTask)"""
|
||||||
|
background_tasks.add_task(holdings_intel.compute_and_store, None, use_llm)
|
||||||
|
return {"ok": True, "queued": True}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
42
stock/app/test_holdings_api.py
Normal file
42
stock/app/test_holdings_api.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
|
||||||
|
def _client(monkeypatch):
|
||||||
|
# Add web-backend root to sys.path so _shared can be imported by main.py
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||||
|
from app import db
|
||||||
|
monkeypatch.setattr(db, "DB_PATH", os.path.join(tempfile.mkdtemp(), "stock.db"))
|
||||||
|
db.init_db()
|
||||||
|
from app.main import app
|
||||||
|
return TestClient(app)
|
||||||
|
|
||||||
|
|
||||||
|
def test_holdings_intel_endpoint(monkeypatch):
|
||||||
|
client = _client(monkeypatch)
|
||||||
|
r = client.get("/api/stock/holdings/intel")
|
||||||
|
assert r.status_code == 200
|
||||||
|
body = r.json()
|
||||||
|
assert "holdings" in body and "portfolio_health" in body
|
||||||
|
|
||||||
|
|
||||||
|
def test_holdings_intel_history_endpoint(monkeypatch):
|
||||||
|
client = _client(monkeypatch)
|
||||||
|
r = client.get("/api/stock/holdings/intel/history?ticker=005930")
|
||||||
|
assert r.status_code == 200
|
||||||
|
body = r.json()
|
||||||
|
assert body["ticker"] == "005930"
|
||||||
|
assert "history" in body
|
||||||
|
assert isinstance(body["history"], list)
|
||||||
|
|
||||||
|
|
||||||
|
def test_holdings_intel_run_endpoint(monkeypatch):
|
||||||
|
client = _client(monkeypatch)
|
||||||
|
r = client.post("/api/stock/holdings/intel/run")
|
||||||
|
assert r.status_code == 200
|
||||||
|
body = r.json()
|
||||||
|
assert body["ok"] is True
|
||||||
|
assert body["queued"] is True
|
||||||
Reference in New Issue
Block a user