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:
@@ -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
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"result": "직답 결과 — single-pass mock 응답입니다.",
|
"result": "직답 결과 — single-pass mock 응답입니다.",
|
||||||
"createdAt": 1779670266607,
|
"createdAt": 1779770051307,
|
||||||
"modelVersion": "unknown"
|
"modelVersion": "unknown"
|
||||||
}
|
}
|
||||||
+2
-2
@@ -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
-1
@@ -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
-1
@@ -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
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"result": "[{\"heading\":\"본문\",\"scope\":\"전체 답변\"}]",
|
"result": "[{\"heading\":\"본문\",\"scope\":\"전체 답변\"}]",
|
||||||
"createdAt": 1779670273513,
|
"createdAt": 1779770057838,
|
||||||
"modelVersion": "unknown"
|
"modelVersion": "unknown"
|
||||||
}
|
}
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
"result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
||||||
"createdAt": 1779670273519,
|
"createdAt": 1779770057840,
|
||||||
"modelVersion": "unknown"
|
"modelVersion": "unknown"
|
||||||
}
|
}
|
||||||
+11
-11
@@ -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": {
|
||||||
@@ -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건.
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -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
|
||||||
|
|||||||
Generated
+1
-1
@@ -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
@@ -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",
|
||||||
|
|||||||
@@ -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} 표시.`);
|
||||||
|
|||||||
@@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user