Add NH(나무) broker integration and fix balance/holds parsing

- finestock/nh/: Nh/NhV 브로커 클래스 추가 (시세/주문/잔고/실시간 WS)
- api_factory.py, path.py: APIProvider.NH/NHV 등록, 도메인/엔드포인트 매핑
- kis.py: get_holds/get_ohlcv_min/get_index_min/get_stock_list 스텁 추가,
  oauth() Content-Type 헤더 수정
- get_balance()의 실전 디버깅으로 드러난 버그 수정:
  - Hold.total(매입금액)이 존재하지 않는 byn_amt 필드를 참조해 항상 0이던 것을
    eal_amt - eal_pls_amt로 계산하도록 수정
  - rsp_cd를 "00000" 단일 값으로만 성공 판정해 정상 응답('00218' 연속조회 중,
    '00166' 마지막 페이지 등)을 실패로 오판하던 것을 Output_0 존재 여부로 판정
  - 응답 헤더의 cts/cts_flag로 연속조회를 재귀 처리해 10건 넘는 보유종목도
    전부 합쳐서 반환하도록 구현
- doc/, tests/, example_*.py, setup.py, requirements.txt, CLAUDE.md 등 추가
- README.md에 .env 환경변수 설정 가이드 추가

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01225Lu4Fc2UpMz6QixEcNT8
This commit is contained in:
2026-08-31 14:35:30 +09:00
co-authored by Claude Sonnet 5
parent a4dceccae1
commit 361b560624
27 changed files with 1820 additions and 7 deletions
+65 -5
View File
@@ -6,10 +6,11 @@ Created by alshin
## Table of Contents
1. [설치](#설치)
2. [아키텍처](#아키텍처)
3. [사용법](#사용법)
4. [Release Notes](#release-notes)
5. [License](#license)
2. [환경변수 설정 (.env)](#환경변수-설정-env)
3. [아키텍처](#아키텍처)
4. [사용법](#사용법)
5. [Release Notes](#release-notes)
6. [License](#license)
---
@@ -21,6 +22,65 @@ pip install finestock
---
## 환경변수 설정 (.env)
브로커 앱키/시크릿/계좌번호/액세스 토큰은 코드에 직접 하드코딩하지 말고 환경변수로 주입한다. 저장소 루트의 `.env.example`을 복사해 `.env`로 만들고 실제 값을 채워 넣는다.
```bash
cp .env.example .env
```
`.env` 파일 내용:
```
APP_KEY=YOUR_APP_KEY
APP_SECRET=YOUR_APP_SECRET
ACCOUNT_NUM=YOUR_ACCOUNT_NUM
ACCOUNT_NUM_SUB=01
ACCESS_TOKEN=
```
`.env``.gitignore`에 의해 커밋되지 않는다(`.env.example`만 커밋 대상).
### 값 로드 방법
`example.py`, `example_async.py`는 모두 `os.environ.get("APP_KEY", ...)` 형태로 값을 읽는다. `.env` 파일 자체는 셸이나 파이썬이 자동으로 읽어주지 않으므로 아래 두 방법 중 하나가 필요하다.
**1) python-dotenv로 자동 로드 (권장)**
```bash
pip install python-dotenv
```
예제 스크립트는 `python-dotenv`가 설치되어 있으면 시작 시 자동으로 `.env`를 읽어 `os.environ`에 채워 넣는다(설치돼 있지 않으면 조용히 건너뛴다). 직접 스크립트를 작성할 때도 아래처럼 최상단에서 호출하면 된다.
```python
from dotenv import load_dotenv
load_dotenv()
import os
app_key = os.environ.get("APP_KEY")
app_secret = os.environ.get("APP_SECRET")
```
**2) 셸에서 직접 환경변수 설정**
```powershell
# PowerShell
$env:APP_KEY = "YOUR_APP_KEY"
$env:APP_SECRET = "YOUR_APP_SECRET"
python example.py
```
```bash
# bash
export APP_KEY="YOUR_APP_KEY"
export APP_SECRET="YOUR_APP_SECRET"
python example.py
```
---
## 아키텍처
`finestock`은 **파사드 패턴(Facade Pattern)**과 **인터페이스 분리 원칙(ISP)**을 결합하여 설계되었습니다.
@@ -77,6 +137,7 @@ if isinstance(api, RealtimeProvider):
# 3. 데이터 수신 (비동기 루프 실행 필요)
# ... (자세한 예제는 example_v1.py 참조)
```
### 3. 타입 힌팅 활용 (Type Hinting)
@@ -94,5 +155,4 @@ market_api: MarketDataProvider = full_api
# market_api. (여기서 get_ohlcv 등만 보임)
df = market_api.get_ohlcv("005930")
```
```