From a02d23de8db3acf9275627b658760e8af28e9d8d Mon Sep 17 00:00:00 2001 From: kyu Date: Wed, 20 May 2026 22:09:32 +0900 Subject: [PATCH] =?UTF-8?q?=EB=8C=80=EC=8B=9C=EB=B3=B4=EB=93=9C=204?= =?UTF-8?q?=EA=B0=9C=20=ED=83=AD=20=EC=B6=94=EA=B0=80:=2010=EA=B3=B5?= =?UTF-8?q?=EC=8B=9D=20=EB=A7=A4=ED=8A=B8=EB=A6=AD=EC=8A=A4=20/=20?= =?UTF-8?q?=EB=B0=B1=ED=85=8C=EC=8A=A4=ED=8A=B8=20/=20=EB=AF=B8=EC=A6=9D?= =?UTF-8?q?=EC=8B=9C=20=EB=8F=99=EC=A1=B0=20/=20=EB=A7=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=9C=C2=B7=EC=84=B9=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dashboard-api/main.py: - SCORE_ENGINE_URL/US_MARKET_URL/AUX_SIGNAL_URL 환경변수 - _proxy_get 헬퍼 - 신규 8개 엔드포인트: /api/formulas/matrix - 10공식 신호 매트릭스 (stock_scores 직접 조회) /api/weights - 학습된 공식 가중치 proxy /api/backtest - 추천 백테스트 proxy /api/sector-concentration - 섹터 집중도 + 30% 경고 proxy /api/usmarket/etfs - 미국 섹터 ETF /api/usmarket/pairs - KR↔US 페어 + 60일 회귀 베타 /api/usmarket/signals - 동조 시그널 /api/macro/ecos - 한국 매크로 (USD/KRW, 국고채) dashboard-api/index.html: - 탭 4개 추가 (formulas/backtest/usmarket/macro) - renderFormulas: 매트릭스 테이블 (한글 매수/매도 신호 색상 + 툴팁) - renderBacktest: 7d/30d 카드 + 등급별 표 + 기간 슬라이드 - renderUsMarket: ETF + 페어 + 시그널 3패널 - renderMacro: KOSPI/USD-KRW/국고채 카드 + 섹터 집중도 막대 + 가중치 Co-Authored-By: Claude Opus 4.7 (1M context) --- dashboard-api/index.html | 298 ++++++++++++++++++++++++++++++++++++++- dashboard-api/main.py | 84 ++++++++++- 2 files changed, 377 insertions(+), 5 deletions(-) diff --git a/dashboard-api/index.html b/dashboard-api/index.html index b82213c..356bcb6 100644 --- a/dashboard-api/index.html +++ b/dashboard-api/index.html @@ -201,6 +201,10 @@ body:not(.auth-required) #login-screen { display:none !important; } + + + +
@@ -212,6 +216,10 @@ body:not(.auth-required) #login-screen { display:none !important; } + + + + diff --git a/dashboard-api/main.py b/dashboard-api/main.py index c6e9ab2..4034454 100644 --- a/dashboard-api/main.py +++ b/dashboard-api/main.py @@ -9,10 +9,13 @@ from fastapi.staticfiles import StaticFiles from pydantic import BaseModel, EmailStr, Field from auth import hash_password, verify_password, create_token, current_user, dummy_verify -TA_ENGINE_URL = os.getenv("TA_ENGINE_URL", "http://ta-engine:8484") -KIS_API_URL = os.getenv("KIS_API_URL", "http://kis-api:8585") -OLLAMA_URL = os.getenv("OLLAMA_URL", "http://ollama:11434") -CHAT_MODEL = os.getenv("CHAT_MODEL", "exaone3.5:7.8b") +TA_ENGINE_URL = os.getenv("TA_ENGINE_URL", "http://ta-engine:8484") +KIS_API_URL = os.getenv("KIS_API_URL", "http://kis-api:8585") +OLLAMA_URL = os.getenv("OLLAMA_URL", "http://ollama:11434") +SCORE_ENGINE_URL = os.getenv("SCORE_ENGINE_URL", "http://score-engine:8686") +US_MARKET_URL = os.getenv("US_MARKET_URL", "http://us-market:8383") +AUX_SIGNAL_URL = os.getenv("AUX_SIGNAL_URL", "http://aux-signal:8282") +CHAT_MODEL = os.getenv("CHAT_MODEL", "exaone3.5:7.8b") class PositionReq(BaseModel): code: str @@ -1994,6 +1997,79 @@ async def calendar_api(days: int = Query(default=30, ge=1, le=180)): "recent_dart": [dict(r) for r in recent][:30]} +async def _proxy_get(url: str, timeout: float = 10.0): + """외부 서비스 GET 프록시 (실패 시 빈 응답)""" + try: + async with httpx.AsyncClient(timeout=timeout) as c: + r = await c.get(url) + r.raise_for_status() + return r.json() + except Exception as e: + return {"error": str(e), "source": url} + + +@app.get("/api/formulas/matrix") +async def formulas_matrix(limit: int = Query(default=50, ge=5, le=200)): + """종목 × 10공식 신호 매트릭스 (오늘 스코어 기준 상위 N)""" + async with pg_pool.acquire() as c: + rows = await c.fetch(""" + SELECT s.stock_code, COALESCE(d.corp_name, s.stock_name) AS stock_name, + s.total_score, s.recommendation, s.buy_votes, s.sell_votes, + s.magic_score, s.f_score, s.altman_z, s.peg, s.momentum_pct, + s.beneish_score, s.gpa_pct, s.g_score, s.amihud_illiq, s.market_beta, + s.signals, s.sector + FROM stock_scores s + LEFT JOIN dart_corps d ON d.stock_code = s.stock_code + WHERE s.score_date = (SELECT MAX(score_date) FROM stock_scores) + AND COALESCE(d.is_active, true) = true + ORDER BY s.total_score DESC + LIMIT $1 + """, limit) + return [dict(r) for r in rows] + + +@app.get("/api/weights") +async def proxy_weights(): + """공식별 학습 가중치 (score-engine /learn-weights)""" + return await _proxy_get(f"{SCORE_ENGINE_URL}/learn-weights") + + +@app.get("/api/backtest") +async def proxy_backtest(days: int = Query(default=180, ge=30, le=720)): + """추천 백테스트 (score-engine /backtest)""" + return await _proxy_get(f"{SCORE_ENGINE_URL}/backtest?days={days}", timeout=30.0) + + +@app.get("/api/sector-concentration") +async def proxy_sector_concentration(): + """섹터 집중도 + 30% 초과 경고 (score-engine /sector/concentration)""" + return await _proxy_get(f"{SCORE_ENGINE_URL}/sector/concentration") + + +@app.get("/api/usmarket/etfs") +async def proxy_us_etfs(): + """미국 섹터 ETF 목록""" + return await _proxy_get(f"{US_MARKET_URL}/etfs") + + +@app.get("/api/usmarket/pairs") +async def proxy_us_pairs(): + """KR↔US 페어 + 60일 회귀 베타""" + return await _proxy_get(f"{US_MARKET_URL}/pairs") + + +@app.get("/api/usmarket/signals") +async def proxy_us_signals(): + """미증시 동조 시그널 (전체)""" + return await _proxy_get(f"{US_MARKET_URL}/signal/latest") + + +@app.get("/api/macro/ecos") +async def proxy_macro_ecos(): + """한국 매크로 (aux-signal /macro/latest: USD/KRW, 국고채 10년, KOSPI)""" + return await _proxy_get(f"{AUX_SIGNAL_URL}/macro/latest") + + @app.get("/") async def index(): return FileResponse("/app/index.html")