feat: v2.2.92 → v2.2.158 — god-file 분해 + Stocks feature + 대화 연속성
R56–R59: agent.ts 2731→1529줄 god-file 분해 (25 modules) · attrParsers + LLM 메서드 8개 (callNonStreaming, streamChatOnce 등) · executeActions 415줄 → 8 handler 그룹 (file/run/list/brain/calendar/sheets/tasks) · handlePrompt 1100줄 → 7 phase 모듈 (system prompt + budget + autoContinue 등) R50–R55: extension.ts 1145→349줄 (telegram/settings/provider commands 분리) Stocks feature 신규: /stocks slash command (v2.2.152~158) · .astra/stocks.json 저장소 + Yahoo Finance 현재가 갱신 · 8 키워드 필터 (ROE/성장성/유동성/수익성/영업효율/기술력/안정성/PBR) · Naver 시가총액 페이지 JSON API (m.stock.naver.com) 발굴 · LLM Top 5 매력도 분석 + Telegram 자동 보고서 · KST 09:00/15:00 watcher 자동 모니터링 대화 연속성 (v2.2.150~157): · [PRIOR TURN CONCLUSION] block 으로 직전 결론 anchor · thin follow-up 분류 → boilerplate 헤더 suppression · slash 명령 결과 chatHistory mirror (capture wrapper) · echo/parrot 금지 system prompt rule 기타: /stocks 슬래시 자동완성 dropdown UI, Naver JSON API 전환 (cheerio 제거) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import { logError, logInfo } from '../../utils';
|
||||
|
||||
/**
|
||||
* Yahoo Finance public chart endpoint 로 현재가 fetch. invest_results/quick_check.js
|
||||
* 의 동일 로직 — symbol 에 suffix 없으면 `.KQ` (코스닥) 우선, 실패 시 `.KS` (코스피) 재시도.
|
||||
*
|
||||
* Yahoo 가 한국 종목은 `<6자리>.KQ` 또는 `<6자리>.KS` 형식. US 종목은 그대로.
|
||||
* symbol 에 이미 `.` 있으면 그대로 사용.
|
||||
*
|
||||
* Returns null 이면 두 suffix 다 실패 — 호출자가 skip 처리.
|
||||
*/
|
||||
export async function fetchYahooPrice(symbol: string, timeoutMs = 8000): Promise<number | null> {
|
||||
if (!symbol) return null;
|
||||
const candidates: string[] = symbol.includes('.')
|
||||
? [symbol]
|
||||
: [`${symbol}.KQ`, `${symbol}.KS`];
|
||||
|
||||
for (const yahooSymbol of candidates) {
|
||||
try {
|
||||
const url = `https://query1.finance.yahoo.com/v8/finance/chart/${yahooSymbol}`;
|
||||
const res = await fetch(url, {
|
||||
headers: { 'User-Agent': 'Mozilla/5.0' },
|
||||
signal: AbortSignal.timeout(timeoutMs),
|
||||
});
|
||||
if (!res.ok) continue;
|
||||
const data: any = await res.json();
|
||||
const price = data?.chart?.result?.[0]?.meta?.regularMarketPrice;
|
||||
if (typeof price === 'number' && Number.isFinite(price)) {
|
||||
return price;
|
||||
}
|
||||
} catch (e: any) {
|
||||
// suffix 후보가 더 남았으면 계속 시도, 마지막이면 null fallthrough.
|
||||
if (yahooSymbol === candidates[candidates.length - 1]) {
|
||||
logError('Yahoo Finance 현재가 조회 실패.', { symbol, yahooSymbol, error: e?.message ?? String(e) });
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 종목 리스트 전체 순회하면서 fetchYahooPrice — 1초 간격으로 throttle (Yahoo rate limit).
|
||||
* partial 갱신 허용: 실패해도 다른 종목은 계속 진행, 결과 Map 반환.
|
||||
*
|
||||
* caller 가 결과를 store 에 일괄 patch.
|
||||
*/
|
||||
export async function fetchAllPrices(
|
||||
symbols: string[],
|
||||
onProgress?: (symbol: string, price: number | null) => void,
|
||||
): Promise<Map<string, number | null>> {
|
||||
const out = new Map<string, number | null>();
|
||||
for (const symbol of symbols) {
|
||||
const price = await fetchYahooPrice(symbol);
|
||||
out.set(symbol, price);
|
||||
onProgress?.(symbol, price);
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
}
|
||||
logInfo('Yahoo 일괄 갱신 완료.', { total: symbols.length, success: [...out.values()].filter(p => p !== null).length });
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user