hermetix 0.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hermetix-0.10.0/LICENSE +21 -0
- hermetix-0.10.0/PKG-INFO +168 -0
- hermetix-0.10.0/README.md +142 -0
- hermetix-0.10.0/hermetix/__init__.py +73 -0
- hermetix-0.10.0/hermetix/broker.py +268 -0
- hermetix-0.10.0/hermetix/brokers/__init__.py +5 -0
- hermetix-0.10.0/hermetix/brokers/db.py +323 -0
- hermetix-0.10.0/hermetix/brokers/db_stream.py +305 -0
- hermetix-0.10.0/hermetix/brokers/kb.py +287 -0
- hermetix-0.10.0/hermetix/brokers/kis.py +377 -0
- hermetix-0.10.0/hermetix/brokers/kis_stream.py +362 -0
- hermetix-0.10.0/hermetix/brokers/kiwoom.py +298 -0
- hermetix-0.10.0/hermetix/brokers/kiwoom_stream.py +298 -0
- hermetix-0.10.0/hermetix/brokers/ls.py +283 -0
- hermetix-0.10.0/hermetix/brokers/ls_stream.py +355 -0
- hermetix-0.10.0/hermetix/brokers/next.py +349 -0
- hermetix-0.10.0/hermetix/brokers/nh.py +351 -0
- hermetix-0.10.0/hermetix/brokers/nh_stream.py +312 -0
- hermetix-0.10.0/hermetix/brokers/toss.py +271 -0
- hermetix-0.10.0/hermetix/brokers/toss_stream.py +352 -0
- hermetix-0.10.0/hermetix/engine.py +588 -0
- hermetix-0.10.0/hermetix/errors.py +49 -0
- hermetix-0.10.0/hermetix/models.py +299 -0
- hermetix-0.10.0/hermetix/strategy.py +148 -0
- hermetix-0.10.0/hermetix/stream.py +248 -0
- hermetix-0.10.0/hermetix/testing.py +174 -0
- hermetix-0.10.0/hermetix.egg-info/PKG-INFO +168 -0
- hermetix-0.10.0/hermetix.egg-info/SOURCES.txt +43 -0
- hermetix-0.10.0/hermetix.egg-info/dependency_links.txt +1 -0
- hermetix-0.10.0/hermetix.egg-info/requires.txt +9 -0
- hermetix-0.10.0/hermetix.egg-info/top_level.txt +1 -0
- hermetix-0.10.0/pyproject.toml +36 -0
- hermetix-0.10.0/setup.cfg +4 -0
- hermetix-0.10.0/tests/test_conformance.py +114 -0
- hermetix-0.10.0/tests/test_db_stream.py +152 -0
- hermetix-0.10.0/tests/test_engine.py +182 -0
- hermetix-0.10.0/tests/test_engine_stream.py +289 -0
- hermetix-0.10.0/tests/test_examples.py +151 -0
- hermetix-0.10.0/tests/test_kis_order_events.py +70 -0
- hermetix-0.10.0/tests/test_ls_stream.py +158 -0
- hermetix-0.10.0/tests/test_nh_stream.py +171 -0
- hermetix-0.10.0/tests/test_parsing.py +357 -0
- hermetix-0.10.0/tests/test_stream.py +326 -0
- hermetix-0.10.0/tests/test_stream_fixture.py +154 -0
- hermetix-0.10.0/tests/test_toss_stream.py +181 -0
hermetix-0.10.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 tauthdev
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
hermetix-0.10.0/PKG-INFO
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hermetix
|
|
3
|
+
Version: 0.10.0
|
|
4
|
+
Summary: 증권사 모의투자 통합 트레이딩 프레임워크 - Python
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/tauthdev/hermetix-trading-core/tree/main/python
|
|
7
|
+
Project-URL: Repository, https://github.com/tauthdev/hermetix-trading-core
|
|
8
|
+
Project-URL: Issues, https://github.com/tauthdev/hermetix-trading-core/issues
|
|
9
|
+
Keywords: trading,paper-trading,stock,bot,korea,broker,kis,kiwoom,websocket
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
20
|
+
Requires-Dist: websockets>=12; extra == "dev"
|
|
21
|
+
Requires-Dist: cryptography>=42; extra == "dev"
|
|
22
|
+
Provides-Extra: stream
|
|
23
|
+
Requires-Dist: websockets>=12; extra == "stream"
|
|
24
|
+
Requires-Dist: cryptography>=42; extra == "stream"
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# Hermetix Python
|
|
28
|
+
|
|
29
|
+
증권사 모의투자 통합 트레이딩 프레임워크 — Python 구현. **런타임 의존성 0개** (표준 라이브러리만). 실시간 스트림만 선택 의존성 `websockets`(+ KIS 주문 통보 복호화용 `cryptography`)를 씁니다 (`pip install 'hermetix[stream]'`).
|
|
30
|
+
|
|
31
|
+
Kotlin 구현(레퍼런스)과 같은 동작을 보장합니다: 같은 브로커 어댑터 3종, 같은 전략 규약, 같은 안전장치. 금액은 전부 `Decimal` — float 를 섞지 마세요.
|
|
32
|
+
|
|
33
|
+
## 설치
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install hermetix # PyPI (실시간 스트림까지: pip install 'hermetix[stream]')
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## 빠른 시작
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from decimal import Decimal
|
|
43
|
+
from hermetix import NextClient, Strategy, StrategySpec, StrategyEngine, Buy, CandleInterval
|
|
44
|
+
|
|
45
|
+
class Ma20Strategy(Strategy):
|
|
46
|
+
spec = StrategySpec(name="ma20", symbols=["AAPL"],
|
|
47
|
+
candle_interval=CandleInterval.DAY_1, candle_limit=20)
|
|
48
|
+
|
|
49
|
+
def decide(self, ctx):
|
|
50
|
+
q = ctx.quote("AAPL")
|
|
51
|
+
candles = ctx.candles_of("AAPL")
|
|
52
|
+
if not q or len(candles) < 20 or ctx.has_position("AAPL") or ctx.has_open_order("AAPL"):
|
|
53
|
+
return []
|
|
54
|
+
ma20 = sum(c.close for c in candles) / 20
|
|
55
|
+
if q.price > ma20:
|
|
56
|
+
return [Buy("AAPL", Decimal(1),
|
|
57
|
+
take_profit_price=q.price * Decimal("1.04"), # 익절/손절은
|
|
58
|
+
stop_loss_price=q.price * Decimal("0.98"))] # 엔진이 자동 실행
|
|
59
|
+
return []
|
|
60
|
+
|
|
61
|
+
broker = NextClient(client_id="pk_test_...", client_secret="sk_test_...")
|
|
62
|
+
StrategyEngine(broker, [Ma20Strategy()]).run() # 정규장 중에만 틱, Ctrl+C 로 종료
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
브로커 전환은 클라이언트 교체 한 줄:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
broker = KisClient(appkey=..., appsecret=..., cano=...) # 한국투자 모의 (KRX)
|
|
69
|
+
broker = NhClient(app_key=..., app_secret=...) # NH투자증권 NH PLUG (⚠️ 미검증, 문서 기반)
|
|
70
|
+
broker = DbClient(app_key=..., app_secret=...) # DB증권 (⚠️ 미검증, 문서 기반)
|
|
71
|
+
broker = LsClient(app_key=..., app_secret=...) # LS증권 (⚠️ 미검증, 문서 기반)
|
|
72
|
+
broker = TossClient(client_id=..., client_secret=...) # 토스증권 (⚠️ 실전 전용, 미검증) — live_trading_enabled 필수
|
|
73
|
+
broker = KbClient(app_key=..., app_secret=...) # KB증권 오픈베타 (⚠️ 실전 전용, 미검증) — live_trading_enabled 필수
|
|
74
|
+
broker = KiwoomClient(appkey=..., secretkey=...) # 키움 모의 (KRX)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
KRX 브로커는 일봉(DAY_1)만 지원합니다(토스는 1m/1d) — 엔진이 기동 시 비호환 전략을 걸러내고 이유를 알려줍니다.
|
|
78
|
+
|
|
79
|
+
## 실전투자로 전환
|
|
80
|
+
|
|
81
|
+
모의에서 검증한 뒤 실제 계좌로 옮길 때는 환경과 명시 동의를 함께 지정합니다. 하나라도 빠지면 엔진이 전략을 스케줄하지 않습니다.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from decimal import Decimal
|
|
85
|
+
from hermetix import KisClient, StrategyEngine, TradingEnvironment
|
|
86
|
+
|
|
87
|
+
broker = KisClient(appkey=..., appsecret=..., cano=..., environment=TradingEnvironment.LIVE) # 호스트·TR ID 자동 전환
|
|
88
|
+
StrategyEngine(
|
|
89
|
+
broker, [MyStrategy()],
|
|
90
|
+
live_trading_enabled=True, # 실전 명시 동의
|
|
91
|
+
max_order_value=Decimal(1_000_000), # 주문 1건 상한 (브로커 통화)
|
|
92
|
+
max_daily_order_value=Decimal(5_000_000), # 하루(UTC) 누적 상한 — 매수·매도 합산
|
|
93
|
+
).run()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- 넥스트증권은 키 프리픽스가 환경을 결정합니다 (`pk_test_`=모의, `pk_live_`=실전). `environment` 와 어긋나면 생성 시 `ValueError`
|
|
97
|
+
- 심볼에 시장 접두를 붙일 수 있습니다 (`KRX:005930`, `US:AAPL`). 접두 없는 심볼은 브로커 기본 시장으로 해석됩니다
|
|
98
|
+
- 키는 항상 당신의 기기에서만 쓰입니다. Hermetix 는 어떤 서버로도 키를 보내지 않습니다
|
|
99
|
+
|
|
100
|
+
## 실시간 스트림 (0.8.0)
|
|
101
|
+
|
|
102
|
+
`kis`·`kiwoom` 은 웹소켓 체결가·호가 스트림(2026-09 모의 실측)과 주문 통보 스트림(문서 기반, 실측 전)을 제공합니다. 전략 코드는 그대로 두고 `trigger` 만 바꾸면 체결 틱마다 `decide()` 가 호출됩니다.
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
pip install 'hermetix[stream]' # websockets + cryptography 선택 의존성 — 코어는 여전히 의존성 0
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from hermetix import KisClient, Strategy, StrategySpec, StrategyEngine, TickTrigger
|
|
110
|
+
|
|
111
|
+
class Scalper(Strategy):
|
|
112
|
+
spec = StrategySpec(name="scalp", symbols=["005930"],
|
|
113
|
+
trigger=TickTrigger.ON_TRADE, # 체결 틱마다 호출
|
|
114
|
+
min_tick_interval_seconds=1.0, # 연속 호출 사이 최소 간격 (캔들·계좌 REST 폭주 방지)
|
|
115
|
+
poll_interval_seconds=60, # 스트림이 끊겼을 때의 안전망 주기
|
|
116
|
+
order_book=True) # 호가창 스트림도 구독 -> ctx.order_book(symbol)
|
|
117
|
+
...
|
|
118
|
+
|
|
119
|
+
StrategyEngine(KisClient(appkey=..., appsecret=..., cano=...), [Scalper()]).run()
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
- 몰려온 틱은 하나로 합쳐지고, 스트림이 끊기면 자동 재접속하는 동안 폴링이 계속 돕니다
|
|
123
|
+
- 스트림 틱이 전략의 모든 심볼을 덮으면 `ctx.quote()` 는 REST 대신 마지막 체결 틱(가격·호가·누적거래량)입니다. 캔들·계좌·미체결은 여전히 REST
|
|
124
|
+
- 스트림을 선언하지 않은 브로커(`next` 는 공개 스펙에 웹소켓 없음, 나머지는 미구현)에서 `ON_TRADE` 를 쓰면 경고 후 폴링으로 동작합니다
|
|
125
|
+
- `order_book=True` 전략은 심볼의 10단계 호가창을 `ctx.order_book(symbol)` 로 받습니다 (`best_ask`/`best_bid`/`asks`/`bids`/총잔량). 호가는 틱을 촉발하지 않습니다
|
|
126
|
+
- 브로커가 주문 통보 채널을 제공하면 엔진이 자동 구독해 진입 주문 체결을 서버 조회 없이 브라켓에 반영하고, KIS 모의처럼 주문 조회가 없는 어댑터의 메모리 추적도 즉시 확정합니다. KIS 는 `KisClient(..., hts_id="HTS아이디")` 가 필요하고(통보 프레임은 AES 암호문이라 `cryptography` 사용), 비우면 경고 후 폴링 판정으로 동작합니다
|
|
127
|
+
- 프레임 파서: `hermetix.brokers.kis_stream.parse_kis_frame / parse_kis_order_book / parse_kis_order_events`, `hermetix.brokers.kiwoom_stream.parse_kiwoom_real / parse_kiwoom_order_book / parse_kiwoom_order_events` — 골든 픽스처 `conformance/fixtures/*.json#stream` 으로 검증
|
|
128
|
+
- **nh · db · ls · toss 스트림 (문서 기반, 실측 전)** — `NhClient`/`DbClient`/`LsClient`/`TossClient` 도 `open_stream()` 으로 체결가·호가·주문 통보를 받습니다. 공식 문서·SDK·AsyncAPI 로 만든 구현이라 계좌로 실측하기 전까지는 미검증이며, 파서는 `hermetix.brokers.{nh,db,ls,toss}_stream` 의 `parse_*` 함수와 픽스처 `stream.measured=false` 섹션으로 검증합니다. NH 는 `market_cd` 에 따라 채널(oc/nc/mc)이 갈리고 모의 서버는 시세 채널이 "미제공" 표기, DB 는 접속 후 10초 안에 첫 구독을 보내야 하며, LS 는 KOSPI/KOSDAQ TR 을 종목마다 둘 다 등록하고, 토스는 Bearer 핸드셰이크·선언형 구독·60초 `PING` 을 씁니다. **넥스트·KB 는 웹소켓이 없어**(공개 스펙에 없음) 폴링만 됩니다.
|
|
129
|
+
|
|
130
|
+
## 수익률
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from hermetix import pnl_report
|
|
134
|
+
print(pnl_report(broker, initial_capital=Decimal(20000)))
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## 구조
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
hermetix/
|
|
141
|
+
├── models.py 공통 도메인 모델 (Quote/Candle/Order/... 전부 Decimal)
|
|
142
|
+
├── errors.py 타입화된 에러 (RateLimit/MarketClosed/... 엔진이 타입별 반응)
|
|
143
|
+
├── broker.py BrokerClient ABC + MarketStream/StreamingBrokerClient + HTTP/쓰로틀/KRX 캘린더
|
|
144
|
+
├── stream.py ReconnectingWebSocket (websockets 선택 의존성, 재접속·유휴 감시·직렬 전송)
|
|
145
|
+
├── brokers/ next.py · kis.py · kiwoom.py · … (방언 정규화는 어댑터 책임), kis_stream.py · kiwoom_stream.py · nh_stream.py · db_stream.py · ls_stream.py · toss_stream.py
|
|
146
|
+
├── strategy.py Strategy/StrategySpec(TickTrigger·order_book)/StrategyContext(order_book)/Signal(Buy·Sell·Cancel)
|
|
147
|
+
└── engine.py StrategyEngine(ON_TRADE 트리거·호가·주문통보)/브라켓(통보로 활성화)/비상정지/PnL
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 공식 전략 예제 (examples/)
|
|
151
|
+
|
|
152
|
+
Kotlin 전략 레포 3종과 동일 로직의 실행 가능한 단일 파일:
|
|
153
|
+
|
|
154
|
+
| 파일 | 전략 | 실행 |
|
|
155
|
+
|---|---|---|
|
|
156
|
+
| `examples/larry.py` | 변동성 돌파 | `HERMETIX_BROKER=next NEXT_CLIENT_ID=... python examples/larry.py` |
|
|
157
|
+
| `examples/trend_breakout.py` | WMA 추세선 돌파 | 동일 |
|
|
158
|
+
| `examples/grid.py` | 목표가 스캘핑 (KRX 호환) | `HERMETIX_BROKER=kis KIS_APPKEY=... python examples/grid.py` |
|
|
159
|
+
|
|
160
|
+
## 테스트
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
pip install -e './python[dev]'
|
|
164
|
+
pytest python/tests/ # 오프라인 (골든 픽스처 재생)
|
|
165
|
+
python python/tests/smoke.py next|kis|kiwoom # 실서버 (환경변수로 키 주입)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
동작 상세(브로커별 제약, 소프트웨어 브라켓, 상태 지도)는 [코어 문서](../docs/architecture.md)와 동일합니다.
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Hermetix Python
|
|
2
|
+
|
|
3
|
+
증권사 모의투자 통합 트레이딩 프레임워크 — Python 구현. **런타임 의존성 0개** (표준 라이브러리만). 실시간 스트림만 선택 의존성 `websockets`(+ KIS 주문 통보 복호화용 `cryptography`)를 씁니다 (`pip install 'hermetix[stream]'`).
|
|
4
|
+
|
|
5
|
+
Kotlin 구현(레퍼런스)과 같은 동작을 보장합니다: 같은 브로커 어댑터 3종, 같은 전략 규약, 같은 안전장치. 금액은 전부 `Decimal` — float 를 섞지 마세요.
|
|
6
|
+
|
|
7
|
+
## 설치
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install hermetix # PyPI (실시간 스트림까지: pip install 'hermetix[stream]')
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 빠른 시작
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from decimal import Decimal
|
|
17
|
+
from hermetix import NextClient, Strategy, StrategySpec, StrategyEngine, Buy, CandleInterval
|
|
18
|
+
|
|
19
|
+
class Ma20Strategy(Strategy):
|
|
20
|
+
spec = StrategySpec(name="ma20", symbols=["AAPL"],
|
|
21
|
+
candle_interval=CandleInterval.DAY_1, candle_limit=20)
|
|
22
|
+
|
|
23
|
+
def decide(self, ctx):
|
|
24
|
+
q = ctx.quote("AAPL")
|
|
25
|
+
candles = ctx.candles_of("AAPL")
|
|
26
|
+
if not q or len(candles) < 20 or ctx.has_position("AAPL") or ctx.has_open_order("AAPL"):
|
|
27
|
+
return []
|
|
28
|
+
ma20 = sum(c.close for c in candles) / 20
|
|
29
|
+
if q.price > ma20:
|
|
30
|
+
return [Buy("AAPL", Decimal(1),
|
|
31
|
+
take_profit_price=q.price * Decimal("1.04"), # 익절/손절은
|
|
32
|
+
stop_loss_price=q.price * Decimal("0.98"))] # 엔진이 자동 실행
|
|
33
|
+
return []
|
|
34
|
+
|
|
35
|
+
broker = NextClient(client_id="pk_test_...", client_secret="sk_test_...")
|
|
36
|
+
StrategyEngine(broker, [Ma20Strategy()]).run() # 정규장 중에만 틱, Ctrl+C 로 종료
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
브로커 전환은 클라이언트 교체 한 줄:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
broker = KisClient(appkey=..., appsecret=..., cano=...) # 한국투자 모의 (KRX)
|
|
43
|
+
broker = NhClient(app_key=..., app_secret=...) # NH투자증권 NH PLUG (⚠️ 미검증, 문서 기반)
|
|
44
|
+
broker = DbClient(app_key=..., app_secret=...) # DB증권 (⚠️ 미검증, 문서 기반)
|
|
45
|
+
broker = LsClient(app_key=..., app_secret=...) # LS증권 (⚠️ 미검증, 문서 기반)
|
|
46
|
+
broker = TossClient(client_id=..., client_secret=...) # 토스증권 (⚠️ 실전 전용, 미검증) — live_trading_enabled 필수
|
|
47
|
+
broker = KbClient(app_key=..., app_secret=...) # KB증권 오픈베타 (⚠️ 실전 전용, 미검증) — live_trading_enabled 필수
|
|
48
|
+
broker = KiwoomClient(appkey=..., secretkey=...) # 키움 모의 (KRX)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
KRX 브로커는 일봉(DAY_1)만 지원합니다(토스는 1m/1d) — 엔진이 기동 시 비호환 전략을 걸러내고 이유를 알려줍니다.
|
|
52
|
+
|
|
53
|
+
## 실전투자로 전환
|
|
54
|
+
|
|
55
|
+
모의에서 검증한 뒤 실제 계좌로 옮길 때는 환경과 명시 동의를 함께 지정합니다. 하나라도 빠지면 엔진이 전략을 스케줄하지 않습니다.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from decimal import Decimal
|
|
59
|
+
from hermetix import KisClient, StrategyEngine, TradingEnvironment
|
|
60
|
+
|
|
61
|
+
broker = KisClient(appkey=..., appsecret=..., cano=..., environment=TradingEnvironment.LIVE) # 호스트·TR ID 자동 전환
|
|
62
|
+
StrategyEngine(
|
|
63
|
+
broker, [MyStrategy()],
|
|
64
|
+
live_trading_enabled=True, # 실전 명시 동의
|
|
65
|
+
max_order_value=Decimal(1_000_000), # 주문 1건 상한 (브로커 통화)
|
|
66
|
+
max_daily_order_value=Decimal(5_000_000), # 하루(UTC) 누적 상한 — 매수·매도 합산
|
|
67
|
+
).run()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- 넥스트증권은 키 프리픽스가 환경을 결정합니다 (`pk_test_`=모의, `pk_live_`=실전). `environment` 와 어긋나면 생성 시 `ValueError`
|
|
71
|
+
- 심볼에 시장 접두를 붙일 수 있습니다 (`KRX:005930`, `US:AAPL`). 접두 없는 심볼은 브로커 기본 시장으로 해석됩니다
|
|
72
|
+
- 키는 항상 당신의 기기에서만 쓰입니다. Hermetix 는 어떤 서버로도 키를 보내지 않습니다
|
|
73
|
+
|
|
74
|
+
## 실시간 스트림 (0.8.0)
|
|
75
|
+
|
|
76
|
+
`kis`·`kiwoom` 은 웹소켓 체결가·호가 스트림(2026-09 모의 실측)과 주문 통보 스트림(문서 기반, 실측 전)을 제공합니다. 전략 코드는 그대로 두고 `trigger` 만 바꾸면 체결 틱마다 `decide()` 가 호출됩니다.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
pip install 'hermetix[stream]' # websockets + cryptography 선택 의존성 — 코어는 여전히 의존성 0
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
from hermetix import KisClient, Strategy, StrategySpec, StrategyEngine, TickTrigger
|
|
84
|
+
|
|
85
|
+
class Scalper(Strategy):
|
|
86
|
+
spec = StrategySpec(name="scalp", symbols=["005930"],
|
|
87
|
+
trigger=TickTrigger.ON_TRADE, # 체결 틱마다 호출
|
|
88
|
+
min_tick_interval_seconds=1.0, # 연속 호출 사이 최소 간격 (캔들·계좌 REST 폭주 방지)
|
|
89
|
+
poll_interval_seconds=60, # 스트림이 끊겼을 때의 안전망 주기
|
|
90
|
+
order_book=True) # 호가창 스트림도 구독 -> ctx.order_book(symbol)
|
|
91
|
+
...
|
|
92
|
+
|
|
93
|
+
StrategyEngine(KisClient(appkey=..., appsecret=..., cano=...), [Scalper()]).run()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- 몰려온 틱은 하나로 합쳐지고, 스트림이 끊기면 자동 재접속하는 동안 폴링이 계속 돕니다
|
|
97
|
+
- 스트림 틱이 전략의 모든 심볼을 덮으면 `ctx.quote()` 는 REST 대신 마지막 체결 틱(가격·호가·누적거래량)입니다. 캔들·계좌·미체결은 여전히 REST
|
|
98
|
+
- 스트림을 선언하지 않은 브로커(`next` 는 공개 스펙에 웹소켓 없음, 나머지는 미구현)에서 `ON_TRADE` 를 쓰면 경고 후 폴링으로 동작합니다
|
|
99
|
+
- `order_book=True` 전략은 심볼의 10단계 호가창을 `ctx.order_book(symbol)` 로 받습니다 (`best_ask`/`best_bid`/`asks`/`bids`/총잔량). 호가는 틱을 촉발하지 않습니다
|
|
100
|
+
- 브로커가 주문 통보 채널을 제공하면 엔진이 자동 구독해 진입 주문 체결을 서버 조회 없이 브라켓에 반영하고, KIS 모의처럼 주문 조회가 없는 어댑터의 메모리 추적도 즉시 확정합니다. KIS 는 `KisClient(..., hts_id="HTS아이디")` 가 필요하고(통보 프레임은 AES 암호문이라 `cryptography` 사용), 비우면 경고 후 폴링 판정으로 동작합니다
|
|
101
|
+
- 프레임 파서: `hermetix.brokers.kis_stream.parse_kis_frame / parse_kis_order_book / parse_kis_order_events`, `hermetix.brokers.kiwoom_stream.parse_kiwoom_real / parse_kiwoom_order_book / parse_kiwoom_order_events` — 골든 픽스처 `conformance/fixtures/*.json#stream` 으로 검증
|
|
102
|
+
- **nh · db · ls · toss 스트림 (문서 기반, 실측 전)** — `NhClient`/`DbClient`/`LsClient`/`TossClient` 도 `open_stream()` 으로 체결가·호가·주문 통보를 받습니다. 공식 문서·SDK·AsyncAPI 로 만든 구현이라 계좌로 실측하기 전까지는 미검증이며, 파서는 `hermetix.brokers.{nh,db,ls,toss}_stream` 의 `parse_*` 함수와 픽스처 `stream.measured=false` 섹션으로 검증합니다. NH 는 `market_cd` 에 따라 채널(oc/nc/mc)이 갈리고 모의 서버는 시세 채널이 "미제공" 표기, DB 는 접속 후 10초 안에 첫 구독을 보내야 하며, LS 는 KOSPI/KOSDAQ TR 을 종목마다 둘 다 등록하고, 토스는 Bearer 핸드셰이크·선언형 구독·60초 `PING` 을 씁니다. **넥스트·KB 는 웹소켓이 없어**(공개 스펙에 없음) 폴링만 됩니다.
|
|
103
|
+
|
|
104
|
+
## 수익률
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from hermetix import pnl_report
|
|
108
|
+
print(pnl_report(broker, initial_capital=Decimal(20000)))
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## 구조
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
hermetix/
|
|
115
|
+
├── models.py 공통 도메인 모델 (Quote/Candle/Order/... 전부 Decimal)
|
|
116
|
+
├── errors.py 타입화된 에러 (RateLimit/MarketClosed/... 엔진이 타입별 반응)
|
|
117
|
+
├── broker.py BrokerClient ABC + MarketStream/StreamingBrokerClient + HTTP/쓰로틀/KRX 캘린더
|
|
118
|
+
├── stream.py ReconnectingWebSocket (websockets 선택 의존성, 재접속·유휴 감시·직렬 전송)
|
|
119
|
+
├── brokers/ next.py · kis.py · kiwoom.py · … (방언 정규화는 어댑터 책임), kis_stream.py · kiwoom_stream.py · nh_stream.py · db_stream.py · ls_stream.py · toss_stream.py
|
|
120
|
+
├── strategy.py Strategy/StrategySpec(TickTrigger·order_book)/StrategyContext(order_book)/Signal(Buy·Sell·Cancel)
|
|
121
|
+
└── engine.py StrategyEngine(ON_TRADE 트리거·호가·주문통보)/브라켓(통보로 활성화)/비상정지/PnL
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## 공식 전략 예제 (examples/)
|
|
125
|
+
|
|
126
|
+
Kotlin 전략 레포 3종과 동일 로직의 실행 가능한 단일 파일:
|
|
127
|
+
|
|
128
|
+
| 파일 | 전략 | 실행 |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| `examples/larry.py` | 변동성 돌파 | `HERMETIX_BROKER=next NEXT_CLIENT_ID=... python examples/larry.py` |
|
|
131
|
+
| `examples/trend_breakout.py` | WMA 추세선 돌파 | 동일 |
|
|
132
|
+
| `examples/grid.py` | 목표가 스캘핑 (KRX 호환) | `HERMETIX_BROKER=kis KIS_APPKEY=... python examples/grid.py` |
|
|
133
|
+
|
|
134
|
+
## 테스트
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
pip install -e './python[dev]'
|
|
138
|
+
pytest python/tests/ # 오프라인 (골든 픽스처 재생)
|
|
139
|
+
python python/tests/smoke.py next|kis|kiwoom # 실서버 (환경변수로 키 주입)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
동작 상세(브로커별 제약, 소프트웨어 브라켓, 상태 지도)는 [코어 문서](../docs/architecture.md)와 동일합니다.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""Hermetix - 증권사 모의투자 통합 트레이딩 프레임워크 (Python).
|
|
2
|
+
|
|
3
|
+
빠른 시작:
|
|
4
|
+
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from hermetix import NextClient, Strategy, StrategySpec, StrategyEngine, Buy, CandleInterval
|
|
7
|
+
|
|
8
|
+
class MyStrategy(Strategy):
|
|
9
|
+
spec = StrategySpec(name="my-first", symbols=["AAPL"])
|
|
10
|
+
|
|
11
|
+
def decide(self, ctx):
|
|
12
|
+
q = ctx.quote("AAPL")
|
|
13
|
+
if q and not ctx.has_position("AAPL") and not ctx.has_open_order("AAPL"):
|
|
14
|
+
return [Buy("AAPL", Decimal(1),
|
|
15
|
+
take_profit_price=q.price * Decimal("1.04"),
|
|
16
|
+
stop_loss_price=q.price * Decimal("0.98"))]
|
|
17
|
+
return []
|
|
18
|
+
|
|
19
|
+
broker = NextClient(client_id="pk_test_...", client_secret="sk_test_...")
|
|
20
|
+
StrategyEngine(broker, [MyStrategy()]).run()
|
|
21
|
+
|
|
22
|
+
브로커 전환은 클라이언트 교체 한 줄:
|
|
23
|
+
|
|
24
|
+
broker = KisClient(appkey=..., appsecret=..., cano=...) # 한국투자 모의
|
|
25
|
+
broker = KiwoomClient(appkey=..., secretkey=...) # 키움 모의
|
|
26
|
+
"""
|
|
27
|
+
from .broker import (BrokerClient, MarketStream, OrderBookListener, OrderEventListener, RateLimiter, StreamingBrokerClient,
|
|
28
|
+
TradeListener)
|
|
29
|
+
from .testing import ConformanceReport, ConformanceScenario, verify_broker_conformance
|
|
30
|
+
from .brokers.db import DbClient
|
|
31
|
+
from .brokers.kb import KbClient
|
|
32
|
+
from .brokers.kis import KisClient
|
|
33
|
+
from .brokers.ls import LsClient
|
|
34
|
+
from .brokers.nh import NhClient
|
|
35
|
+
from .brokers.toss import TossClient
|
|
36
|
+
from .brokers.kiwoom import KiwoomClient
|
|
37
|
+
from .brokers.next import NextClient
|
|
38
|
+
from .brokers.db_stream import DbMarketStream
|
|
39
|
+
from .brokers.kis_stream import KisMarketStream
|
|
40
|
+
from .brokers.kiwoom_stream import KiwoomMarketStream
|
|
41
|
+
from .brokers.ls_stream import LsMarketStream
|
|
42
|
+
from .brokers.nh_stream import NhMarketStream
|
|
43
|
+
from .brokers.toss_stream import TossMarketStream
|
|
44
|
+
from .engine import BracketMonitor, MarketCalendar, OrderExecutor, RiskGuard, StrategyEngine, TradingGuard, pnl_report
|
|
45
|
+
from .errors import (
|
|
46
|
+
AuthError, BrokerApiError, InsufficientFundsError, InvalidOrderError,
|
|
47
|
+
MarketClosedError, OrderNotFoundError, RateLimitError,
|
|
48
|
+
)
|
|
49
|
+
from .models import (
|
|
50
|
+
Account, BrokerCapabilities, Candle, CandleInterval, CreateOrderRequest,
|
|
51
|
+
Fill, Holding, MarketDay, Order, OrderBookLevel, OrderBookTick, OrderEvent, OrderEventType, OrderSide, OrderStatus,
|
|
52
|
+
OrderType, Quote, StreamChannel, TimeInForce, TradeTick, TradingEnvironment, normalize_order_id, parse_symbol, symbol_code,
|
|
53
|
+
symbols_match,
|
|
54
|
+
)
|
|
55
|
+
from .strategy import Buy, Cancel, Sell, Signal, Strategy, StrategyContext, StrategySpec, TickTrigger
|
|
56
|
+
|
|
57
|
+
__version__ = "0.10.0"
|
|
58
|
+
|
|
59
|
+
__all__ = [
|
|
60
|
+
"BrokerClient", "RateLimiter", "MarketStream", "StreamingBrokerClient", "TradeListener", "NextClient", "KisClient", "KiwoomClient", "NhClient", "DbClient",
|
|
61
|
+
"LsClient", "TossClient", "KbClient",
|
|
62
|
+
"ConformanceReport", "ConformanceScenario", "verify_broker_conformance",
|
|
63
|
+
"StrategyEngine", "TradingGuard", "BracketMonitor", "OrderExecutor", "RiskGuard", "MarketCalendar", "pnl_report",
|
|
64
|
+
"TradingEnvironment", "parse_symbol", "symbol_code", "symbols_match",
|
|
65
|
+
"Strategy", "StrategySpec", "StrategyContext", "Signal", "Buy", "Sell", "Cancel", "TickTrigger",
|
|
66
|
+
"StreamChannel", "TradeTick", "OrderBookLevel", "OrderBookTick", "OrderEvent", "OrderEventType", "normalize_order_id",
|
|
67
|
+
"OrderBookListener", "OrderEventListener",
|
|
68
|
+
"KisMarketStream", "KiwoomMarketStream", "NhMarketStream", "DbMarketStream", "LsMarketStream", "TossMarketStream",
|
|
69
|
+
"Account", "BrokerCapabilities", "Candle", "CandleInterval", "CreateOrderRequest",
|
|
70
|
+
"Fill", "Holding", "MarketDay", "Order", "OrderSide", "OrderStatus", "OrderType", "Quote", "TimeInForce",
|
|
71
|
+
"BrokerApiError", "AuthError", "RateLimitError", "MarketClosedError",
|
|
72
|
+
"InsufficientFundsError", "InvalidOrderError", "OrderNotFoundError",
|
|
73
|
+
]
|