kwcli 0.1.0__py3-none-any.whl
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.
- kiwoom/__init__.py +47 -0
- kiwoom/_data/kiwoom_api_spec.json +66372 -0
- kiwoom/core/__init__.py +6 -0
- kiwoom/core/auth.py +349 -0
- kiwoom/core/client.py +244 -0
- kiwoom/core/errors.py +177 -0
- kiwoom/core/platform_paths.py +68 -0
- kiwoom/core/profiles.py +143 -0
- kiwoom/core/runtime.py +153 -0
- kiwoom/core/secrets.py +217 -0
- kiwoom/core/settings.py +64 -0
- kiwoom/core/token_store.py +186 -0
- kiwoom/core/types.py +28 -0
- kiwoom/core/ws_client.py +262 -0
- kiwoom/realtime/__init__.py +30 -0
- kiwoom/realtime/decoders.py +171 -0
- kiwoom/realtime/events.py +98 -0
- kiwoom/realtime/packets.py +65 -0
- kiwoom/realtime/schemas.py +76 -0
- kiwoom/realtime/stream.py +213 -0
- kiwoom/specs.py +272 -0
- kiwoom_cli/README.md +671 -0
- kiwoom_cli/__init__.py +9 -0
- kiwoom_cli/__main__.py +5 -0
- kiwoom_cli/argument_maps.py +561 -0
- kiwoom_cli/arguments.py +147 -0
- kiwoom_cli/auth_context.py +64 -0
- kiwoom_cli/banner.py +125 -0
- kiwoom_cli/commands/__init__.py +1 -0
- kiwoom_cli/commands/auth.py +406 -0
- kiwoom_cli/commands/groups.py +281 -0
- kiwoom_cli/commands/mapped.py +74 -0
- kiwoom_cli/commands/orders.py +158 -0
- kiwoom_cli/commands/spec.py +98 -0
- kiwoom_cli/commands/stocks.py +100 -0
- kiwoom_cli/commands/streams.py +470 -0
- kiwoom_cli/doctor.py +324 -0
- kiwoom_cli/errors.py +24 -0
- kiwoom_cli/executor/__init__.py +33 -0
- kiwoom_cli/executor/condition.py +374 -0
- kiwoom_cli/executor/rest.py +132 -0
- kiwoom_cli/executor/waits.py +34 -0
- kiwoom_cli/executor/websocket.py +131 -0
- kiwoom_cli/main.py +203 -0
- kiwoom_cli/maps/README.md +99 -0
- kiwoom_cli/maps/api_commands.csv +209 -0
- kiwoom_cli/maps/arguments.csv +731 -0
- kiwoom_cli/maps/order_confirmation_commands.csv +13 -0
- kiwoom_cli/maps/order_confirmation_fields.csv +71 -0
- kiwoom_cli/maps/order_price_policies.csv +47 -0
- kiwoom_cli/maps/order_value_labels.csv +28 -0
- kiwoom_cli/maps/positional_arguments.csv +21 -0
- kiwoom_cli/order_confirmation.py +167 -0
- kiwoom_cli/output.py +136 -0
- kiwoom_cli/registry.py +95 -0
- kiwoom_cli/safety.py +30 -0
- kiwoom_cli/setup.py +533 -0
- kwcli-0.1.0.dist-info/METADATA +215 -0
- kwcli-0.1.0.dist-info/RECORD +62 -0
- kwcli-0.1.0.dist-info/WHEEL +4 -0
- kwcli-0.1.0.dist-info/entry_points.txt +2 -0
- kwcli-0.1.0.dist-info/licenses/LICENSE.md +36 -0
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
"""Table-driven registration for pure mapped command groups.
|
|
2
|
+
|
|
3
|
+
Each ``MappedGroup`` row declares one market/group CLI subtree; registration
|
|
4
|
+
order stays explicit in ``main.build_parser``. Groups with bespoke behavior
|
|
5
|
+
(stocks, orders, streams, auth, spec) keep their own modules.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
|
|
13
|
+
from kiwoom_cli.commands.mapped import add_mapped_command_parsers, execute_mapped_rest_command
|
|
14
|
+
from kiwoom_cli.output import ACCOUNT_REDACT_FIELDS
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class MappedGroup:
|
|
19
|
+
market: str
|
|
20
|
+
name: str
|
|
21
|
+
help_text: str
|
|
22
|
+
commands: tuple[tuple[str, str], ...]
|
|
23
|
+
redact_fields: tuple[str, ...] = field(default=())
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def cli_group(self) -> str:
|
|
27
|
+
"""The ``cli_group`` key used in maps/api_commands.csv."""
|
|
28
|
+
if self.market == "domestic":
|
|
29
|
+
return self.name
|
|
30
|
+
return f"{self.market} {self.name}"
|
|
31
|
+
|
|
32
|
+
@property
|
|
33
|
+
def dest(self) -> str:
|
|
34
|
+
prefix = "" if self.market == "domestic" else f"{self.market}_"
|
|
35
|
+
return f"{prefix}{self.name.replace('-', '_')}_command"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def add_group_parser(subparsers: argparse._SubParsersAction, group: MappedGroup) -> None:
|
|
39
|
+
group_parser = subparsers.add_parser(group.name, help=group.help_text)
|
|
40
|
+
group_subparsers = group_parser.add_subparsers(dest=group.dest, required=True)
|
|
41
|
+
add_mapped_command_parsers(
|
|
42
|
+
group_subparsers,
|
|
43
|
+
group=group.cli_group,
|
|
44
|
+
commands=group.commands,
|
|
45
|
+
handler=_make_handler(group),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _make_handler(group: MappedGroup):
|
|
50
|
+
def handler(args: argparse.Namespace) -> None:
|
|
51
|
+
execute_mapped_rest_command(
|
|
52
|
+
group=group.cli_group,
|
|
53
|
+
command=getattr(args, group.dest),
|
|
54
|
+
args=args,
|
|
55
|
+
redact_fields=group.redact_fields,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return handler
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
DOMESTIC_GROUPS: dict[str, MappedGroup] = {
|
|
62
|
+
group.name: group
|
|
63
|
+
for group in (
|
|
64
|
+
MappedGroup(
|
|
65
|
+
market="domestic",
|
|
66
|
+
name="quotes",
|
|
67
|
+
help_text="현재가와 시세 조회 명령입니다.",
|
|
68
|
+
commands=(
|
|
69
|
+
("price", "종목 현재가 시세를 조회합니다."),
|
|
70
|
+
("balance", "프로그램매매 차익잔고 추이를 조회합니다."),
|
|
71
|
+
("by-stock", "종목별 기관 매매 추이를 조회합니다."),
|
|
72
|
+
("list", "신주인수권 전체 시세를 조회합니다."),
|
|
73
|
+
("gold-price", "금현물 시세를 조회합니다."),
|
|
74
|
+
("gold-fills", "금현물 체결 추이를 조회합니다."),
|
|
75
|
+
("gold-daily", "금현물 일별 추이를 조회합니다."),
|
|
76
|
+
("gold-expected", "금현물 예상체결을 조회합니다."),
|
|
77
|
+
("multi-period", "주식 일/주/월/시/분 요약 시세를 조회합니다."),
|
|
78
|
+
("intraday-minute", "주식 시분 시세를 조회합니다."),
|
|
79
|
+
("institution-daily", "일별 기관 매매 종목을 조회합니다."),
|
|
80
|
+
("strength-time", "체결강도 시간별 추이를 조회합니다."),
|
|
81
|
+
("strength-daily", "체결강도 일별 추이를 조회합니다."),
|
|
82
|
+
("investor-intraday", "장중 투자자별 매매를 조회합니다."),
|
|
83
|
+
("investor-after-close", "장마감 후 투자자별 매매를 조회합니다."),
|
|
84
|
+
("broker-trend", "증권사별 종목 매매 동향을 조회합니다."),
|
|
85
|
+
("daily-price", "일별 주가를 조회합니다."),
|
|
86
|
+
("after-hours", "시간외 단일가를 조회합니다."),
|
|
87
|
+
("program-time", "프로그램매매 시간대별 추이를 조회합니다."),
|
|
88
|
+
("program-cumulative", "프로그램매매 누적 추이를 조회합니다."),
|
|
89
|
+
("program-by-stock", "종목 시간별 프로그램매매 추이를 조회합니다."),
|
|
90
|
+
("program-daily", "프로그램매매 일자별 추이를 조회합니다."),
|
|
91
|
+
("stock-program-daily", "종목 일별 프로그램매매 추이를 조회합니다."),
|
|
92
|
+
),
|
|
93
|
+
),
|
|
94
|
+
MappedGroup(
|
|
95
|
+
market="domestic",
|
|
96
|
+
name="orderbooks",
|
|
97
|
+
help_text="호가 조회 명령입니다.",
|
|
98
|
+
commands=(
|
|
99
|
+
("list", "종목 호가를 조회합니다."),
|
|
100
|
+
("gold", "금현물 호가를 조회합니다."),
|
|
101
|
+
),
|
|
102
|
+
),
|
|
103
|
+
MappedGroup(
|
|
104
|
+
market="domestic",
|
|
105
|
+
name="candles",
|
|
106
|
+
help_text="차트/캔들 조회 명령입니다.",
|
|
107
|
+
commands=(
|
|
108
|
+
("daily", "종목 일봉 차트를 조회합니다."),
|
|
109
|
+
("by-stock", "종목별 투자자/기관 차트를 조회합니다."),
|
|
110
|
+
("lookup", "장중 투자자별 매매 차트를 조회합니다."),
|
|
111
|
+
("stock-tick", "주식 틱 차트를 조회합니다."),
|
|
112
|
+
("stock-minute", "주식 분봉 차트를 조회합니다."),
|
|
113
|
+
("stock-weekly", "주식 주봉 차트를 조회합니다."),
|
|
114
|
+
("stock-monthly", "주식 월봉 차트를 조회합니다."),
|
|
115
|
+
("stock-yearly", "주식 년봉 차트를 조회합니다."),
|
|
116
|
+
("sector-tick", "업종 틱 차트를 조회합니다."),
|
|
117
|
+
("sector-minute", "업종 분봉 차트를 조회합니다."),
|
|
118
|
+
("sector-daily", "업종 일봉 차트를 조회합니다."),
|
|
119
|
+
("sector-weekly", "업종 주봉 차트를 조회합니다."),
|
|
120
|
+
("sector-monthly", "업종 월봉 차트를 조회합니다."),
|
|
121
|
+
("sector-yearly", "업종 년봉 차트를 조회합니다."),
|
|
122
|
+
("gold-tick", "금현물 틱 차트를 조회합니다."),
|
|
123
|
+
("gold-minute", "금현물 분봉 차트를 조회합니다."),
|
|
124
|
+
("gold-daily", "금현물 일봉 차트를 조회합니다."),
|
|
125
|
+
("gold-weekly", "금현물 주봉 차트를 조회합니다."),
|
|
126
|
+
("gold-monthly", "금현물 월봉 차트를 조회합니다."),
|
|
127
|
+
("gold-today-tick", "금현물 당일 틱 차트를 조회합니다."),
|
|
128
|
+
("gold-today-minute", "금현물 당일 분봉 차트를 조회합니다."),
|
|
129
|
+
),
|
|
130
|
+
),
|
|
131
|
+
MappedGroup(
|
|
132
|
+
market="domestic",
|
|
133
|
+
name="etfs",
|
|
134
|
+
help_text="ETF 조회 명령입니다.",
|
|
135
|
+
commands=(
|
|
136
|
+
("info", "ETF 종목정보를 조회합니다."),
|
|
137
|
+
("daily", "ETF 일별추이를 조회합니다."),
|
|
138
|
+
("profit", "ETF 수익률을 조회합니다."),
|
|
139
|
+
("list", "ETF 전체 시세를 조회합니다."),
|
|
140
|
+
("intraday-trend", "ETF 시간대별 추이를 조회합니다."),
|
|
141
|
+
("intraday-fills", "ETF 시간대별 체결을 조회합니다."),
|
|
142
|
+
("daily-fills", "ETF 일자별 체결을 조회합니다."),
|
|
143
|
+
("nav", "ETF NAV 관련 정보를 조회합니다."),
|
|
144
|
+
("foreign-trend", "ETF 외국인 순매수 추이를 조회합니다."),
|
|
145
|
+
),
|
|
146
|
+
),
|
|
147
|
+
MappedGroup(
|
|
148
|
+
market="domestic",
|
|
149
|
+
name="elws",
|
|
150
|
+
help_text="ELW 조회 명령입니다.",
|
|
151
|
+
commands=(
|
|
152
|
+
("daily", "ELW 일별 민감도 지표를 조회합니다."),
|
|
153
|
+
("balance", "ELW LP 보유 일별 추이를 조회합니다."),
|
|
154
|
+
("conditions", "ELW 조건검색 결과를 조회합니다."),
|
|
155
|
+
("sensitivity", "ELW 민감도 지표를 조회합니다."),
|
|
156
|
+
("price-move", "ELW 가격 급등락을 조회합니다."),
|
|
157
|
+
("broker-net", "거래원별 ELW 순매매 상위를 조회합니다."),
|
|
158
|
+
("divergence", "ELW 괴리율을 조회합니다."),
|
|
159
|
+
("change-rank", "ELW 등락율 순위를 조회합니다."),
|
|
160
|
+
("balance-rank", "ELW 잔량 순위를 조회합니다."),
|
|
161
|
+
("proximity", "ELW 근접율을 조회합니다."),
|
|
162
|
+
("details", "ELW 종목 상세정보를 조회합니다."),
|
|
163
|
+
),
|
|
164
|
+
),
|
|
165
|
+
MappedGroup(
|
|
166
|
+
market="domestic",
|
|
167
|
+
name="investors",
|
|
168
|
+
help_text="기관/외국인 조회 명령입니다.",
|
|
169
|
+
commands=(
|
|
170
|
+
("by-stock", "종목별 외국인 매매동향을 조회합니다."),
|
|
171
|
+
("trend", "기관/외국인 연속매매 현황을 조회합니다."),
|
|
172
|
+
("gold-status", "금현물 투자자 현황을 조회합니다."),
|
|
173
|
+
),
|
|
174
|
+
),
|
|
175
|
+
MappedGroup(
|
|
176
|
+
market="domestic",
|
|
177
|
+
name="rankings",
|
|
178
|
+
help_text="순위 정보 조회 명령입니다.",
|
|
179
|
+
commands=(
|
|
180
|
+
("orderbook-balance", "호가잔량 상위를 조회합니다."),
|
|
181
|
+
("orderbook-balance-spike", "호가잔량 급증을 조회합니다."),
|
|
182
|
+
("balance-rate-spike", "잔량율 급증을 조회합니다."),
|
|
183
|
+
("volume-spike", "거래량 급증을 조회합니다."),
|
|
184
|
+
("previous-change-rate", "전일대비 등락률 상위를 조회합니다."),
|
|
185
|
+
("list-fills", "예상체결 등락률 상위를 조회합니다."),
|
|
186
|
+
("today-volume", "당일 거래량 상위를 조회합니다."),
|
|
187
|
+
("previous-volume", "전일 거래량 상위를 조회합니다."),
|
|
188
|
+
("amount", "거래대금 상위를 조회합니다."),
|
|
189
|
+
("credit-ratio", "신용비율 상위를 조회합니다."),
|
|
190
|
+
("foreign-period-trades", "외인 기간별 매매 상위를 조회합니다."),
|
|
191
|
+
("foreign-continuous-net", "외인 연속 순매매 상위를 조회합니다."),
|
|
192
|
+
("foreign-limit-usage", "외인 한도소진율 증가 상위를 조회합니다."),
|
|
193
|
+
("foreign-broker-trades", "외국계 창구 매매 상위를 조회합니다."),
|
|
194
|
+
("broker-by-stock", "종목별 증권사 순위를 조회합니다."),
|
|
195
|
+
("broker-trades", "증권사별 매매 상위를 조회합니다."),
|
|
196
|
+
("stock-main-brokers", "당일 주요 거래원을 조회합니다."),
|
|
197
|
+
("net-buy-brokers", "순매수 거래원 순위를 조회합니다."),
|
|
198
|
+
("top-exit-brokers", "당일 상위 이탈원을 조회합니다."),
|
|
199
|
+
("same-net-trades", "동일 순매매 순위를 조회합니다."),
|
|
200
|
+
("investor-intraday", "장중 투자자별 매매 상위를 조회합니다."),
|
|
201
|
+
("after-hours-change-rate", "시간외 단일가 등락율 순위를 조회합니다."),
|
|
202
|
+
("foreign-institution-trades", "외국인기관 매매 상위를 조회합니다."),
|
|
203
|
+
),
|
|
204
|
+
),
|
|
205
|
+
MappedGroup(
|
|
206
|
+
market="domestic",
|
|
207
|
+
name="sectors",
|
|
208
|
+
help_text="업종 조회 명령입니다.",
|
|
209
|
+
commands=(
|
|
210
|
+
("program", "종목 기준 업종 프로그램 매매를 조회합니다."),
|
|
211
|
+
("investor-flows", "업종별 투자자 순매수를 조회합니다."),
|
|
212
|
+
("price", "업종 현재가를 조회합니다."),
|
|
213
|
+
("stocks", "업종별 주가를 조회합니다."),
|
|
214
|
+
("indices", "전업종 지수를 조회합니다."),
|
|
215
|
+
("daily", "업종 현재가 일별 데이터를 조회합니다."),
|
|
216
|
+
),
|
|
217
|
+
),
|
|
218
|
+
MappedGroup(
|
|
219
|
+
market="domestic",
|
|
220
|
+
name="short-selling",
|
|
221
|
+
help_text="공매도 조회 명령입니다.",
|
|
222
|
+
commands=(("trend", "종목별 공매도 추이를 조회합니다."),),
|
|
223
|
+
),
|
|
224
|
+
MappedGroup(
|
|
225
|
+
market="domestic",
|
|
226
|
+
name="securities-lending",
|
|
227
|
+
help_text="대차거래 조회 명령입니다.",
|
|
228
|
+
commands=(
|
|
229
|
+
("by-stock", "종목별 대차거래 추이를 조회합니다."),
|
|
230
|
+
("trend", "대차거래 추이를 조회합니다."),
|
|
231
|
+
("list", "대차거래 상위 종목을 조회합니다."),
|
|
232
|
+
("lookup", "대차거래 내역을 조회합니다."),
|
|
233
|
+
),
|
|
234
|
+
),
|
|
235
|
+
MappedGroup(
|
|
236
|
+
market="domestic",
|
|
237
|
+
name="themes",
|
|
238
|
+
help_text="테마 조회 명령입니다.",
|
|
239
|
+
commands=(
|
|
240
|
+
("by-stock", "테마 구성 종목을 조회합니다."),
|
|
241
|
+
("lookup", "테마 그룹을 조회합니다."),
|
|
242
|
+
),
|
|
243
|
+
),
|
|
244
|
+
MappedGroup(
|
|
245
|
+
market="domestic",
|
|
246
|
+
name="accounts",
|
|
247
|
+
help_text="계좌 조회 명령입니다.",
|
|
248
|
+
commands=(
|
|
249
|
+
("list", "계좌번호 목록을 조회합니다."),
|
|
250
|
+
("daily-balance-return", "일별잔고수익률을 조회합니다."),
|
|
251
|
+
("realized-profit-stock-daily", "일자별 종목별 실현손익을 일자 기준으로 조회합니다."),
|
|
252
|
+
("realized-profit-period-stock", "일자별 종목별 실현손익을 기간 기준으로 조회합니다."),
|
|
253
|
+
("realized-profit-daily", "일자별 실현손익을 조회합니다."),
|
|
254
|
+
("realized-profit-today-detail", "당일 실현손익 상세를 조회합니다."),
|
|
255
|
+
("return-rate", "계좌수익률을 조회합니다."),
|
|
256
|
+
("day-trading-log", "당일매매일지를 조회합니다."),
|
|
257
|
+
("cash", "예수금 상세 현황을 조회합니다."),
|
|
258
|
+
("estimated-assets-daily", "일별 추정예탁자산 현황을 조회합니다."),
|
|
259
|
+
("assets", "추정자산을 조회합니다."),
|
|
260
|
+
("valuation", "계좌평가 현황을 조회합니다."),
|
|
261
|
+
("fill-balance", "체결잔고를 조회합니다."),
|
|
262
|
+
("order-fill-detail", "계좌별 주문체결내역 상세를 조회합니다."),
|
|
263
|
+
("next-settlement", "계좌별 익일결제 예정내역을 조회합니다."),
|
|
264
|
+
("order-fill-status", "계좌별 주문체결 현황을 조회합니다."),
|
|
265
|
+
("credit-margin", "신용보증금율별 주문가능수량을 조회합니다."),
|
|
266
|
+
("margin-details", "증거금 세부내역을 조회합니다."),
|
|
267
|
+
("transaction-history", "위탁종합 거래내역을 조회합니다."),
|
|
268
|
+
("daily-return-detail", "일별 계좌수익률 상세현황을 조회합니다."),
|
|
269
|
+
("today-status", "계좌별 당일현황을 조회합니다."),
|
|
270
|
+
("holdings", "계좌평가 잔고내역을 조회합니다."),
|
|
271
|
+
("gold-balance", "금현물 잔고를 조회합니다."),
|
|
272
|
+
("gold-cash", "금현물 예수금을 조회합니다."),
|
|
273
|
+
("gold-all-order-fills", "금현물 주문체결 전체를 조회합니다."),
|
|
274
|
+
("gold-order-fills", "금현물 주문체결을 조회합니다."),
|
|
275
|
+
("gold-transactions", "금현물 거래내역을 조회합니다."),
|
|
276
|
+
("gold-open-orders", "금현물 미체결을 조회합니다."),
|
|
277
|
+
),
|
|
278
|
+
redact_fields=ACCOUNT_REDACT_FIELDS,
|
|
279
|
+
),
|
|
280
|
+
)
|
|
281
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Shared helpers for mapped resource command modules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from collections.abc import Callable, Iterable
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from kiwoom_cli.argument_maps import add_mapped_arguments, build_body
|
|
10
|
+
from kiwoom_cli.arguments import add_output_format_arg, add_pages_arg, add_runtime_args
|
|
11
|
+
from kiwoom_cli.executor import execute_rest_command
|
|
12
|
+
from kiwoom_cli.output import format_response
|
|
13
|
+
from kiwoom_cli.registry import get_implemented_command
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
CommandHelp = tuple[str, str]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def add_mapped_command_parser(
|
|
20
|
+
subparsers: argparse._SubParsersAction,
|
|
21
|
+
*,
|
|
22
|
+
group: str,
|
|
23
|
+
command: str,
|
|
24
|
+
help_text: str,
|
|
25
|
+
handler: Callable[[argparse.Namespace], None],
|
|
26
|
+
parser_kwargs: dict[str, Any] | None = None,
|
|
27
|
+
) -> argparse.ArgumentParser:
|
|
28
|
+
definition = get_implemented_command(group, command)
|
|
29
|
+
kwargs = {"help": help_text}
|
|
30
|
+
if parser_kwargs:
|
|
31
|
+
kwargs.update(parser_kwargs)
|
|
32
|
+
command_parser = subparsers.add_parser(command, **kwargs)
|
|
33
|
+
add_mapped_arguments(command_parser, definition.command_path)
|
|
34
|
+
add_pages_arg(command_parser)
|
|
35
|
+
add_output_format_arg(command_parser)
|
|
36
|
+
add_runtime_args(command_parser)
|
|
37
|
+
command_parser.set_defaults(handler=handler)
|
|
38
|
+
return command_parser
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def add_mapped_command_parsers(
|
|
42
|
+
subparsers: argparse._SubParsersAction,
|
|
43
|
+
*,
|
|
44
|
+
group: str,
|
|
45
|
+
commands: Iterable[CommandHelp],
|
|
46
|
+
handler: Callable[[argparse.Namespace], None],
|
|
47
|
+
) -> None:
|
|
48
|
+
for command, help_text in commands:
|
|
49
|
+
add_mapped_command_parser(
|
|
50
|
+
subparsers,
|
|
51
|
+
group=group,
|
|
52
|
+
command=command,
|
|
53
|
+
help_text=help_text,
|
|
54
|
+
handler=handler,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def execute_mapped_rest_command(
|
|
59
|
+
*,
|
|
60
|
+
group: str,
|
|
61
|
+
command: str,
|
|
62
|
+
args: argparse.Namespace,
|
|
63
|
+
redact_fields: Iterable[str] = (),
|
|
64
|
+
) -> None:
|
|
65
|
+
definition = get_implemented_command(group, command)
|
|
66
|
+
body = build_body(definition.command_path, args)
|
|
67
|
+
response = execute_rest_command(
|
|
68
|
+
definition,
|
|
69
|
+
body=body,
|
|
70
|
+
mode=args.mode,
|
|
71
|
+
profile=args.profile,
|
|
72
|
+
pages=getattr(args, "pages", 1),
|
|
73
|
+
)
|
|
74
|
+
print(format_response(response, output_format=args.format, redact_fields=redact_fields))
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""Order commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
from kiwoom_cli.argument_maps import add_mapped_arguments, build_body
|
|
9
|
+
from kiwoom_cli.arguments import add_output_format_arg, add_runtime_args
|
|
10
|
+
from kiwoom_cli.commands.mapped import (
|
|
11
|
+
add_mapped_command_parser,
|
|
12
|
+
execute_mapped_rest_command,
|
|
13
|
+
)
|
|
14
|
+
from kiwoom_cli.executor import execute_rest_command
|
|
15
|
+
from kiwoom_cli.order_confirmation import (
|
|
16
|
+
build_order_confirmation,
|
|
17
|
+
format_order_confirmation,
|
|
18
|
+
)
|
|
19
|
+
from kiwoom_cli.output import ACCOUNT_REDACT_FIELDS, format_response
|
|
20
|
+
from kiwoom_cli.registry import get_implemented_command
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class OrdersMarket:
|
|
25
|
+
group: str
|
|
26
|
+
dest: str
|
|
27
|
+
help_text: str
|
|
28
|
+
confirm_help: str
|
|
29
|
+
reads: tuple[tuple[str, str], ...]
|
|
30
|
+
writes: tuple[tuple[str, str], ...]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
DOMESTIC_ORDER_READS = (
|
|
34
|
+
("chance", "주문 전 주문/인출 가능금액을 조회합니다."),
|
|
35
|
+
("margin", "증거금율별 주문가능수량을 조회합니다."),
|
|
36
|
+
("list-open", "미체결 주문을 조회합니다."),
|
|
37
|
+
("list-fills", "체결 주문을 조회합니다."),
|
|
38
|
+
("open-detail", "미체결 분할주문 상세를 조회합니다."),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
DOMESTIC_ORDER_WRITES = (
|
|
42
|
+
("buy", "매수 주문을 전송합니다 (--confirm 없으면 미전송 주문 확인)."),
|
|
43
|
+
("sell", "매도 주문을 전송합니다 (--confirm 없으면 미전송 주문 확인)."),
|
|
44
|
+
("modify", "정정 주문을 전송합니다 (--confirm 없으면 미전송 주문 확인)."),
|
|
45
|
+
("cancel", "취소 주문을 전송합니다 (--confirm 없으면 미전송 주문 확인)."),
|
|
46
|
+
(
|
|
47
|
+
"credit-buy",
|
|
48
|
+
"신용 매수주문을 전송합니다 (--confirm 없으면 미전송 주문 확인).",
|
|
49
|
+
),
|
|
50
|
+
(
|
|
51
|
+
"credit-sell",
|
|
52
|
+
"신용 매도주문을 전송합니다 (--confirm 없으면 미전송 주문 확인).",
|
|
53
|
+
),
|
|
54
|
+
(
|
|
55
|
+
"credit-modify",
|
|
56
|
+
"신용 정정주문을 전송합니다 (--confirm 없으면 미전송 주문 확인).",
|
|
57
|
+
),
|
|
58
|
+
(
|
|
59
|
+
"credit-cancel",
|
|
60
|
+
"신용 취소주문을 전송합니다 (--confirm 없으면 미전송 주문 확인).",
|
|
61
|
+
),
|
|
62
|
+
(
|
|
63
|
+
"gold-buy",
|
|
64
|
+
"금현물 매수주문을 전송합니다 (--confirm 없으면 미전송 주문 확인).",
|
|
65
|
+
),
|
|
66
|
+
(
|
|
67
|
+
"gold-sell",
|
|
68
|
+
"금현물 매도주문을 전송합니다 (--confirm 없으면 미전송 주문 확인).",
|
|
69
|
+
),
|
|
70
|
+
(
|
|
71
|
+
"gold-modify",
|
|
72
|
+
"금현물 정정주문을 전송합니다 (--confirm 없으면 미전송 주문 확인).",
|
|
73
|
+
),
|
|
74
|
+
(
|
|
75
|
+
"gold-cancel",
|
|
76
|
+
"금현물 취소주문을 전송합니다 (--confirm 없으면 미전송 주문 확인).",
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
ORDER_MARKETS: dict[str, OrdersMarket] = {
|
|
81
|
+
"domestic": OrdersMarket(
|
|
82
|
+
group="orders",
|
|
83
|
+
dest="orders_command",
|
|
84
|
+
help_text="주문 조회 및 주문 전송(--confirm) 명령입니다.",
|
|
85
|
+
confirm_help="지정하면 실제 주문을 전송합니다. 미지정 시 미전송 주문 확인을 출력합니다.",
|
|
86
|
+
reads=DOMESTIC_ORDER_READS,
|
|
87
|
+
writes=DOMESTIC_ORDER_WRITES,
|
|
88
|
+
),
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def add_orders_parser(
|
|
93
|
+
subparsers: argparse._SubParsersAction, market: str = "domestic"
|
|
94
|
+
) -> None:
|
|
95
|
+
market_table = ORDER_MARKETS[market]
|
|
96
|
+
orders_parser = subparsers.add_parser("orders", help=market_table.help_text)
|
|
97
|
+
orders_subparsers = orders_parser.add_subparsers(
|
|
98
|
+
dest=market_table.dest, required=True
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
for command, help_text in market_table.reads:
|
|
102
|
+
command_parser = add_mapped_command_parser(
|
|
103
|
+
orders_subparsers,
|
|
104
|
+
group=market_table.group,
|
|
105
|
+
command=command,
|
|
106
|
+
help_text=help_text,
|
|
107
|
+
handler=handle_order_read,
|
|
108
|
+
)
|
|
109
|
+
command_parser.set_defaults(
|
|
110
|
+
order_command=command, orders_group=market_table.group
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
for command, help_text in market_table.writes:
|
|
114
|
+
definition = get_implemented_command(market_table.group, command)
|
|
115
|
+
command_parser = orders_subparsers.add_parser(command, help=help_text)
|
|
116
|
+
add_mapped_arguments(command_parser, definition.command_path)
|
|
117
|
+
command_parser.add_argument(
|
|
118
|
+
"--confirm",
|
|
119
|
+
action="store_true",
|
|
120
|
+
help=market_table.confirm_help,
|
|
121
|
+
)
|
|
122
|
+
add_output_format_arg(command_parser)
|
|
123
|
+
add_runtime_args(command_parser)
|
|
124
|
+
command_parser.set_defaults(
|
|
125
|
+
handler=handle_order_submit,
|
|
126
|
+
order_command=command,
|
|
127
|
+
orders_group=market_table.group,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def handle_order_read(args: argparse.Namespace) -> None:
|
|
132
|
+
execute_mapped_rest_command(
|
|
133
|
+
group=args.orders_group,
|
|
134
|
+
command=args.order_command,
|
|
135
|
+
args=args,
|
|
136
|
+
redact_fields=ACCOUNT_REDACT_FIELDS,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def handle_order_submit(args: argparse.Namespace) -> None:
|
|
141
|
+
definition = get_implemented_command(args.orders_group, args.order_command)
|
|
142
|
+
body = build_body(definition.command_path, args)
|
|
143
|
+
if not args.confirm:
|
|
144
|
+
payload = build_order_confirmation(definition, args)
|
|
145
|
+
print(format_order_confirmation(payload, output_format=args.format))
|
|
146
|
+
return
|
|
147
|
+
response = execute_rest_command(
|
|
148
|
+
definition,
|
|
149
|
+
body=body,
|
|
150
|
+
mode=args.mode,
|
|
151
|
+
profile=args.profile,
|
|
152
|
+
confirm=True,
|
|
153
|
+
)
|
|
154
|
+
print(
|
|
155
|
+
format_response(
|
|
156
|
+
response, output_format=args.format, redact_fields=ACCOUNT_REDACT_FIELDS
|
|
157
|
+
)
|
|
158
|
+
)
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Spec search commands."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
|
|
7
|
+
from kiwoom.specs import (
|
|
8
|
+
format_api_groups,
|
|
9
|
+
format_api_spec,
|
|
10
|
+
format_api_summaries,
|
|
11
|
+
format_search_results,
|
|
12
|
+
get_api_spec,
|
|
13
|
+
list_api_groups,
|
|
14
|
+
list_api_summaries,
|
|
15
|
+
search_api_specs,
|
|
16
|
+
)
|
|
17
|
+
from kiwoom_cli.output import format_payload
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def add_spec_parser(subparsers: argparse._SubParsersAction) -> None:
|
|
21
|
+
spec_parser = subparsers.add_parser("spec", help="API 스펙을 검색합니다.")
|
|
22
|
+
spec_subparsers = spec_parser.add_subparsers(dest="spec_command", required=True)
|
|
23
|
+
|
|
24
|
+
search_parser = spec_subparsers.add_parser(
|
|
25
|
+
"search", help="API ID, 이름, 요청 필드로 스펙을 검색합니다."
|
|
26
|
+
)
|
|
27
|
+
search_parser.add_argument("query", help="검색어")
|
|
28
|
+
search_parser.add_argument("--limit", type=_positive_int, default=10, help="최대 결과 수")
|
|
29
|
+
search_parser.set_defaults(handler=handle_spec_search)
|
|
30
|
+
|
|
31
|
+
show_parser = spec_subparsers.add_parser("show", help="API ID로 스펙 상세를 표시합니다.")
|
|
32
|
+
show_parser.add_argument("api_id", help="API ID")
|
|
33
|
+
show_parser.add_argument(
|
|
34
|
+
"--format", choices=("pretty", "json", "yaml"), default="pretty", help="출력 형식"
|
|
35
|
+
)
|
|
36
|
+
show_parser.set_defaults(handler=handle_spec_show)
|
|
37
|
+
|
|
38
|
+
groups_parser = spec_subparsers.add_parser("groups", help="API 그룹과 API 수를 표시합니다.")
|
|
39
|
+
groups_parser.add_argument(
|
|
40
|
+
"--format", choices=("pretty", "json", "yaml"), default="pretty", help="출력 형식"
|
|
41
|
+
)
|
|
42
|
+
groups_parser.set_defaults(handler=handle_spec_groups)
|
|
43
|
+
|
|
44
|
+
apis_parser = spec_subparsers.add_parser("apis", help="API 목록을 표시합니다.")
|
|
45
|
+
apis_parser.add_argument("--group", help="메뉴 경로에 포함될 그룹명")
|
|
46
|
+
apis_parser.add_argument("--limit", type=_positive_int, help="최대 결과 수")
|
|
47
|
+
apis_parser.add_argument(
|
|
48
|
+
"--format", choices=("pretty", "json", "yaml"), default="pretty", help="출력 형식"
|
|
49
|
+
)
|
|
50
|
+
apis_parser.set_defaults(handler=handle_spec_apis)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def handle_spec_search(args: argparse.Namespace) -> None:
|
|
54
|
+
results = search_api_specs(args.query, limit=args.limit)
|
|
55
|
+
print(format_search_results(results))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def handle_spec_show(args: argparse.Namespace) -> None:
|
|
59
|
+
api_payload = get_api_spec(args.api_id)
|
|
60
|
+
print(
|
|
61
|
+
_format_spec_payload(
|
|
62
|
+
api_payload, output_format=args.format, pretty_formatter=format_api_spec
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def handle_spec_groups(args: argparse.Namespace) -> None:
|
|
68
|
+
groups = list_api_groups()
|
|
69
|
+
print(
|
|
70
|
+
_format_spec_payload(
|
|
71
|
+
groups, output_format=args.format, pretty_formatter=format_api_groups
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def handle_spec_apis(args: argparse.Namespace) -> None:
|
|
77
|
+
apis = list_api_summaries(group=args.group, limit=args.limit)
|
|
78
|
+
print(
|
|
79
|
+
_format_spec_payload(
|
|
80
|
+
apis, output_format=args.format, pretty_formatter=format_api_summaries
|
|
81
|
+
)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _format_spec_payload(payload, *, output_format: str, pretty_formatter) -> str:
|
|
86
|
+
if output_format == "pretty":
|
|
87
|
+
return pretty_formatter(payload)
|
|
88
|
+
return format_payload(payload, output_format=output_format)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _positive_int(value: str) -> int:
|
|
92
|
+
try:
|
|
93
|
+
parsed = int(value)
|
|
94
|
+
except ValueError as exc:
|
|
95
|
+
raise argparse.ArgumentTypeError("--limit 값은 정수여야 합니다.") from exc
|
|
96
|
+
if parsed <= 0:
|
|
97
|
+
raise argparse.ArgumentTypeError("--limit 값은 0보다 커야 합니다.")
|
|
98
|
+
return parsed
|