kiwoom-cli 1.1.0__tar.gz → 1.1.2__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.
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/PKG-INFO +1 -1
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/__init__.py +1 -1
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/commands/account.py +6 -6
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/commands/order.py +69 -37
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/config.py +2 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli.egg-info/PKG-INFO +1 -1
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli.egg-info/SOURCES.txt +1 -0
- kiwoom_cli-1.1.2/tests/test_auth.py +30 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/tests/test_client.py +66 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/LICENSE +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/README.md +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/api_spec.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/auth.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/client.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/commands/__init__.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/commands/_constants.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/commands/dashboard.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/commands/market.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/commands/stock.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/commands/stream.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/commands/watch.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/formatters.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/main.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/output.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/secure_store.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli/streaming.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli.egg-info/dependency_links.txt +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli.egg-info/entry_points.txt +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli.egg-info/requires.txt +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/kiwoom_cli.egg-info/top_level.txt +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/pyproject.toml +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/setup.cfg +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/tests/test_account.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/tests/test_cli.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/tests/test_formatters.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/tests/test_formatters_property.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/tests/test_market.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/tests/test_migration.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/tests/test_order.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/tests/test_secure_store.py +0 -0
- {kiwoom_cli-1.1.0 → kiwoom_cli-1.1.2}/tests/test_stock.py +0 -0
|
@@ -44,12 +44,12 @@ def account_list():
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
@account.command("balance")
|
|
47
|
-
@click.option("--exchange", "
|
|
47
|
+
@click.option("--exchange", "dmst_stex_tp", default="KRX", type=click.Choice(["KRX", "NXT"]), help="거래소 구분")
|
|
48
48
|
@click.option("--delist", "qry_tp", default="0", type=click.Choice(["0", "1"]), help="상장폐지조회구분 (0=전체, 1=제외)")
|
|
49
|
-
def balance(
|
|
49
|
+
def balance(dmst_stex_tp: str, qry_tp: str):
|
|
50
50
|
"""계좌 평가현황 (잔고, 보유종목, 손익). (kt00004)"""
|
|
51
51
|
with KiwoomClient() as c:
|
|
52
|
-
data, _ = c.request("kt00004", {"qry_tp": qry_tp, "dmst_stex_tp":
|
|
52
|
+
data, _ = c.request("kt00004", {"qry_tp": qry_tp, "dmst_stex_tp": dmst_stex_tp})
|
|
53
53
|
print_account_eval(data)
|
|
54
54
|
|
|
55
55
|
|
|
@@ -98,11 +98,11 @@ def returns():
|
|
|
98
98
|
|
|
99
99
|
|
|
100
100
|
@returns.command("summary")
|
|
101
|
-
@click.option("--exchange", "
|
|
102
|
-
def returns_summary(
|
|
101
|
+
@click.option("--exchange", "stex_tp", default="all", type=click.Choice(["all", "KRX", "NXT"]), help="거래소구분")
|
|
102
|
+
def returns_summary(stex_tp: str):
|
|
103
103
|
"""계좌 수익률 조회. (ka10085)"""
|
|
104
104
|
with KiwoomClient() as c:
|
|
105
|
-
data, _ = c.request("ka10085", {"stex_tp": EXCHANGE_ALL_ZERO[
|
|
105
|
+
data, _ = c.request("ka10085", {"stex_tp": EXCHANGE_ALL_ZERO[stex_tp]})
|
|
106
106
|
print_generic_table(data, title="계좌 수익률")
|
|
107
107
|
|
|
108
108
|
|
|
@@ -49,7 +49,7 @@ def _order_type_help() -> str:
|
|
|
49
49
|
return "주문유형:\n" + "\n".join(lines)
|
|
50
50
|
|
|
51
51
|
|
|
52
|
-
def _show_order_preview(action: str, code: str, qty: int, price: int, order_type: str,
|
|
52
|
+
def _show_order_preview(action: str, code: str, qty: int, price: int, order_type: str, dmst_stex_tp: str | None = None) -> None:
|
|
53
53
|
price_str = f"{price:,}원" if price else "시장가"
|
|
54
54
|
body = (
|
|
55
55
|
f"[bold]{action} 주문[/]\n\n"
|
|
@@ -58,8 +58,8 @@ def _show_order_preview(action: str, code: str, qty: int, price: int, order_type
|
|
|
58
58
|
f" 가격: {price_str}\n"
|
|
59
59
|
f" 유형: {order_type}"
|
|
60
60
|
)
|
|
61
|
-
if
|
|
62
|
-
body += f"\n 거래소: {
|
|
61
|
+
if dmst_stex_tp is not None:
|
|
62
|
+
body += f"\n 거래소: {dmst_stex_tp}"
|
|
63
63
|
console.print(Panel(
|
|
64
64
|
body,
|
|
65
65
|
title="주문 확인",
|
|
@@ -67,6 +67,38 @@ def _show_order_preview(action: str, code: str, qty: int, price: int, order_type
|
|
|
67
67
|
))
|
|
68
68
|
|
|
69
69
|
|
|
70
|
+
def _show_modify_preview(action: str, orig_ord_no: str, code: str, qty: int, price: int, dmst_stex_tp: str | None = None) -> None:
|
|
71
|
+
lines = [
|
|
72
|
+
f" 원주문번호: {orig_ord_no}",
|
|
73
|
+
f" 종목코드: {code}",
|
|
74
|
+
f" 수량: {qty:,}",
|
|
75
|
+
f" 가격: {price:,}원",
|
|
76
|
+
]
|
|
77
|
+
if dmst_stex_tp is not None:
|
|
78
|
+
lines.append(f" 거래소: {dmst_stex_tp}")
|
|
79
|
+
console.print(Panel(
|
|
80
|
+
f"[bold]{action} 주문[/]\n\n" + "\n".join(lines),
|
|
81
|
+
title="주문 확인",
|
|
82
|
+
border_style="yellow",
|
|
83
|
+
))
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _show_cancel_preview(action: str, orig_ord_no: str, code: str, qty: int, dmst_stex_tp: str | None = None) -> None:
|
|
87
|
+
qty_str = f"{qty:,}" if qty else "전량"
|
|
88
|
+
lines = [
|
|
89
|
+
f" 원주문번호: {orig_ord_no}",
|
|
90
|
+
f" 종목코드: {code}",
|
|
91
|
+
f" 수량: {qty_str}",
|
|
92
|
+
]
|
|
93
|
+
if dmst_stex_tp is not None:
|
|
94
|
+
lines.append(f" 거래소: {dmst_stex_tp}")
|
|
95
|
+
console.print(Panel(
|
|
96
|
+
f"[bold]{action} 주문[/]\n\n" + "\n".join(lines),
|
|
97
|
+
title="주문 확인",
|
|
98
|
+
border_style="yellow",
|
|
99
|
+
))
|
|
100
|
+
|
|
101
|
+
|
|
70
102
|
# ════════════════════════════════════════════════════════
|
|
71
103
|
# Top-level order group
|
|
72
104
|
# ════════════════════════════════════════════════════════
|
|
@@ -86,10 +118,10 @@ def order():
|
|
|
86
118
|
@click.argument("qty", type=int)
|
|
87
119
|
@click.option("--price", type=int, default=0, help="주문가격 (시장가 주문시 생략)")
|
|
88
120
|
@click.option("--type", "order_type", default="market", type=click.Choice(list(ORDER_TYPES.keys())), help="주문유형")
|
|
89
|
-
@click.option("--exchange", "
|
|
121
|
+
@click.option("--exchange", "dmst_stex_tp", default="KRX", type=click.Choice(["KRX", "NXT", "SOR"]), help="거래소")
|
|
90
122
|
@click.option("--cond-price", "cond_uv", type=int, default=0, help="조건부가격 (스톱지정가 등)")
|
|
91
123
|
@click.option("--confirm", is_flag=True, help="확인 프롬프트 없이 주문 실행")
|
|
92
|
-
def buy(code: str, qty: int, price: int, order_type: str,
|
|
124
|
+
def buy(code: str, qty: int, price: int, order_type: str, dmst_stex_tp: str, cond_uv: int, confirm: bool):
|
|
93
125
|
"""주식 매수주문 (kt10000).
|
|
94
126
|
|
|
95
127
|
예: kiwoom order buy 005930 10 --price 70000 --type limit --confirm
|
|
@@ -97,11 +129,11 @@ def buy(code: str, qty: int, price: int, order_type: str, stex: str, cond_uv: in
|
|
|
97
129
|
if not confirm:
|
|
98
130
|
click.confirm("주문을 실행하시겠습니까?", abort=True)
|
|
99
131
|
|
|
100
|
-
_show_order_preview("매수", code, qty, price, order_type,
|
|
132
|
+
_show_order_preview("매수", code, qty, price, order_type, dmst_stex_tp)
|
|
101
133
|
|
|
102
134
|
with KiwoomClient() as c:
|
|
103
135
|
data, _ = c.request("kt10000", {
|
|
104
|
-
"dmst_stex_tp":
|
|
136
|
+
"dmst_stex_tp": dmst_stex_tp,
|
|
105
137
|
"stk_cd": code,
|
|
106
138
|
"ord_qty": str(qty),
|
|
107
139
|
"ord_uv": str(price) if price else "",
|
|
@@ -116,10 +148,10 @@ def buy(code: str, qty: int, price: int, order_type: str, stex: str, cond_uv: in
|
|
|
116
148
|
@click.argument("qty", type=int)
|
|
117
149
|
@click.option("--price", type=int, default=0, help="주문가격 (시장가 주문시 생략)")
|
|
118
150
|
@click.option("--type", "order_type", default="market", type=click.Choice(list(ORDER_TYPES.keys())), help="주문유형")
|
|
119
|
-
@click.option("--exchange", "
|
|
151
|
+
@click.option("--exchange", "dmst_stex_tp", default="KRX", type=click.Choice(["KRX", "NXT", "SOR"]), help="거래소")
|
|
120
152
|
@click.option("--cond-price", "cond_uv", type=int, default=0, help="조건부가격 (스톱지정가 등)")
|
|
121
153
|
@click.option("--confirm", is_flag=True, help="확인 프롬프트 없이 주문 실행")
|
|
122
|
-
def sell(code: str, qty: int, price: int, order_type: str,
|
|
154
|
+
def sell(code: str, qty: int, price: int, order_type: str, dmst_stex_tp: str, cond_uv: int, confirm: bool):
|
|
123
155
|
"""주식 매도주문 (kt10001).
|
|
124
156
|
|
|
125
157
|
예: kiwoom order sell 005930 10 --type market --confirm
|
|
@@ -127,11 +159,11 @@ def sell(code: str, qty: int, price: int, order_type: str, stex: str, cond_uv: i
|
|
|
127
159
|
if not confirm:
|
|
128
160
|
click.confirm("주문을 실행하시겠습니까?", abort=True)
|
|
129
161
|
|
|
130
|
-
_show_order_preview("매도", code, qty, price, order_type,
|
|
162
|
+
_show_order_preview("매도", code, qty, price, order_type, dmst_stex_tp)
|
|
131
163
|
|
|
132
164
|
with KiwoomClient() as c:
|
|
133
165
|
data, _ = c.request("kt10001", {
|
|
134
|
-
"dmst_stex_tp":
|
|
166
|
+
"dmst_stex_tp": dmst_stex_tp,
|
|
135
167
|
"stk_cd": code,
|
|
136
168
|
"ord_qty": str(qty),
|
|
137
169
|
"ord_uv": str(price) if price else "",
|
|
@@ -146,10 +178,10 @@ def sell(code: str, qty: int, price: int, order_type: str, stex: str, cond_uv: i
|
|
|
146
178
|
@click.argument("code")
|
|
147
179
|
@click.argument("qty", type=int)
|
|
148
180
|
@click.argument("price", type=int)
|
|
149
|
-
@click.option("--exchange", "
|
|
181
|
+
@click.option("--exchange", "dmst_stex_tp", default="KRX", type=click.Choice(["KRX", "NXT", "SOR"]), help="거래소")
|
|
150
182
|
@click.option("--cond-price", "mdfy_cond_uv", type=int, default=0, help="정정 조건부가격")
|
|
151
183
|
@click.option("--confirm", is_flag=True, help="확인 프롬프트 없이 주문 실행")
|
|
152
|
-
def modify(orig_order_no: str, code: str, qty: int, price: int,
|
|
184
|
+
def modify(orig_order_no: str, code: str, qty: int, price: int, dmst_stex_tp: str, mdfy_cond_uv: int, confirm: bool):
|
|
153
185
|
"""주식 정정주문 (kt10002).
|
|
154
186
|
|
|
155
187
|
예: kiwoom order modify 0000139 005930 1 70000 --confirm
|
|
@@ -157,11 +189,11 @@ def modify(orig_order_no: str, code: str, qty: int, price: int, stex: str, mdfy_
|
|
|
157
189
|
if not confirm:
|
|
158
190
|
click.confirm("주문을 실행하시겠습니까?", abort=True)
|
|
159
191
|
|
|
160
|
-
|
|
192
|
+
_show_modify_preview("정정", orig_order_no, code, qty, price, dmst_stex_tp)
|
|
161
193
|
|
|
162
194
|
with KiwoomClient() as c:
|
|
163
195
|
data, _ = c.request("kt10002", {
|
|
164
|
-
"dmst_stex_tp":
|
|
196
|
+
"dmst_stex_tp": dmst_stex_tp,
|
|
165
197
|
"orig_ord_no": orig_order_no,
|
|
166
198
|
"stk_cd": code,
|
|
167
199
|
"mdfy_qty": str(qty),
|
|
@@ -175,9 +207,9 @@ def modify(orig_order_no: str, code: str, qty: int, price: int, stex: str, mdfy_
|
|
|
175
207
|
@click.argument("orig_order_no")
|
|
176
208
|
@click.argument("code")
|
|
177
209
|
@click.option("--qty", type=int, default=0, help="취소수량 (0=전량취소)")
|
|
178
|
-
@click.option("--exchange", "
|
|
210
|
+
@click.option("--exchange", "dmst_stex_tp", default="KRX", type=click.Choice(["KRX", "NXT", "SOR"]), help="거래소")
|
|
179
211
|
@click.option("--confirm", is_flag=True, help="확인 프롬프트 없이 주문 실행")
|
|
180
|
-
def cancel(orig_order_no: str, code: str, qty: int,
|
|
212
|
+
def cancel(orig_order_no: str, code: str, qty: int, dmst_stex_tp: str, confirm: bool):
|
|
181
213
|
"""주식 취소주문 (kt10003).
|
|
182
214
|
|
|
183
215
|
예: kiwoom order cancel 0000140 005930 --confirm
|
|
@@ -185,11 +217,11 @@ def cancel(orig_order_no: str, code: str, qty: int, stex: str, confirm: bool):
|
|
|
185
217
|
if not confirm:
|
|
186
218
|
click.confirm("주문을 실행하시겠습니까?", abort=True)
|
|
187
219
|
|
|
188
|
-
|
|
220
|
+
_show_cancel_preview("취소", orig_order_no, code, qty, dmst_stex_tp)
|
|
189
221
|
|
|
190
222
|
with KiwoomClient() as c:
|
|
191
223
|
data, _ = c.request("kt10003", {
|
|
192
|
-
"dmst_stex_tp":
|
|
224
|
+
"dmst_stex_tp": dmst_stex_tp,
|
|
193
225
|
"orig_ord_no": orig_order_no,
|
|
194
226
|
"stk_cd": code,
|
|
195
227
|
"cncl_qty": str(qty),
|
|
@@ -212,10 +244,10 @@ def credit():
|
|
|
212
244
|
@click.argument("qty", type=int)
|
|
213
245
|
@click.option("--price", type=int, default=0, help="주문가격 (시장가 주문시 생략)")
|
|
214
246
|
@click.option("--type", "order_type", default="market", type=click.Choice(list(ORDER_TYPES.keys())), help="주문유형")
|
|
215
|
-
@click.option("--exchange", "
|
|
247
|
+
@click.option("--exchange", "dmst_stex_tp", default="KRX", type=click.Choice(["KRX", "NXT", "SOR"]), help="거래소")
|
|
216
248
|
@click.option("--cond-price", "cond_uv", type=int, default=0, help="조건부가격")
|
|
217
249
|
@click.option("--confirm", is_flag=True, help="확인 프롬프트 없이 주문 실행")
|
|
218
|
-
def credit_buy(code: str, qty: int, price: int, order_type: str,
|
|
250
|
+
def credit_buy(code: str, qty: int, price: int, order_type: str, dmst_stex_tp: str, cond_uv: int, confirm: bool):
|
|
219
251
|
"""신용 매수주문 (kt10006).
|
|
220
252
|
|
|
221
253
|
예: kiwoom order credit buy 005930 10 --type limit --price 70000 --confirm
|
|
@@ -223,11 +255,11 @@ def credit_buy(code: str, qty: int, price: int, order_type: str, stex: str, cond
|
|
|
223
255
|
if not confirm:
|
|
224
256
|
click.confirm("주문을 실행하시겠습니까?", abort=True)
|
|
225
257
|
|
|
226
|
-
_show_order_preview("신용 매수", code, qty, price, order_type,
|
|
258
|
+
_show_order_preview("신용 매수", code, qty, price, order_type, dmst_stex_tp)
|
|
227
259
|
|
|
228
260
|
with KiwoomClient() as c:
|
|
229
261
|
data, _ = c.request("kt10006", {
|
|
230
|
-
"dmst_stex_tp":
|
|
262
|
+
"dmst_stex_tp": dmst_stex_tp,
|
|
231
263
|
"stk_cd": code,
|
|
232
264
|
"ord_qty": str(qty),
|
|
233
265
|
"ord_uv": str(price) if price else "",
|
|
@@ -242,10 +274,10 @@ def credit_buy(code: str, qty: int, price: int, order_type: str, stex: str, cond
|
|
|
242
274
|
@click.argument("qty", type=int)
|
|
243
275
|
@click.option("--price", type=int, default=0, help="주문가격 (시장가 주문시 생략)")
|
|
244
276
|
@click.option("--type", "order_type", default="market", type=click.Choice(list(ORDER_TYPES.keys())), help="주문유형")
|
|
245
|
-
@click.option("--exchange", "
|
|
277
|
+
@click.option("--exchange", "dmst_stex_tp", default="KRX", type=click.Choice(["KRX", "NXT", "SOR"]), help="거래소")
|
|
246
278
|
@click.option("--cond-price", "cond_uv", type=int, default=0, help="조건부가격")
|
|
247
279
|
@click.option("--confirm", is_flag=True, help="확인 프롬프트 없이 주문 실행")
|
|
248
|
-
def credit_sell(code: str, qty: int, price: int, order_type: str,
|
|
280
|
+
def credit_sell(code: str, qty: int, price: int, order_type: str, dmst_stex_tp: str, cond_uv: int, confirm: bool):
|
|
249
281
|
"""신용 매도주문 (kt10007).
|
|
250
282
|
|
|
251
283
|
예: kiwoom order credit sell 005930 10 --type market --confirm
|
|
@@ -253,11 +285,11 @@ def credit_sell(code: str, qty: int, price: int, order_type: str, stex: str, con
|
|
|
253
285
|
if not confirm:
|
|
254
286
|
click.confirm("주문을 실행하시겠습니까?", abort=True)
|
|
255
287
|
|
|
256
|
-
_show_order_preview("신용 매도", code, qty, price, order_type,
|
|
288
|
+
_show_order_preview("신용 매도", code, qty, price, order_type, dmst_stex_tp)
|
|
257
289
|
|
|
258
290
|
with KiwoomClient() as c:
|
|
259
291
|
data, _ = c.request("kt10007", {
|
|
260
|
-
"dmst_stex_tp":
|
|
292
|
+
"dmst_stex_tp": dmst_stex_tp,
|
|
261
293
|
"stk_cd": code,
|
|
262
294
|
"ord_qty": str(qty),
|
|
263
295
|
"ord_uv": str(price) if price else "",
|
|
@@ -272,10 +304,10 @@ def credit_sell(code: str, qty: int, price: int, order_type: str, stex: str, con
|
|
|
272
304
|
@click.argument("code")
|
|
273
305
|
@click.argument("qty", type=int)
|
|
274
306
|
@click.argument("price", type=int)
|
|
275
|
-
@click.option("--exchange", "
|
|
307
|
+
@click.option("--exchange", "dmst_stex_tp", default="KRX", type=click.Choice(["KRX", "NXT", "SOR"]), help="거래소")
|
|
276
308
|
@click.option("--cond-price", "mdfy_cond_uv", type=int, default=0, help="정정 조건부가격")
|
|
277
309
|
@click.option("--confirm", is_flag=True, help="확인 프롬프트 없이 주문 실행")
|
|
278
|
-
def credit_modify(orig_order_no: str, code: str, qty: int, price: int,
|
|
310
|
+
def credit_modify(orig_order_no: str, code: str, qty: int, price: int, dmst_stex_tp: str, mdfy_cond_uv: int, confirm: bool):
|
|
279
311
|
"""신용 정정주문 (kt10008).
|
|
280
312
|
|
|
281
313
|
예: kiwoom order credit modify 0000139 005930 1 70000 --confirm
|
|
@@ -283,11 +315,11 @@ def credit_modify(orig_order_no: str, code: str, qty: int, price: int, stex: str
|
|
|
283
315
|
if not confirm:
|
|
284
316
|
click.confirm("주문을 실행하시겠습니까?", abort=True)
|
|
285
317
|
|
|
286
|
-
|
|
318
|
+
_show_modify_preview("신용 정정", orig_order_no, code, qty, price, dmst_stex_tp)
|
|
287
319
|
|
|
288
320
|
with KiwoomClient() as c:
|
|
289
321
|
data, _ = c.request("kt10008", {
|
|
290
|
-
"dmst_stex_tp":
|
|
322
|
+
"dmst_stex_tp": dmst_stex_tp,
|
|
291
323
|
"orig_ord_no": orig_order_no,
|
|
292
324
|
"stk_cd": code,
|
|
293
325
|
"mdfy_qty": str(qty),
|
|
@@ -301,9 +333,9 @@ def credit_modify(orig_order_no: str, code: str, qty: int, price: int, stex: str
|
|
|
301
333
|
@click.argument("orig_order_no")
|
|
302
334
|
@click.argument("code")
|
|
303
335
|
@click.option("--qty", type=int, default=0, help="취소수량 (0=전량취소)")
|
|
304
|
-
@click.option("--exchange", "
|
|
336
|
+
@click.option("--exchange", "dmst_stex_tp", default="KRX", type=click.Choice(["KRX", "NXT", "SOR"]), help="거래소")
|
|
305
337
|
@click.option("--confirm", is_flag=True, help="확인 프롬프트 없이 주문 실행")
|
|
306
|
-
def credit_cancel(orig_order_no: str, code: str, qty: int,
|
|
338
|
+
def credit_cancel(orig_order_no: str, code: str, qty: int, dmst_stex_tp: str, confirm: bool):
|
|
307
339
|
"""신용 취소주문 (kt10009).
|
|
308
340
|
|
|
309
341
|
예: kiwoom order credit cancel 0000140 005930 --confirm
|
|
@@ -311,11 +343,11 @@ def credit_cancel(orig_order_no: str, code: str, qty: int, stex: str, confirm: b
|
|
|
311
343
|
if not confirm:
|
|
312
344
|
click.confirm("주문을 실행하시겠습니까?", abort=True)
|
|
313
345
|
|
|
314
|
-
|
|
346
|
+
_show_cancel_preview("신용 취소", orig_order_no, code, qty, dmst_stex_tp)
|
|
315
347
|
|
|
316
348
|
with KiwoomClient() as c:
|
|
317
349
|
data, _ = c.request("kt10009", {
|
|
318
|
-
"dmst_stex_tp":
|
|
350
|
+
"dmst_stex_tp": dmst_stex_tp,
|
|
319
351
|
"orig_ord_no": orig_order_no,
|
|
320
352
|
"stk_cd": code,
|
|
321
353
|
"cncl_qty": str(qty),
|
|
@@ -399,7 +431,7 @@ def gold_modify(orig_order_no: str, code: str, qty: int, price: int, confirm: bo
|
|
|
399
431
|
if not confirm:
|
|
400
432
|
click.confirm("주문을 실행하시겠습니까?", abort=True)
|
|
401
433
|
|
|
402
|
-
|
|
434
|
+
_show_modify_preview("금현물 정정", orig_order_no, code, qty, price)
|
|
403
435
|
|
|
404
436
|
with KiwoomClient() as c:
|
|
405
437
|
data, _ = c.request("kt50002", {
|
|
@@ -424,7 +456,7 @@ def gold_cancel(orig_order_no: str, code: str, qty: int, confirm: bool):
|
|
|
424
456
|
if not confirm:
|
|
425
457
|
click.confirm("주문을 실행하시겠습니까?", abort=True)
|
|
426
458
|
|
|
427
|
-
|
|
459
|
+
_show_cancel_preview("금현물 취소", orig_order_no, code, qty)
|
|
428
460
|
|
|
429
461
|
with KiwoomClient() as c:
|
|
430
462
|
data, _ = c.request("kt50003", {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Tests for kiwoom_cli.auth — keyring-backed token storage."""
|
|
2
|
+
|
|
3
|
+
import keyring
|
|
4
|
+
|
|
5
|
+
from kiwoom_cli import auth
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_save_token_stores_in_keyring():
|
|
9
|
+
auth.save_token("abc123", profile="test-profile")
|
|
10
|
+
assert keyring.get_password(auth.KEYRING_SERVICE, "test-profile:token") == "abc123"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_load_token_returns_stored_value():
|
|
14
|
+
auth.save_token("xyz789", profile="test-profile")
|
|
15
|
+
assert auth.load_token(profile="test-profile") == "xyz789"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_load_token_returns_none_when_missing():
|
|
19
|
+
assert auth.load_token(profile="nonexistent") is None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_delete_token_removes_stored_value():
|
|
23
|
+
auth.save_token("to-delete", profile="test-profile")
|
|
24
|
+
assert auth.load_token(profile="test-profile") == "to-delete"
|
|
25
|
+
auth.delete_token(profile="test-profile")
|
|
26
|
+
assert auth.load_token(profile="test-profile") is None
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_delete_token_missing_key_is_noop():
|
|
30
|
+
auth.delete_token(profile="never-existed") # must not raise
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import httpx
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
+
from kiwoom_cli import client as client_mod
|
|
7
8
|
from kiwoom_cli.client import KiwoomClient, KiwoomAPIError
|
|
8
9
|
|
|
9
10
|
|
|
@@ -89,3 +90,68 @@ def test_request_all_paginates(mock_client):
|
|
|
89
90
|
assert len(results) == 2
|
|
90
91
|
assert results[0]["page"] == 1
|
|
91
92
|
assert results[1]["page"] == 2
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_request_all_respects_max_pages(mock_client):
|
|
96
|
+
client, httpx_mock = mock_client
|
|
97
|
+
for i in range(2):
|
|
98
|
+
httpx_mock.add_response(
|
|
99
|
+
url="https://mock.test/api/dostk/stkinfo",
|
|
100
|
+
json={"page": i, "return_code": 0},
|
|
101
|
+
headers={"cont-yn": "Y", "next-key": f"key{i+1}"},
|
|
102
|
+
)
|
|
103
|
+
results = client.request_all("ka10001", {"stk_cd": "005930"}, max_pages=2)
|
|
104
|
+
assert len(results) == 2
|
|
105
|
+
assert results[0]["page"] == 0
|
|
106
|
+
assert results[1]["page"] == 1
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def test_client_init_loads_from_config_and_auth(monkeypatch):
|
|
110
|
+
monkeypatch.setattr(client_mod.config, "get_domain", lambda profile=None: "https://mock.test")
|
|
111
|
+
monkeypatch.setattr(client_mod.auth, "load_token", lambda profile=None: "stored-token")
|
|
112
|
+
c = client_mod.KiwoomClient()
|
|
113
|
+
try:
|
|
114
|
+
assert c.domain == "https://mock.test"
|
|
115
|
+
assert c.token == "stored-token"
|
|
116
|
+
finally:
|
|
117
|
+
c.close()
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_issue_token_saves_and_returns(mock_client, monkeypatch):
|
|
121
|
+
client, httpx_mock = mock_client
|
|
122
|
+
saved = {}
|
|
123
|
+
monkeypatch.setattr(
|
|
124
|
+
client_mod.auth, "save_token",
|
|
125
|
+
lambda t, profile=None: saved.update({"token": t, "profile": profile}),
|
|
126
|
+
)
|
|
127
|
+
httpx_mock.add_response(
|
|
128
|
+
url="https://mock.test/oauth2/token",
|
|
129
|
+
json={"token": "new-token-xyz", "return_code": 0},
|
|
130
|
+
)
|
|
131
|
+
token = client.issue_token(appkey="ak123", secretkey="sk456")
|
|
132
|
+
assert token == "new-token-xyz"
|
|
133
|
+
assert client.token == "new-token-xyz"
|
|
134
|
+
assert saved["token"] == "new-token-xyz"
|
|
135
|
+
req = httpx_mock.get_request()
|
|
136
|
+
body = req.content.decode()
|
|
137
|
+
assert "ak123" in body and "sk456" in body
|
|
138
|
+
assert "client_credentials" in body
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_revoke_token_clears_state(mock_client, monkeypatch):
|
|
142
|
+
client, httpx_mock = mock_client
|
|
143
|
+
monkeypatch.setattr(client_mod.config, "get_appkey", lambda profile=None: "ak123")
|
|
144
|
+
monkeypatch.setattr(client_mod.config, "get_secretkey", lambda profile=None: "sk456")
|
|
145
|
+
deleted = {}
|
|
146
|
+
monkeypatch.setattr(
|
|
147
|
+
client_mod.auth, "delete_token",
|
|
148
|
+
lambda profile=None: deleted.update({"called": True, "profile": profile}),
|
|
149
|
+
)
|
|
150
|
+
httpx_mock.add_response(
|
|
151
|
+
url="https://mock.test/oauth2/revoke",
|
|
152
|
+
json={"return_code": 0},
|
|
153
|
+
)
|
|
154
|
+
client.revoke_token()
|
|
155
|
+
assert client.token is None
|
|
156
|
+
assert deleted["called"] is True
|
|
157
|
+
assert deleted["profile"] is None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|