Coverage for brokers / korea_investment / korea_invest_trid_provider.py: 95%
58 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-04 15:08 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-04 15:08 +0000
1from __future__ import annotations
2from typing import Dict, Optional, Union
3from enum import Enum
5from config.config_loader import load_configs, load_config, TR_IDS_CONFIG_PATH
6from brokers.korea_investment.korea_invest_env import KoreaInvestApiEnv
7from brokers.korea_investment.korea_invest_trid_keys import TrIdLeaf, TrId
9_DOMAINS = ("quotations", "account", "trading")
11class KoreaInvestTrIdProvider:
12 """
13 - tr_ids_config.yaml에서 TR-ID 테이블을 읽어와 키 기반으로 안전하게 조회
14 - 모드(모의/실전)에 따라 자동으로 paper/real leaf 선택(논리 키 TrId 사용 시)
15 - 문자열/Enum 모두 지원
16 """
18 def __init__(self, env: KoreaInvestApiEnv, tr_ids: Dict) -> None:
19 self._env = env
20 if not isinstance(tr_ids, dict) or not tr_ids:
21 raise ValueError("tr_ids 설정이 비어 있거나 올바르지 않습니다.")
22 self._tr_ids = tr_ids # shape: {'quotations': {...}, 'account': {...}, 'trading': {...}}
24 # ---------- 팩토리 ----------
25 @classmethod
26 def from_config_loader(cls, env: KoreaInvestApiEnv) -> "KoreaInvestTrIdProvider":
27 merged = load_configs()
28 tr_ids = merged.get("tr_ids")
29 if not tr_ids:
30 # 병합에 없다면 원본 파일 직접 로드
31 tr_ids = load_config(TR_IDS_CONFIG_PATH).get("tr_ids")
32 return cls(env, tr_ids)
34 # ---------- 내부 유틸 ----------
35 def _get_leaf_value(self, leaf_key: str) -> str:
36 """
37 leaf_key가 어느 도메인(account/quotations/trading)에 있든 값을 찾아 반환.
38 """
39 for domain in _DOMAINS:
40 dom = self._tr_ids.get(domain, {})
41 if leaf_key in dom:
42 return dom[leaf_key]
43 raise KeyError(f"tr_ids에 '{leaf_key}'를 찾을 수 없습니다.")
45 # ---------- 공개 API ----------
46 def get_by_leaf(self, key: Union[str, TrIdLeaf]) -> str:
47 """leaf 키로 직접 조회 (모드 무시)"""
48 leaf = key.value if isinstance(key, Enum) else str(key)
49 return self._get_leaf_value(leaf)
51 def get(self, key: Union[str, TrId, TrIdLeaf], **kwargs) -> str:
52 """
53 - TrId(논리키): 모드에 따라 paper/real leaf 자동 선택
54 - TrIdLeaf/str(leaf키): 그대로 반환
55 """
56 # 논리 키 처리
57 if isinstance(key, TrId): 57 ↛ 71line 57 didn't jump to line 71 because the condition on line 57 was always true
58 is_paper = bool(self._env.is_paper_trading)
59 if key is TrId.INQUIRE_BALANCE:
60 leaf = TrIdLeaf.INQUIRE_BALANCE_PAPER if is_paper else TrIdLeaf.INQUIRE_BALANCE_REAL
61 return self.get_by_leaf(leaf)
62 if key is TrId.ORDER_CASH_BUY:
63 leaf = TrIdLeaf.ORDER_CASH_BUY_PAPER if is_paper else TrIdLeaf.ORDER_CASH_BUY_REAL
64 return self.get_by_leaf(leaf)
65 if key is TrId.ORDER_CASH_SELL: 65 ↛ 68line 65 didn't jump to line 68 because the condition on line 65 was always true
66 leaf = TrIdLeaf.ORDER_CASH_SELL_PAPER if is_paper else TrIdLeaf.ORDER_CASH_SELL_REAL
67 return self.get_by_leaf(leaf)
68 raise KeyError(f"지원하지 않는 논리 키: {key}")
70 # leaf 키 그대로
71 return self.get_by_leaf(key)
73 # 편의 함수(읽기 쉬운 API)
74 def quotations(self, leaf: TrIdLeaf) -> str:
75 return self.get_by_leaf(leaf)
77 def account_inquire_balance(self) -> str:
78 return self.get(TrId.INQUIRE_BALANCE)
80 def trading_order_cash(self, is_buy: bool) -> str:
81 return self.get(TrId.ORDER_CASH_BUY if is_buy else TrId.ORDER_CASH_SELL)
83 def daily_itemchartprice(self) -> str:
84 leaf = TrIdLeaf.DAILY_ITEMCHARTPRICE
85 return self.get_by_leaf(leaf)
87 def time_itemchartprice(self) -> str:
88 leaf = TrIdLeaf.TIME_ITEMCHARTPRICE
89 return self.get_by_leaf(leaf)
91 def time_daily_itemchartprice(self) -> str:
92 leaf = TrIdLeaf.TIME_DAILY_ITEMCHARTPRICE
93 return self.get_by_leaf(leaf)