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 900a7cd97b
19 changed files with 864 additions and 7 deletions
+58
View File
@@ -0,0 +1,58 @@
import sys
import unittest
from unittest.mock import MagicMock
import queue
# Create verification script
class TestRefactoring(unittest.TestCase):
def test_lazy_loading(self):
# We need to ensure we start fresh
if 'finestock' in sys.modules:
del sys.modules['finestock']
if 'finestock.ls' in sys.modules:
del sys.modules['finestock.ls']
import finestock
# Check if ls is imported in sys.modules
# Note: submodule might be imported but not bound to finestock.ls if lazy
# But here we check sys.modules
# Actually, if we just import finestock, it should NOT likely import finestock.ls unless __init__ does it
# However, due to previous run_command usage or environment, it might strictly be tricky to un-import.
# But let's try.
# Ideally, we check that accessing finestock.ls raises AttributeError until we create it or import it.
with self.assertRaises(AttributeError):
_ = finestock.ls
print("PASS: Lazy loading check (finestock.ls not accessible)")
def test_interfaces_and_queue(self):
import finestock
from finestock.api_factory import APIProvider
from finestock.comm.api_interface import MarketDataProvider, RealtimeProvider
# Create API
ls_api = finestock.create_api(APIProvider.LS)
self.assertIsInstance(ls_api, MarketDataProvider, "LS should implement MarketDataProvider")
self.assertIsInstance(ls_api, RealtimeProvider, "LS should implement RealtimeProvider")
print("PASS: Interface implementation check")
# Test Queue
q = queue.Queue()
ls_api.set_data_queue(q)
test_data = {"price": 100}
ls_api.add_data(test_data)
received = q.get(timeout=1)
self.assertEqual(received, test_data)
print("PASS: Queue injection check")
# Test absence of make_queue
self.assertFalse(hasattr(ls_api, 'make_queue'), "make_queue should be removed")
print("PASS: make_queue removal check")
if __name__ == '__main__':
unittest.main()