File size: 4,236 Bytes
7dfd114
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
"""yfinance fallback ์บ์‹œ ๋นŒ๋”.

๋กœ์ปฌ(yfinance ๊ฐ€ ์ •์ƒ ๋™์ž‘ํ•˜๋Š” ํ™˜๊ฒฝ)์—์„œ ์‹คํ–‰ํ•ด ๊ฐ ๋ผ์šฐํ„ฐ ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๊ณ ,
๊ทธ ๊ฒฐ๊ณผ JSON ์„ backend/cache/ ์— ์ €์žฅํ•œ๋‹ค. Hugging Face Spaces ์ฒ˜๋Ÿผ ๋ฐ์ดํ„ฐ์„ผํ„ฐ
IP ์—์„œ yfinance ๊ฐ€ 401 ๋กœ ์ฐจ๋‹จ๋˜๋Š” ํ™˜๊ฒฝ์—์„œ ์ด ์บ์‹œ๊ฐ€ fallback ์œผ๋กœ ์ œ๊ณต๋œ๋‹ค.

์‹คํ–‰:
    cd D:/OStock/backend
    .venv/Scripts/python.exe scripts/build_cache.py

์ฃผ์˜: news/prediction ๋ผ์šฐํ„ฐ๋Š” transformers->torch ๋ฅผ ๋Œ์–ด์™€ import ์ˆœ์„œ ์ด์Šˆ๊ฐ€
์žˆ์œผ๋ฏ€๋กœ, ์—ฌ๊ธฐ์„œ๋Š” yfinance ๊ธฐ๋ฐ˜ ๋ผ์šฐํ„ฐ(market/quotes/financial/technical)๋งŒ import ํ•œ๋‹ค.
"""

import os
import sys
import json
import datetime

# scripts/ ์—์„œ ์‹คํ–‰ํ•ด๋„ backend/ ๋ชจ๋“ˆ์„ import ํ•  ์ˆ˜ ์žˆ๋„๋ก backend ๊ฒฝ๋กœ ์ถ”๊ฐ€
BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, BACKEND_DIR)

from config import SECTOR_TICKERS  # noqa: E402
from routers import market, quotes, financial, technical  # noqa: E402

CACHE_DIR = os.path.join(BACKEND_DIR, "cache")


def _save(relpath, data):
    path = os.path.join(CACHE_DIR, relpath)
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)


def _is_error(result):
    """์—”๋“œํฌ์ธํŠธ ๊ฒฐ๊ณผ๊ฐ€ ์—๋Ÿฌ/๋นˆ๊ฐ’์ด๋ฉด True (์ €์žฅ ์Šคํ‚ต + ์‹คํŒจ ์นด์šดํŠธ์šฉ)."""
    if result is None:
        return True
    if isinstance(result, dict):
        if "error" in result:
            return True
        vals = list(result.values())
        # sectors/bond ์ฒ˜๋Ÿผ ๋ชจ๋“  ํ•˜์œ„ ํ•ญ๋ชฉ์ด ์—๋Ÿฌ dict ์ธ ๊ฒฝ์šฐ
        if vals and all(isinstance(v, dict) and "error" in v for v in vals):
            return True
        return False
    if isinstance(result, list):
        return len(result) == 0
    return False


def main():
    os.makedirs(CACHE_DIR, exist_ok=True)
    stats = {"ok": 0, "fail": 0, "failed": []}

    def run(label, relpath, fn):
        try:
            result = fn()
        except Exception as e:  # noqa: BLE001
            result = {"error": str(e)}
        if _is_error(result):
            stats["fail"] += 1
            stats["failed"].append(label)
            print(f"  [FAIL] {label}")
        else:
            _save(relpath, result)
            stats["ok"] += 1
            print(f"  [ OK ] {label}")

    print("== ๋ฌดํŒŒ๋ผ๋ฏธํ„ฐ ์—”๋“œํฌ์ธํŠธ ==")
    run("indices", "indices.json", market.get_market_indices)
    run("sectors", "sectors.json", market.get_sector_data)
    run("get_bond_data", "get_bond_data.json", market.get_bond_data)

    print(f"== ์ข…๋ชฉ๋ณ„ ์—”๋“œํฌ์ธํŠธ ({len(SECTOR_TICKERS)} ์ข…๋ชฉ x 4) ==")
    for _name, tk in SECTOR_TICKERS.items():
        run(f"stocks/{tk}", f"stocks/{tk}.json",
            lambda tk=tk: quotes.get_stock_data(ticker=tk))
        # period/interval ์„ ๋ช…์‹œ ์ „๋‹ฌํ•ด์•ผ ํ•œ๋‹ค. ํ•จ์ˆ˜๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•˜๋ฉด FastAPI ๊ฐ€
        # ์ฃผ์ž…ํ•˜์ง€ ์•Š์•„ ๊ธฐ๋ณธ๊ฐ’์ด Query(...) ๊ฐ์ฒด๋กœ ๋‚จ์•„ .lower() ์—์„œ ๊นจ์ง„๋‹ค.
        run(f"stock_chart/{tk}", f"stock_chart/{tk}.json",
            lambda tk=tk: quotes.get_stock_chart(ticker=tk, period="1mo", interval="1d"))
        run(f"financial_info/{tk}", f"financial_info/{tk}.json",
            lambda tk=tk: financial.get_financial_info(ticker=tk))
        run(f"technical_info/{tk}", f"technical_info/{tk}.json",
            lambda tk=tk: technical.get_technical_info(ticker=tk))

    meta = {
        "generated_at": datetime.datetime.now().astimezone().isoformat(timespec="seconds"),
        "endpoints": [
            "indices", "sectors", "get_bond_data",
            "stocks", "stock_chart", "financial_info", "technical_info",
        ],
        "ticker_count": len(SECTOR_TICKERS),
        "success": stats["ok"],
        "failed": stats["fail"],
    }
    with open(os.path.join(CACHE_DIR, ".meta.json"), "w", encoding="utf-8") as f:
        json.dump(meta, f, ensure_ascii=False, indent=2)

    print(f"\n์™„๋ฃŒ: ์„ฑ๊ณต {stats['ok']} / ์‹คํŒจ {stats['fail']}")
    if stats["failed"]:
        print("์‹คํŒจ ํ•ญ๋ชฉ:", ", ".join(stats["failed"]))
    print("์ƒ์„ฑ ์‹œ๊ฐ:", meta["generated_at"])


if __name__ == "__main__":
    main()