stocks: 미국 주식 지원(개별 종목) + datacollect LLM 직접 호출 + 백엔드 기본 NAS
미국 주식 (개별 종목 조회 — add/check/judge/analysis/position): - marketUtils: KR(6자리)/US(티커) 시장 판별, 통화·시총 포매팅($/원, B·T/억·조) - yahooClient: US는 raw 티커(.KQ/.KS 미부착), 심볼 라우팅 분리 - yahooFundamentals: Yahoo quoteSummary(무키) cookie+crumb 핸드셰이크로 ROE/PER/PBR/마진/성장/부채 fetch (429·crumb 하드닝) - fundamentalsRouter: 심볼별 Naver(KR)/Yahoo(US) 분기 - criteriaEval: market-aware — US는 유보율 미보고라 유동성·안정성을 부채비율(D/E)·시총($B)으로 대체 - types: Stock.market 필드; slashStocks/llmJudge: 통화·라우팅·프롬프트 US 대응 - 테스트: stocksMarket + criteriaEval US 케이스 (전체 통과) datacollect/llm.ts: LLM을 확장(PC)에서 직접 호출(과거 bridge /api/lm 프록시 제거) → 백엔드 NAS 이전 시 PC→NAS→PC 실패 제거, 스크래핑=NAS·LLM 추론=PC 분리 package.json: datacollectBridge 기본값 nas + https://dc.koritips.com; 버전 2.2.261 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -117,6 +117,46 @@ describe('stocks criteriaEval', () => {
|
||||
expect(ev.results.find(r => r.keyword === '안정성')!.state).toBe('fail');
|
||||
});
|
||||
|
||||
// ── 미국 종목 (market-aware) ─────────────────────────────────────────
|
||||
|
||||
test('US — 유보율 미보고여도 부채비율로 유동성 통과, 안정성은 시총·부채', () => {
|
||||
const stock: Stock = { 이름: 'Apple', 심볼: 'AAPL', 투자성향: '스윙/중기' };
|
||||
const fresh = {
|
||||
symbol: 'AAPL', market: 'US' as const, currency: 'USD' as const, source: 'Yahoo quoteSummary',
|
||||
roe: 150, operatingMargin: 30, per: 28, pbr: 45, debtRatio: 140,
|
||||
revenueGrowthYoY: 8, marketCapNative: 3.2e12,
|
||||
};
|
||||
const ev = evaluateCriteria(stock, fresh, NOW);
|
||||
const byKw = (k: string) => ev.results.find(r => r.keyword === k)!;
|
||||
// 부채비율 140% > 100 → 유동성·안정성 미통과 (유보율 기준은 적용 안 됨)
|
||||
expect(byKw('유동성').state).toBe('fail');
|
||||
expect(byKw('유동성').detail).toMatch(/부채비율/);
|
||||
expect(byKw('안정성').state).toBe('fail');
|
||||
// 유보율은 numbers 에 N/A 로 표기 (KR 의 '5,800%' 자리)
|
||||
expect(ev.numbers.유보율).toBe('N/A(미보고)');
|
||||
// 시가총액은 $B 표기
|
||||
expect(ev.numbers.시가총액).toMatch(/\$/);
|
||||
});
|
||||
|
||||
test('US — 저부채·대형주는 유동성·안정성 통과', () => {
|
||||
const stock: Stock = { 이름: 'TestCo', 심볼: 'TST', 투자성향: '저평가우량주' };
|
||||
const fresh = {
|
||||
symbol: 'TST', market: 'US' as const, currency: 'USD' as const,
|
||||
roe: 18, operatingMargin: 22, per: 9, pbr: 0.9, debtRatio: 40,
|
||||
revenueGrowthYoY: 14, marketCapNative: 5e9,
|
||||
};
|
||||
const ev = evaluateCriteria(stock, fresh, NOW);
|
||||
const byKw = (k: string) => ev.results.find(r => r.keyword === k)!;
|
||||
expect(byKw('유동성').state).toBe('pass'); // D/E 40% ≤100
|
||||
expect(byKw('안정성').state).toBe('pass'); // D/E ≤100 AND $5B ≥$2B
|
||||
});
|
||||
|
||||
test('US — 심볼만으로 시장 판별 (fresh 없이 stocks.json 저장값)', () => {
|
||||
const ev = evaluateCriteria({ 이름: 'NVIDIA', 심볼: 'NVDA' }, undefined, NOW);
|
||||
// 유보율 자리가 N/A → US 경로 진입 확인
|
||||
expect(ev.numbers.유보율).toBe('N/A(미보고)');
|
||||
});
|
||||
|
||||
test('PER 키워드 — ≤12배 통과, 저평가우량주 우선순위에 포함', () => {
|
||||
const { ev, verdict } = judge({
|
||||
이름: '저PER주', 심볼: '000013', 투자성향: '저평가우량주',
|
||||
|
||||
Reference in New Issue
Block a user