auto: 일일 백업 2026-07-04 02:00
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user