feat(stocks): v2.2.160-161 — 저평가 강조 + 224일선 회복 + 낙폭과대 키워드

영상(주식단테 시리즈) 기준을 /stocks discover에 정량 매핑:

v2.2.160:
- 저평가 키워드 2단계 추가 (PBR ≤ 1.0 = 저평가, ≤ 0.7 = 초저평가)
- 정렬 타이브레이커: 통과 키워드 수 desc → PBR asc
- 224회복 보너스 (가격 only): MA224 돌파 + 최근 30일 중 5일+ 아래에 머문 적
- yahooClient: fetchYahooHistory + evalMa224Recovery 신설

v2.2.161:
- 224회복 거래량 검증 추가 (최근 5일 평균 ≥ 60일 평균 × 1.2) — 거짓 돌파 필터
- 신규 낙폭과대 키워드: 1년 고점 대비 -25% AND 60일 저점에서 +10%
- yahooClient: YahooHistory에 volumes, evalDropRecovery 신설

chronicle: ADR-0025 추가.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 10:10:09 +09:00
parent 323537e12b
commit d206293a19
18 changed files with 1279 additions and 993 deletions
+34 -32
View File
@@ -3,20 +3,20 @@
<!-- ASTRA:AUTO-START --> <!-- ASTRA:AUTO-START -->
## Snapshot ## Snapshot
- **Workspace**: `ConnectAI` `v2.2.158` _(absolute path varies by environment; resolved from the active VS Code workspace)_ - **Workspace**: `connectai` `v2.2.161` _(absolute path varies by environment; resolved from the active VS Code workspace)_
- **Description**: The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making. - **Description**: The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.
- **Stack**: TypeScript, Node.js, VS Code Extension, LM Studio SDK, Test runner - **Stack**: TypeScript, Node.js, VS Code Extension, LM Studio SDK, Test runner
- **Stats**: 395 source files, ~63,423 lines across 5 top-level modules. - **Stats**: 396 source files, ~63,667 lines across 5 top-level modules.
## Last Refresh ## Last Refresh
- **Time**: 2026-05-25T00:59:03.313Z - **Time**: 2026-05-26T08:10:10.182Z
- **Files newly analysed**: 1 - **Files newly analysed**: 3
- **Files reused from cache**: 394 - **Files reused from cache**: 393
## Directory Map ## Directory Map
```mermaid ```mermaid
mindmap mindmap
root((ConnectAI)) root((connectai))
src/ src/
features/ features/
sidebar/ sidebar/
@@ -33,6 +33,7 @@ mindmap
docs/ docs/
records/ records/
docs/ docs/
Meeting/
``` ```
## Module Dependencies ## Module Dependencies
@@ -43,7 +44,7 @@ flowchart LR
media["media/<br/>6 files"] media["media/<br/>6 files"]
tests["tests/<br/>37 files"] tests["tests/<br/>37 files"]
core_py["core_py/<br/>6 files"] core_py["core_py/<br/>6 files"]
docs["docs/<br/>99 files"] docs["docs/<br/>100 files"]
tests --> src tests --> src
``` ```
@@ -66,7 +67,7 @@ flowchart LR
## Modules ## Modules
### `src/` — 247 files, ~45,859 lines ### `src/` — 247 files, ~46,059 lines
**Sub-directories** **Sub-directories**
- `src/features/` (87) — Astra Office — public API. 다음 세션에서 추가될 OfficeSnapshot presenter / schema 도 같은 entry 로 노출 예정. 현재 노출: full webview panel H - `src/features/` (87) — Astra Office — public API. 다음 세션에서 추가될 OfficeSnapshot presenter / schema 도 같은 entry 로 노출 예정. 현재 노출: full webview panel H
@@ -77,17 +78,17 @@ flowchart LR
- `src/extension/` (8) — 8 files (.ts) - `src/extension/` (8) — 8 files (.ts)
- `src/memory/` (8) — Episodic Memory (일화 기억) 과거 대화/회의/결정의 맥락 흐름을 저장합니다. 세션 종료 시 자동으로 에피소드를 요약하여 저장합니다. "왜 이렇게 결정했는지", "어떤 흐름으로 진행했는지" 기록. 저장 - `src/memory/` (8) — Episodic Memory (일화 기억) 과거 대화/회의/결정의 맥락 흐름을 저장합니다. 세션 종료 시 자동으로 에피소드를 요약하여 저장합니다. "왜 이렇게 결정했는지", "어떤 흐름으로 진행했는지" 기록. 저장
- `src/retrieval/` (8) — Brain Index — persistent, mtime-keyed tokenized cache of the Second Brain RAG 검색은 매 질의마다 브레인의 모든 .md 파일을 읽고 토크나이즈해서 TF-I - `src/retrieval/` (8) — Brain Index — persistent, mtime-keyed tokenized cache of the Second Brain RAG 검색은 매 질의마다 브레인의 모든 .md 파일을 읽고 토크나이즈해서 TF-I
- `src/docs/` (6) — src Chronicle Records - `src/docs/` (6) — Bug: Edited agent.ts Edited agent.ts Edited agent.ts Edited agent.ts Edited agent.ts ...
- `src/integrations/` (6) — Per-chat conversation history for the Telegram bot. Why this exists: the previous bot was stateless — every inbound mess - `src/integrations/` (6) — Per-chat conversation history for the Telegram bot. Why this exists: the previous bot was stateless — every inbound mess
- `src/lmstudio/` (4) — 4 files (.ts) - `src/lmstudio/` (4) — 4 files (.ts)
- `src/skills/` (4) — 4 files (.ts) - `src/skills/` (4) — 4 files (.ts)
**Key files** **Key files**
- `src/utils.ts` (471 lines) - `src/utils.ts` (471 lines)
- `src/agent.ts` (1487 lines) - `src/agent.ts` (1484 lines)
- `src/config.ts` (418 lines) - `src/config.ts` (418 lines)
- `src/features/company/types.ts` (446 lines) — Type definitions for the 1인 기업 (One-Person Company) mode. The mode turns the user into a virtual CEO that dispatches work to a roster of specialist agents. Each turn produces a session directory conta - `src/features/company/types.ts` (446 lines) — Type definitions for the 1인 기업 (One-Person Company) mode. The mode turns the user into a virtual CEO that dispatches work to a roster of specialist agents. Each turn produces a session directory conta
- `src/sidebarProvider.ts` (3194 lines) - `src/sidebarProvider.ts` (3186 lines)
- `src/core/services.ts` (176 lines) - `src/core/services.ts` (176 lines)
- `src/lib/contextManager.ts` (278 lines) — Context Manager (컨텍스트 한계 관리) "context length = 132k" 는 "답변을 132k 토큰까지 생성해도 된다" 가 아닙니다. 시스템 프롬프트 + 대화 기록 + 입력 문서 + 생성될 답변 + 여유분 ≤ context length 이 모듈은 요청을 보내기 전에 입력 토큰을 추정하고, - 동적으로 출력 상한(maxTokens)을 계 - `src/lib/contextManager.ts` (278 lines) — Context Manager (컨텍스트 한계 관리) "context length = 132k" 는 "답변을 132k 토큰까지 생성해도 된다" 가 아닙니다. 시스템 프롬프트 + 대화 기록 + 입력 문서 + 생성될 답변 + 여유분 ≤ context length 이 모듈은 요청을 보내기 전에 입력 토큰을 추정하고, - 동적으로 출력 상한(maxTokens)을 계
- `src/features/company/companyConfig.ts` (896 lines) — State + config plumbing for 1인 기업 모드. Two surfaces: - CompanyState (runtime data: enabled flag, company name, which agents are active, per-agent model overrides). Persisted in VS Code's globalState so - `src/features/company/companyConfig.ts` (896 lines) — State + config plumbing for 1인 기업 모드. Two surfaces: - CompanyState (runtime data: enabled flag, company name, which agents are active, per-agent model overrides). Persisted in VS Code's globalState so
@@ -109,11 +110,11 @@ flowchart LR
- `src/lmstudio/streamer.ts` (252 lines) - `src/lmstudio/streamer.ts` (252 lines)
- `src/core/responseRecovery.ts` (310 lines) — Response Recovery — Thought Quarantine + Final-only Retry + Auto-Continuation The user already asked their question; they're waiting for an answer, not for a chance to babysit the generation engine. S - `src/core/responseRecovery.ts` (310 lines) — Response Recovery — Thought Quarantine + Final-only Retry + Auto-Continuation The user already asked their question; they're waiting for an answer, not for a chance to babysit the generation engine. S
### `media/` — 6 files, ~7,649 lines ### `media/` — 6 files, ~7,671 lines
**Key files** **Key files**
- `media/sidebar.css` (2104 lines) — Stylesheet - `media/sidebar.css` (2114 lines) — Stylesheet
- `media/sidebar.js` (3921 lines) - `media/sidebar.js` (3933 lines)
- `media/sidebar.html` (539 lines) — Astra - `media/sidebar.html` (539 lines) — Astra
- `media/settings-panel.html` (406 lines) — Astra Settings - `media/settings-panel.html` (406 lines) — Astra Settings
- `media/settings-panel.css` (210 lines) — Stylesheet - `media/settings-panel.css` (210 lines) — Stylesheet
@@ -164,38 +165,39 @@ flowchart LR
- `core_py/optimizer.py` (55 lines) - `core_py/optimizer.py` (55 lines)
- `core_py/queue_worker.py` (82 lines) - `core_py/queue_worker.py` (82 lines)
### `docs/` — 99 files, ~3,631 lines ### `docs/` — 100 files, ~3,653 lines
**Sub-directories** **Sub-directories**
- `docs/records/` (86) — Astra Project Chronicle Records - `docs/records/` (87) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 프로젝트 코드 리뷰 해줄 수 있어? 개선할 부분이 있는지, 그러고...
- `docs/docs/` (5) — docs Chronicle Records - `docs/docs/` (5) — Bug: Viewed integrationretrieval.test.ts:1-59 integrationretrieval.test.ts를 통해 ...
- `docs/Meeting/` (0)
**Key files** **Key files**
- `docs/TELEGRAM_REMOTE_EXECUTION_PLAN.md` (452 lines) — Telegram Remote Execution 기획서 - `docs/TELEGRAM_REMOTE_EXECUTION_PLAN.md` (452 lines) — Telegram Remote Execution 기획서
- `docs/AgentEngine_Architecture.md` (314 lines) — AgentEngine Architecture Document - `docs/AgentEngine_Architecture.md` (314 lines) — AgentEngine Architecture Document
- `docs/records/ConnectAI/timeline.md` (209 lines) — Project Timeline - `docs/records/ConnectAI/timeline.md` (212 lines) — Project Timeline
- `docs/ASTRA_OFFICE_REFACTOR.md` (198 lines) — Astra Office Refactor — Design Doc - `docs/ASTRA_OFFICE_REFACTOR.md` (198 lines) — Astra Office Refactor — Design Doc
- `docs/EXPERIENCE_MEMORY_PLAN.md` (122 lines) — Experience Memory (Mistake / Lesson Loop) — Implementation Plan - `docs/EXPERIENCE_MEMORY_PLAN.md` (122 lines) — Experience Memory (Mistake / Lesson Loop) — Implementation Plan
- `docs/records/ConnectAI/development/2026-05-02_connectai_project_knowledge_overview.md` (121 lines) — Astra Project Knowledge Overview - `docs/records/ConnectAI/development/2026-05-02_connectai_project_knowledge_overview.md` (121 lines) — Astra Project Knowledge Overview
- `docs/records/ConnectAI/development/2026-05-03_connectai_project_knowledge_overview.md` (121 lines) — Astra Project Knowledge Overview - `docs/records/ConnectAI/development/2026-05-03_connectai_project_knowledge_overview.md` (121 lines) — Astra Project Knowledge Overview
- `docs/Advanced_Features_Implementation_Guide.md` (40 lines) — Advanced Features Implementation Guide - `docs/Advanced_Features_Implementation_Guide.md` (40 lines) — Advanced Features Implementation Guide
- `docs/PROJECT_CHRONICLE_GUARD_ROADMAP.md` (43 lines) — Project Chronicle Guard: Search Engine Roadmap
- `docs/UX_UI_Consistency_Guidelines.md` (44 lines) — UX/UI Consistency Guidelines
- `docs/docs/records/docs/README.md` (18 lines) — docs Chronicle Records
- `docs/docs/records/docs/bugs/BUG-0001-viewed-integration-retrieval-test-ts-1-59-integration-retrie.md` (16 lines) — Bug: Viewed integrationretrieval.test.ts:1-59 integrationretrieval.test.ts를 통해 ... - `docs/docs/records/docs/bugs/BUG-0001-viewed-integration-retrieval-test-ts-1-59-integration-retrie.md` (16 lines) — Bug: Viewed integrationretrieval.test.ts:1-59 integrationretrieval.test.ts를 통해 ...
- `docs/docs/records/docs/chronicle.config.json` (11 lines) — JSON configuration - `docs/docs/records/docs/chronicle.config.json` (11 lines) — JSON configuration
- `docs/docs/records/docs/project-profile.md` (31 lines) — Project Profile - `docs/docs/records/docs/project-profile.md` (31 lines) — Project Profile
- `docs/docs/records/docs/README.md` (18 lines) — docs Chronicle Records
- `docs/docs/records/docs/timeline.md` (7 lines) — Project Timeline - `docs/docs/records/docs/timeline.md` (7 lines) — Project Timeline
- `docs/records/ConnectAI/README.md` (18 lines) — Astra Project Chronicle Records - `docs/PROJECT_CHRONICLE_GUARD_ROADMAP.md` (43 lines) — Project Chronicle Guard: Search Engine Roadmap
- `docs/records/ConnectAI/bugs/BUG-0001-volumes-data-project-antigravity-connectai-프로젝트-코드-리뷰-해줄-수-있.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 프로젝트 코드 리뷰 해줄 수 있어? 개선할 부분이 있는지, 그러고... - `docs/records/ConnectAI/bugs/BUG-0001-volumes-data-project-antigravity-connectai-프로젝트-코드-리뷰-해줄-수-있.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 프로젝트 코드 리뷰 해줄 수 있어? 개선할 부분이 있는지, 그러고...
- `docs/records/ConnectAI/bugs/BUG-0002-지금-내가-분석-요청하고-너가-답을-줄때-아래-템플릿에-맞춰-답을-써주고-있는데-개선-포인트가-있는지-확인해.md` (16 lines) — Bug: 지금 내가 분석 요청하고 너가 답을 줄때 아래 템플릿에 맞춰 답을 써주고 있는데, 개선 포인트가 있는지 확인해줘. ## 내가 보는 위험 가장 큰... - `docs/records/ConnectAI/bugs/BUG-0002-지금-내가-분석-요청하고-너가-답을-줄때-아래-템플릿에-맞춰-답을-써주고-있는데-개선-포인트가-있는지-확인해.md` (16 lines) — Bug: 지금 내가 분석 요청하고 너가 답을 줄때 아래 템플릿에 맞춰 답을 써주고 있는데, 개선 포인트가 있는지 확인해줘. ## 내가 보는 위험 가장 큰...
- `docs/records/ConnectAI/bugs/BUG-0003-volumes-data-project-antigravity-connectai-내-질문에-대한-답변이-잘-정리.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는데 focused... - `docs/records/ConnectAI/bugs/BUG-0003-volumes-data-project-antigravity-connectai-내-질문에-대한-답변이-잘-정리.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는데 focused...
- `docs/records/ConnectAI/bugs/BUG-0004-volumes-data-project-antigravity-connectai-내-질문에-대한-답변이-잘-정리.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는데 focused... - `docs/records/ConnectAI/bugs/BUG-0004-volumes-data-project-antigravity-connectai-내-질문에-대한-답변이-잘-정리.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는데 focused...
- `docs/records/ConnectAI/bugs/BUG-0005-다시한번-답줘-volumes-data-project-antigravity-connectai-내-질문에-대한-.md` (16 lines) — Bug: 다시한번 답줘. /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는... - `docs/records/ConnectAI/bugs/BUG-0005-다시한번-답줘-volumes-data-project-antigravity-connectai-내-질문에-대한-.md` (16 lines) — Bug: 다시한번 답줘. /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는...
- `docs/records/ConnectAI/bugs/BUG-0006-volumes-data-project-antigravity-connectai-내-질문에-대한-답변이-잘-정리.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는데 focused... - `docs/records/ConnectAI/bugs/BUG-0006-volumes-data-project-antigravity-connectai-내-질문에-대한-답변이-잘-정리.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는데 focused...
- `docs/records/ConnectAI/bugs/BUG-0007-volumes-data-project-antigravity-connectai-내-질문에-대한-답변이-잘-정리.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는데 focused... - `docs/records/ConnectAI/bugs/BUG-0007-volumes-data-project-antigravity-connectai-내-질문에-대한-답변이-잘-정리.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는데 focused...
- `docs/records/ConnectAI/bugs/BUG-0008-volumes-data-project-antigravity-connectai-내-질문에-대한-답변이-잘-정리.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는데 focused... - `docs/records/ConnectAI/bugs/BUG-0008-volumes-data-project-antigravity-connectai-내-질문에-대한-답변이-잘-정리.md` (16 lines) — Bug: /Volumes/Data/project/Antigravity/ConnectAI 내 질문에 대한 답변이 잘 정리되서 알려주긴 하는데 focused...
- `docs/records/ConnectAI/bugs/BUG-0009-문제점을-읽고-어떻게-개선하는게-최선인지-분석해주면-좋겠어-알겠습니다-지금부터-connectai-프로젝트-에.md` (16 lines) — Bug: 문제점을 읽고 어떻게 개선하는게 최선인지 분석해주면 좋겠어. 알겠습니다. 지금부터 ConnectAI 프로젝트에만 완전히 집중하겠습니다. ... - `docs/records/ConnectAI/bugs/BUG-0009-문제점을-읽고-어떻게-개선하는게-최선인지-분석해주면-좋겠어-알겠습니다-지금부터-connectai-프로젝트-에.md` (16 lines) — Bug: 문제점을 읽고 어떻게 개선하는게 최선인지 분석해주면 좋겠어. 알겠습니다. 지금부터 ConnectAI 프로젝트에만 완전히 집중하겠습니다. ...
- `docs/records/ConnectAI/bugs/BUG-0010-문제점을-읽고-어떻게-개선하는게-최선인지-분석해주면-좋겠어-알겠습니다-지금부터-connectai-프로젝트-에.md` (16 lines) — Bug: 문제점을 읽고 어떻게 개선하는게 최선인지 분석해주면 좋겠어. 알겠습니다. 지금부터 ConnectAI 프로젝트에만 완전히 집중하겠습니다. ...
- `docs/records/ConnectAI/bugs/BUG-0011-문제점을-읽고-어떻게-개선하는게-최선인지-분석해주면-좋겠어-알겠습니다-지금부터-connectai-프로젝트-에.md` (16 lines) — Bug: 문제점을 읽고 어떻게 개선하는게 최선인지 분석해주면 좋겠어. 알겠습니다. 지금부터 ConnectAI 프로젝트에만 완전히 집중하겠습니다. ...
## VS Code Extension Surface ## VS Code Extension Surface
- **Extension ID**: `g1nation.astra` - **Extension ID**: `g1nation.astra`
@@ -339,7 +341,7 @@ Astra는 대표님의 명시적인 승인 하에 로컬 시스템의 강력한
**Designed for High-Performance Decision Making.** **Designed for High-Performance Decision Making.**
Copyright (C) **g1nation**. All rights reserved. Copyright (C) **g1nation**. All rights reserved.
_Last auto-scan: 2026-05-25T00:59:03.313Z · signature `fca24b52`_ _Last auto-scan: 2026-05-26T08:10:10.182Z · signature `850d1550`_
<!-- ASTRA:AUTO-END --> <!-- ASTRA:AUTO-END -->
## Purpose ## Purpose
File diff suppressed because it is too large Load Diff
@@ -1,5 +1,5 @@
{ {
"result": "직답 결과 — single-pass mock 응답입니다.", "result": "직답 결과 — single-pass mock 응답입니다.",
"createdAt": 1779670266607, "createdAt": 1779770051307,
"modelVersion": "unknown" "modelVersion": "unknown"
} }
@@ -1,5 +1,5 @@
{ {
"result": "---\nid: wiki_on\ndate: 2026-05-25T00:51:06.608Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\n직답 결과 — single-pass mock 응답입니다.\n\n직답 결과 — single-pass mock 응답입니다.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `0/100` | ✅ Low |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[DIRECT]** 답변 작성 중... (단일 호출 fast-path) (13ms)\n", "result": "---\nid: wiki_on\ndate: 2026-05-26T04:34:11.309Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\n직답 결과 — single-pass mock 응답입니다.\n\n직답 결과 — single-pass mock 응답입니다.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `0/100` | ✅ Low |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[DIRECT]** 답변 작성 중... (단일 호출 fast-path) (24ms)\n",
"createdAt": 1779670266608, "createdAt": 1779770051309,
"modelVersion": "unknown" "modelVersion": "unknown"
} }
@@ -1,8 +1,8 @@
{ {
"missionId": "wiki_on", "missionId": "wiki_on",
"status": "completed", "status": "completed",
"startTime": "2026-05-25T00:51:06.593Z", "startTime": "2026-05-26T04:34:11.282Z",
"totalElapsedMs": 15, "totalElapsedMs": 28,
"results": { "results": {
"direct": "직답 결과 — single-pass mock 응답입니다." "direct": "직답 결과 — single-pass mock 응답입니다."
}, },
@@ -12,16 +12,16 @@
{ {
"from": "idle", "from": "idle",
"to": "direct", "to": "direct",
"durationMs": 13, "durationMs": 24,
"message": "답변 작성 중... (단일 호출 fast-path)", "message": "답변 작성 중... (단일 호출 fast-path)",
"ts": "2026-05-25T00:51:06.606Z" "ts": "2026-05-26T04:34:11.306Z"
}, },
{ {
"from": "direct", "from": "direct",
"to": "completed", "to": "completed",
"durationMs": 2, "durationMs": 4,
"message": "미션 완료", "message": "미션 완료",
"ts": "2026-05-25T00:51:06.608Z" "ts": "2026-05-26T04:34:11.310Z"
} }
], ],
"resilienceMetrics": { "resilienceMetrics": {
@@ -1,5 +1,5 @@
{ {
"result": "Final report with inconsistencies. This should be long enough to pass validation.", "result": "Final report with inconsistencies. This should be long enough to pass validation.",
"createdAt": 1779670273525, "createdAt": 1779770057843,
"modelVersion": "unknown" "modelVersion": "unknown"
} }
@@ -1,5 +1,5 @@
{ {
"result": "Final report with inconsistencies. This should be long enough to pass validation.", "result": "Final report with inconsistencies. This should be long enough to pass validation.",
"createdAt": 1779670273525, "createdAt": 1779770057842,
"modelVersion": "unknown" "modelVersion": "unknown"
} }
@@ -1,5 +1,5 @@
{ {
"result": "[{\"heading\":\"본문\",\"scope\":\"전체 답변\"}]", "result": "[{\"heading\":\"본문\",\"scope\":\"전체 답변\"}]",
"createdAt": 1779670273513, "createdAt": 1779770057838,
"modelVersion": "unknown" "modelVersion": "unknown"
} }
@@ -1,5 +1,5 @@
{ {
"result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.", "result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
"createdAt": 1779670273519, "createdAt": 1779770057840,
"modelVersion": "unknown" "modelVersion": "unknown"
} }
@@ -1,8 +1,8 @@
{ {
"missionId": "stress_conflict_1779670273495", "missionId": "stress_conflict_1779770057821",
"status": "completed", "status": "completed",
"startTime": "2026-05-25T00:51:13.495Z", "startTime": "2026-05-26T04:34:17.821Z",
"totalElapsedMs": 30, "totalElapsedMs": 23,
"results": { "results": {
"outline": "[{\"heading\":\"본문\",\"scope\":\"전체 답변\"}]", "outline": "[{\"heading\":\"본문\",\"scope\":\"전체 답변\"}]",
"section_0": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.", "section_0": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
@@ -14,30 +14,30 @@
{ {
"from": "idle", "from": "idle",
"to": "outline", "to": "outline",
"durationMs": 12, "durationMs": 16,
"message": "답변 구조 잡는 중...", "message": "답변 구조 잡는 중...",
"ts": "2026-05-25T00:51:13.507Z" "ts": "2026-05-26T04:34:17.837Z"
}, },
{ {
"from": "outline", "from": "outline",
"to": "section", "to": "section",
"durationMs": 6, "durationMs": 2,
"message": "본문 작성 중...", "message": "본문 작성 중...",
"ts": "2026-05-25T00:51:13.513Z" "ts": "2026-05-26T04:34:17.839Z"
}, },
{ {
"from": "section", "from": "section",
"to": "polish", "to": "polish",
"durationMs": 6, "durationMs": 2,
"message": "최종 다듬기 중...", "message": "최종 다듬기 중...",
"ts": "2026-05-25T00:51:13.519Z" "ts": "2026-05-26T04:34:17.841Z"
}, },
{ {
"from": "polish", "from": "polish",
"to": "completed", "to": "completed",
"durationMs": 6, "durationMs": 2,
"message": "미션 완료", "message": "미션 완료",
"ts": "2026-05-25T00:51:13.525Z" "ts": "2026-05-26T04:34:17.843Z"
} }
], ],
"resilienceMetrics": { "resilienceMetrics": {
+34
View File
@@ -1,5 +1,39 @@
# Astra Patch Notes # Astra Patch Notes
## v2.2.161 (2026-05-22)
### 📈 /stocks discover — 낙폭과대 키워드 + 224회복 거래량 확인
- **신규 `낙폭과대` 키워드** ([evalDropRecovery](src/features/stocks/yahooClient.ts)): 영상(주식단테 "이미 빠진 종목 + 바닥 찍고 회복")의 정량화. 현재가 ≤ 1년 최고가 × 0.75(25%↓) AND 현재가 ≥ 60일 최저가 × 1.10(저점에서 10%↑) → `낙폭과대` +1. 224회복(추세 전환)과는 다른 각도 — 안전마진 + 반등 초입.
- **`224회복` 거래량 확인 강화** ([evalMa224Recovery](src/features/stocks/yahooClient.ts)): 기존 가격 조건에 더해 최근 5일 평균 거래량 ≥ 60일 평균 × 1.2 요구. 거짓 돌파(일시적 가격 튐) 필터. 거래량 데이터 부족 시 가격 조건만으로 판정(폴백).
- 추가 API 비용 없음 — `fetchYahooHistory`가 이미 1년 일봉을 가져오므로 거래량·1년 max·60일 min 계산만 추가.
- 결과 표시 예: `통과 (5): ROE, 수익성, 저평가, 224회복, 낙폭과대`.
- **신규 패키징:** `astra-2.2.161.vsix`.
---
## v2.2.160 (2026-05-22)
### 📈 /stocks discover — 저평가(PBR↓) 강조 + 224일선 회복 패턴 추가
- **저평가 2단계 키워드** ([stockDiscovery.ts:evaluateKeywords](src/features/stocks/stockDiscovery.ts)): 기존 `PBR`(≤1.5)는 유지. 추가로 PBR ≤ 1.0 = `저평가`, PBR ≤ 0.7 = `초저평가`. PBR 낮을수록 점수 자체가 +1~2 가산.
- **PBR 오름차순 타이브레이커**: 정렬 = 통과 키워드 수 desc → 동점 시 PBR asc. 같은 점수면 저평가 종목이 위로.
- **224일선 회복 보너스** (`224회복`): 영상(주식단테 "224일선 안착 = 추세 전환") 기준 정량화. 1차 통과 후보에 한해 Yahoo 1년 일봉 fetch → 현재 종가 ≥ 오늘 MA224 AND 최근 30거래일 중 5일 이상 MA224 아래에 머문 적 있으면 `224회복` 키워드 +1. 회복 직후 추세 전환 신호인 종목이 자연스럽게 상위로.
- **신규 모듈** ([yahooClient.ts](src/features/stocks/yahooClient.ts)): `fetchYahooHistory`(1년 일봉), `fetchAllHistories`(배치, 1초/심볼 throttle), `evalMa224Recovery`(rolling MA224 + 회복 판정).
- 비용: 1차 통과 후보(보통 20~50개)에만 시세 fetch → +20~50초 추가. 전체 1차후보엔 안 돌림.
- **신규 패키징:** `astra-2.2.160.vsix`.
---
## v2.2.159 (2026-05-22)
### 📦 재패키징 — 다른 머신의 2.2.158 코드 + chronicle 갱신을 이 머신 설치본으로 빌드
- 소스 변경 없음. `git pull` 로 받은 2.2.158 작업 트리(다른 머신의 v2.2.64~2.2.158 누적 작업 — LM Studio 발견·로그·CSP·chunked writer·info prompt 강화·답변 포맷 정리·god-file 분해·Stocks·대화 연속성 등)를 이 머신용 `.vsix` 로 패키징.
- **신규 패키징:** `astra-2.2.159.vsix`.
---
## v2.2.72 (2026-05-23) ## v2.2.72 (2026-05-23)
### ⚡ LM Studio 통신 hardening + 속도 부스트 팩 ### ⚡ LM Studio 통신 hardening + 속도 부스트 팩
한 turn 에 두 라운드 작업을 묶었다. (A) 통신 경로 안전망 9건 + (B) 속도 개선 4건. 한 turn 에 두 라운드 작업을 묶었다. (A) 통신 경로 안전망 9건 + (B) 속도 개선 4건.
+5 -5
View File
@@ -1,11 +1,11 @@
{ {
"projectId": "connectai", "projectId": "connectai",
"projectName": "ConnectAI", "projectName": "connectai",
"projectRoot": "/Volumes/Data/project/Antigravity/ConnectAI", "projectRoot": "E:\\Wiki\\connectai",
"recordRoot": "/Volumes/Data/project/Antigravity/ConnectAI/docs/records/ConnectAI", "recordRoot": "E:\\Wiki\\connectai\\docs\\records\\connectai",
"description": "Auto-created by Project Architecture activation.", "description": "Auto-created by Project Architecture activation.",
"corePurpose": "", "corePurpose": "",
"detailLevel": "standard", "detailLevel": "standard",
"createdAt": "2026-05-23T03:51:11.620Z", "createdAt": "2026-05-20T09:42:40.003Z",
"updatedAt": "2026-05-25T07:42:03.089Z" "updatedAt": "2026-05-26T08:10:03.968Z"
} }
@@ -0,0 +1,19 @@
# ADR: E:\Wiki\connectai 프로젝트에 대한 너의 평가 해줘.
## Status
accepted
## Context
E:\Wiki\connectai 프로젝트에 대한 너의 평가 해줘.
## Decision
한 줄 판단 — 현재 정보만으로는 기술 구조를 판단할 수 없습니다. 잘된 점 - 확장성 있는 기능 설계: `g1nation.company.toggle`과 같은 명령어를 통해 '1인 기업 모드'라는 독특한 사용자 경험(UX)을 제공하며, 에이전트 기반의 워크플로우 확장을 고려한 구조를 갖추고 있습니다. - 체계적인 컨텍스트 관리: `src/lib/contextManager.ts`를 통해 LLM의 토큰 한계를 동적으로 관리하고 입력 토큰을 추적하는 로직은 대규모 언어 모델(LLM) 활용 시 필수적인 안정성을 제공합니다. - 풍부한 사용자 인터페이스(UI) 커맨드: 29개의 다양한 명령어를 통해 VS Code 사이드바와 에디터 영역에서 사용자가 복잡한 설정 없이도 기능을 즉시 제어할 수 있도록 설계되어 있습니다. 부족한 점 - 높은 의존성 위험: `src/utils.ts`가 87개의 파일에서 참조되고 있어, 유틸리티 함수 하나를 수정했을 때 전체 프로젝트에 미치는 영향(Blast Radi...
## Reason
Captured automatically because the conversation contained decision-oriented language.
## Alternatives
Not captured yet.
## Consequences
- Future prompts should treat this as project context unless the user changes direction.
+3
View File
@@ -207,3 +207,6 @@
## 2026-05-23 ## 2026-05-23
- Auto decision record created: decisions/ADR-0024-volumes-data-project-antigravity-connectai-코드-리뷰하고-사용자-입장에서-.md - Auto decision record created: decisions/ADR-0024-volumes-data-project-antigravity-connectai-코드-리뷰하고-사용자-입장에서-.md
## 2026-05-26
- Auto decision record created: decisions\ADR-0025-e-wiki-connectai-프로젝트에-대한-너의-평가-해줘.md
+1 -1
View File
@@ -6,7 +6,7 @@
"packages": { "packages": {
"": { "": {
"name": "astra", "name": "astra",
"version": "2.2.154", "version": "2.2.161",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@lmstudio/sdk": "^1.5.0", "@lmstudio/sdk": "^1.5.0",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "astra", "name": "astra",
"displayName": "Astra", "displayName": "Astra",
"description": "The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.", "description": "The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.",
"version": "2.2.158", "version": "2.2.161",
"publisher": "g1nation", "publisher": "g1nation",
"license": "MIT", "license": "MIT",
"icon": "assets/icon.png", "icon": "assets/icon.png",
+44 -7
View File
@@ -1,6 +1,7 @@
import { logInfo } from '../../utils'; import { logInfo } from '../../utils';
import { screenMarket, type Market, type ScreenerEntry } from './naverScreener'; import { screenMarket, type Market, type ScreenerEntry } from './naverScreener';
import { fetchAllFundamentals, type Fundamentals } from './naverFundamentals'; import { fetchAllFundamentals, type Fundamentals } from './naverFundamentals';
import { fetchAllHistories, evalMa224Recovery, evalDropRecovery } from './yahooClient';
import type { Stock } from './types'; import type { Stock } from './types';
/** /**
@@ -68,6 +69,10 @@ function evaluateKeywords(f: Fundamentals): string[] {
if (techKeywords.some(k => sector.includes(k)) && pbr >= 2) passed.push('기술력'); if (techKeywords.some(k => sector.includes(k)) && pbr >= 2) passed.push('기술력');
if (retention >= 3000 && mktCap >= 5000) passed.push('안정성'); if (retention >= 3000 && mktCap >= 5000) passed.push('안정성');
if (pbr > 0 && pbr <= 1.5) passed.push('PBR'); if (pbr > 0 && pbr <= 1.5) passed.push('PBR');
// 저평가 강조 — PBR이 낮을수록 추가 가산. 사용자 요청("저평가된 종목을 기준으로")에 맞춰
// 1.0 / 0.7 두 단계로 elevate. PBR=0.5짜리는 PBR+저평가+초저평가 = 3 키워드 자동 통과.
if (pbr > 0 && pbr <= 1.0) passed.push('저평가');
if (pbr > 0 && pbr <= 0.7) passed.push('초저평가');
return passed; return passed;
} }
@@ -128,17 +133,42 @@ export async function discoverStocks(opts: DiscoverOptions = {}): Promise<Discov
if (i % 10 === 0 || i === total) progress(` 펀더멘털 ${i}/${total} 처리 중...`); if (i % 10 === 0 || i === total) progress(` 펀더멘털 ${i}/${total} 처리 중...`);
}); });
// (4) 8 키워드 평가. // (4) 펀더멘털 키워드 평가 — 1차 통과 후보 추림.
const candidates: DiscoveredCandidate[] = []; const prelim: { entry: ScreenerEntry; f: Fundamentals; passed: string[] }[] = [];
for (const entry of allEntries) { for (const entry of allEntries) {
const f = fundsMap.get(entry.symbol); const f = fundsMap.get(entry.symbol);
if (!f) continue; if (!f) continue;
const passed = evaluateKeywords(f); const passed = evaluateKeywords(f);
if (passed.length < 3) continue; if (passed.length < 3) continue;
// 상위 3개만 골라서 텍스트화 (judge 와 동일 패턴). prelim.push({ entry, f, passed });
}
// (5) 1년 시세 → 224일선 회복 패턴 보너스 키워드.
// 영상의 "주가가 224일선 아래 머물다 돌파 = 추세 전환" 신호를 펀더멘털 통과 후보에만
// 얹는다(전체 1차후보에 안 돌리는 이유: Yahoo 1초/심볼 throttle 비용 절감).
if (prelim.length > 0) {
progress(`\n📈 ${prelim.length}개 후보의 1년 시세로 224일선 회복 패턴 확인 중 (Yahoo 1초/종목, ~${prelim.length}초)...`);
const histMap = await fetchAllHistories(
prelim.map(p => p.entry.symbol),
(_sym, _ok, i, total) => {
if (i % 10 === 0 || i === total) progress(` 시세 ${i}/${total} 처리 중...`);
},
);
for (const p of prelim) {
const h = histMap.get(p.entry.symbol);
if (!h) continue;
const r = evalMa224Recovery(h);
if (r?.passed) p.passed.push('224회복');
const dr = evalDropRecovery(h);
if (dr?.passed) p.passed.push('낙폭과대');
}
}
// (6) DiscoveredCandidate 변환.
const candidates: DiscoveredCandidate[] = prelim.map(({ entry, f, passed }) => {
const top3 = passed.slice(0, 3); const top3 = passed.slice(0, 3);
const filterText = `[발굴 자동] 충족 (${top3.join(', ')})`; const filterText = `[발굴 자동] 충족 (${top3.join(', ')})`;
candidates.push({ return {
symbol: entry.symbol, symbol: entry.symbol,
name: entry.name, name: entry.name,
market: entry.market, market: entry.market,
@@ -146,11 +176,18 @@ export async function discoverStocks(opts: DiscoverOptions = {}): Promise<Discov
passedKeywords: passed, passedKeywords: passed,
asStock: fundamentalsToStock(entry, f, filterText), asStock: fundamentalsToStock(entry, f, filterText),
fundamentals: f, fundamentals: f,
};
}); });
}
// sortScore — 통과 키워드 수 내림차순. // (7) sortScore — 통과 키워드 수 desc, 동점 시 PBR asc(저평가가 위로) 타이브레이커.
candidates.sort((a, b) => b.passedKeywords.length - a.passedKeywords.length); candidates.sort((a, b) => {
if (b.passedKeywords.length !== a.passedKeywords.length) {
return b.passedKeywords.length - a.passedKeywords.length;
}
const pbrA = a.fundamentals.pbr ?? Number.MAX_SAFE_INTEGER;
const pbrB = b.fundamentals.pbr ?? Number.MAX_SAFE_INTEGER;
return pbrA - pbrB;
});
const limited = candidates.slice(0, limit); const limited = candidates.slice(0, limit);
logInfo(`Stocks discovery: ${allEntries.length} 1차 후보 → ${candidates.length} 매수후보 → ${limited.length} 표시.`); logInfo(`Stocks discovery: ${allEntries.length} 1차 후보 → ${candidates.length} 매수후보 → ${limited.length} 표시.`);
+183
View File
@@ -58,3 +58,186 @@ export async function fetchAllPrices(
logInfo('Yahoo 일괄 갱신 완료.', { total: symbols.length, success: [...out.values()].filter(p => p !== null).length }); logInfo('Yahoo 일괄 갱신 완료.', { total: symbols.length, success: [...out.values()].filter(p => p !== null).length });
return out; return out;
} }
// ───────────────────────────────────────────────────────────────────────────
// 1년 일봉 시세 — 224일 이동평균 + 회복 패턴 판정에 사용.
// 영상(주식단테 "224일선 안착 = 추세 전환") 신호를 펀더멘털 발굴에 보조 기준으로 얹기 위한 입력.
// ───────────────────────────────────────────────────────────────────────────
export interface YahooHistory {
/** 종가 배열 (오래된→최신 순). null 종가는 제거됨. */
closes: number[];
/** 거래량 배열 (closes와 같은 인덱스 정렬, null은 0으로 채움). */
volumes: number[];
}
export async function fetchYahooHistory(symbol: string, timeoutMs = 10000): Promise<YahooHistory | 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}?range=1y&interval=1d`;
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 q = data?.chart?.result?.[0]?.indicators?.quote?.[0];
const closesRaw: any[] = q?.close ?? [];
const volumesRaw: any[] = q?.volume ?? [];
// closes·volumes를 같은 인덱스로 정렬해 유지 — null close는 양쪽 모두 제거,
// null volume은 0으로 보전(낙폭과대/거래량 평균 계산이 흔들리지 않게).
const closes: number[] = [];
const volumes: number[] = [];
for (let i = 0; i < closesRaw.length; i++) {
const c = closesRaw[i];
if (typeof c !== 'number' || !Number.isFinite(c)) continue;
closes.push(c);
const v = volumesRaw[i];
volumes.push(typeof v === 'number' && Number.isFinite(v) ? v : 0);
}
if (closes.length === 0) continue;
return { closes, volumes };
} catch (e: any) {
if (yahooSymbol === candidates[candidates.length - 1]) {
logError('Yahoo Finance 1년 시세 조회 실패.', { symbol, yahooSymbol, error: e?.message ?? String(e) });
}
}
}
return null;
}
export async function fetchAllHistories(
symbols: string[],
onProgress?: (symbol: string, ok: boolean, i: number, total: number) => void,
): Promise<Map<string, YahooHistory | null>> {
const out = new Map<string, YahooHistory | null>();
for (let i = 0; i < symbols.length; i++) {
const symbol = symbols[i];
const h = await fetchYahooHistory(symbol);
out.set(symbol, h);
onProgress?.(symbol, h !== null, i + 1, symbols.length);
await new Promise(r => setTimeout(r, 1000)); // Yahoo rate limit 보호
}
return out;
}
/** window 길이 단순 이동평균 (오래된→최신 순 입력, 출력 길이 = arr.length - window + 1). */
function rollingMean(arr: number[], window: number): number[] {
const out: number[] = [];
if (arr.length < window) return out;
let sum = 0;
for (let i = 0; i < window; i++) sum += arr[i];
out.push(sum / window);
for (let i = window; i < arr.length; i++) {
sum += arr[i] - arr[i - window];
out.push(sum / window);
}
return out;
}
export interface Ma224RecoveryResult {
/** 회복 패턴 + 거래량 확인을 모두 통과했는지. */
passed: boolean;
/** 오늘 시점 MA224 값. */
ma224Today?: number;
/** 최근 30거래일 중 종가가 그 시점 MA224 아래였던 일수. */
daysBelowLast30?: number;
/** 현재 종가. */
currentPrice?: number;
/** 거래량 확인 통과 여부 (최근 5일 평균 ≥ 60일 평균 × 1.2). 거래량 데이터 부족 시 true. */
volumeConfirmed?: boolean;
/** 최근 5일 평균 거래량. */
vol5dAvg?: number;
/** 최근 60일 평균 거래량. */
vol60dAvg?: number;
}
/**
* 224( 1 ) .
*
* "주가가 224일선 아래 머물다 → 강한 거래량으로 돌파 → 안착 = 추세 전환"
* :
* - MA224 ( )
* - 30 5 MA224 ( )
*
* ( 224 ) null .
*/
export function evalMa224Recovery(history: YahooHistory): Ma224RecoveryResult | null {
const closes = history.closes;
if (closes.length < 224) return null;
const ma = rollingMean(closes, 224);
const ma224Today = ma[ma.length - 1];
const currentPrice = closes[closes.length - 1];
if (!Number.isFinite(ma224Today) || !Number.isFinite(currentPrice)) return null;
if (currentPrice < ma224Today) {
return { passed: false, ma224Today, daysBelowLast30: 0, currentPrice };
}
const lookback = Math.min(30, ma.length);
let daysBelow = 0;
for (let i = ma.length - lookback; i < ma.length; i++) {
const priceAtI = closes[i + 224 - 1]; // ma[i] = mean of closes[i..i+223]
if (priceAtI < ma[i]) daysBelow++;
}
const priceCondition = daysBelow >= 5;
// 거래량 확인 — 영상(주식단테 "강한 거래량으로 돌파") 의 정량화. 거짓 돌파(일시적
// 가격 튐) 필터. 데이터 부족(거래량 60일 미만)이면 가격 조건만으로 판정한다.
let volumeConfirmed = true;
let vol5dAvg: number | undefined;
let vol60dAvg: number | undefined;
if (history.volumes && history.volumes.length >= 60) {
const v5 = history.volumes.slice(-5);
const v60 = history.volumes.slice(-60);
vol5dAvg = v5.reduce((a, b) => a + b, 0) / 5;
vol60dAvg = v60.reduce((a, b) => a + b, 0) / 60;
volumeConfirmed = vol60dAvg > 0 && vol5dAvg >= vol60dAvg * 1.2;
}
return {
passed: priceCondition && volumeConfirmed,
ma224Today, daysBelowLast30: daysBelow, currentPrice,
volumeConfirmed, vol5dAvg, vol60dAvg,
};
}
export interface DropRecoveryResult {
/** 낙폭과대 + 반등 초입 패턴 통과 여부. */
passed: boolean;
/** 1년 최고가 (history 전 구간 max). */
high1y?: number;
/** 60거래일 최저가. */
low60d?: number;
/** 현재 종가. */
currentPrice?: number;
/** 1년 최고가 대비 현재가 비율 (0~1+, 낮을수록 많이 빠짐). */
fromHighRatio?: number;
/** 60일 저점 대비 현재가 비율 (1+, 높을수록 많이 반등). */
fromLowRatio?: number;
}
/**
* "낙폭과대 + 반등 초입" . ( "이미 빠진 종목 + 바닥 찍고 회복")
* . 224( ) + .
*
* - 1 × 0.75 (= 25% )
* - AND 60 × 1.10 (= 10% )
*
* ( 60 ) null .
*/
export function evalDropRecovery(history: YahooHistory): DropRecoveryResult | null {
const closes = history.closes;
if (closes.length < 60) return null;
const currentPrice = closes[closes.length - 1];
const high1y = Math.max(...closes);
const low60d = Math.min(...closes.slice(-60));
if (!Number.isFinite(high1y) || !Number.isFinite(low60d) || high1y <= 0 || low60d <= 0) return null;
const fromHighRatio = currentPrice / high1y;
const fromLowRatio = currentPrice / low60d;
return {
passed: fromHighRatio <= 0.75 && fromLowRatio >= 1.10,
high1y, low60d, currentPrice, fromHighRatio, fromLowRatio,
};
}