auto: 일일 백업 2026-07-04 02:00

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
hyowons
2026-07-04 02:00:03 +09:00
parent dd1ad7d964
commit 008ce8be65
173 changed files with 4164 additions and 3641 deletions
@@ -171,6 +171,7 @@ def collect_snapshot(code: str, holding: dict | None = None) -> dict:
sma5 = sma(closes, 5)
sma20 = sma(closes, 20)
sma60 = sma(closes, 60)
sma120 = sma(closes, 120)
flow = kc.get_investor_flow(code, days=20)
flow_summary = _summarize_flow(flow)
@@ -191,6 +192,7 @@ def collect_snapshot(code: str, holding: dict | None = None) -> dict:
'sma5': sma5,
'sma20': sma20,
'sma60': sma60,
'sma120': sma120,
'flow': flow,
'flow_summary': flow_summary,
'holding': holding,
@@ -583,7 +585,7 @@ def remove_peer(stock_code: str, peer_code: str) -> bool:
# ============================================================================
CHART_W = 1000
CHART_H = 720 # crosshair 박스 영역(상단) + X축 날짜 라벨 영역 확보
CHART_H = 660 # X축 날짜 라벨 아래 여백 최소화 (라벨 baseline VOL_BOT+26=642)
CHART_PAD_L = 20 # 좌측 라벨 없음 (가격 라벨 우측으로 이동)
CHART_PAD_R = 140 # 우측 가격·거래량 라벨 영역 (28px 폰트 = 6자리+쉼표 약 140px)
CHART_PAD_T = 60 # SMA legend 영역 (y=4~50). OHLC 팝업은 차트 안 floating tooltip.
@@ -619,12 +621,13 @@ def render_svg_chart(snap: dict, range_key: str = '1Y', ref_price: float | None
sma5 = (snap.get('sma5') or [])[-n_take:]
sma20 = (snap.get('sma20') or [])[-n_take:]
sma60 = (snap.get('sma60') or [])[-n_take:]
sma120 = (snap.get('sma120') or [])[-n_take:]
# ---- 스케일 ----
highs = [c['high'] for c in candles]
lows = [c['low'] for c in candles]
# SMA 값도 가격축 범위에 포함 (선이 잘리지 않도록)
sma_vals = [v for v in (sma5 + sma20 + sma60) if v is not None]
sma_vals = [v for v in (sma5 + sma20 + sma60 + sma120) if v is not None]
y_max = max(max(highs), max(sma_vals) if sma_vals else 0)
y_min = min(min(lows), min(sma_vals) if sma_vals else float('inf'))
if ref_price:
@@ -757,6 +760,7 @@ def render_svg_chart(snap: dict, range_key: str = '1Y', ref_price: float | None
parts.append(sma_path(sma5, '#fbbf24', 'SMA5')) # 노랑
parts.append(sma_path(sma20, '#34d399', 'SMA20')) # 초록
parts.append(sma_path(sma60, '#a78bfa', 'SMA60')) # 보라
parts.append(sma_path(sma120, '#22d3ee', 'SMA120')) # 청록
# ---- 전일종가 기준선 + 대비 등락률 (ref_price 지정 시 — 분봉) ----
if ref_price:
@@ -772,16 +776,16 @@ def render_svg_chart(snap: dict, range_key: str = '1Y', ref_price: float | None
# SMA 범례
legend_y = PRICE_TOP + 14
legend_items = [('SMA5', '#fbbf24'), ('SMA20', '#34d399'), ('SMA60', '#a78bfa')]
legend_items = [('5일', '#fbbf24'), ('20일', '#34d399'), ('60일', '#a78bfa'), ('120일', '#22d3ee')]
lx = CHART_PAD_L + 8
for name, color in legend_items:
parts.append(
f'<rect x="{lx}" y="{legend_y-8}" width="10" height="3" fill="{color}"/>'
)
parts.append(
f'<text x="{lx+14}" y="{legend_y-2}" fill="#cfd5df">{name}</text>'
f'<text x="{lx+14}" y="{legend_y-2}" fill="#cfd5df" font-size="20">{name}</text>'
)
lx += 70
lx += 96
# ---- 거래량 패널 배경 ----
parts.append(
@@ -836,6 +840,38 @@ def render_svg_chart(snap: dict, range_key: str = '1Y', ref_price: float | None
f'{_fmt_date(candles[i]["date"])}</text>'
)
# ---- 구간 고점선 + 현재가 태그 (고점·현재가 강조) ----
# 고점: 표시 구간 내 최고 고가에 노란 점선 + 우측 '고 N' 라벨.
hi_price = max(highs)
hy = price_y(hi_price)
parts.append(
f'<line x1="{CHART_PAD_L}" y1="{hy:.1f}" x2="{CHART_W-CHART_PAD_R}" y2="{hy:.1f}" '
f'stroke="#fbbf24" stroke-width="1" stroke-dasharray="4 4" opacity="0.55"/>'
)
# 고가 금액 — 가격패널 좌하단 안 (구간별 최고 고가). 상단 우측 대신 아래로 이동.
parts.append(
f'<text x="{CHART_PAD_L + 8}" y="{PRICE_BOT - 10:.1f}" text-anchor="start" fill="#fbbf24" '
f'font-size="26" font-weight="600">고가 {int(round(hi_price)):,}</text>'
)
# 현재가(마지막 종가): 얇은 기준선 + 우측 축 강조 태그(상승=빨강/하락=파랑).
cur_price = candles[-1]['close']
cy = price_y(cur_price)
last_up = candles[-1]['close'] >= candles[-1]['open']
cur_color = '#ef4444' if last_up else '#3b82f6'
parts.append(
f'<line x1="{CHART_PAD_L}" y1="{cy:.1f}" x2="{CHART_W-CHART_PAD_R}" y2="{cy:.1f}" '
f'stroke="{cur_color}" stroke-width="1" stroke-dasharray="2 3" opacity="0.5"/>'
)
tag_cy = min(max(cy, PRICE_TOP + 15), PRICE_BOT - 15)
parts.append(
f'<rect x="{CHART_W - CHART_PAD_R + 2}" y="{tag_cy-15:.1f}" width="{CHART_PAD_R-6}" height="30" '
f'fill="{cur_color}" rx="3"/>'
)
parts.append(
f'<text x="{CHART_W - CHART_PAD_R + 8}" y="{tag_cy+8:.1f}" text-anchor="start" fill="#fff" '
f'font-size="26" font-weight="700">{int(round(cur_price)):,}</text>'
)
parts.append('</svg>')
return ''.join(parts)