ksxt 0.0.8__py3-none-any.whl → 0.0.9__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.
- ksxt/__init__.py +3 -1
- ksxt/__pycache__/__init__.cpython-312.pyc +0 -0
- ksxt/__pycache__/bithumb.cpython-312.pyc +0 -0
- ksxt/__pycache__/koreainvest.cpython-312.pyc +0 -0
- ksxt/__pycache__/upbit.cpython-312.pyc +0 -0
- ksxt/api/__init__.py +26 -0
- ksxt/api/__pycache__/__init__.cpython-312.pyc +0 -0
- ksxt/api/__pycache__/bithumb.cpython-312.pyc +0 -0
- ksxt/api/__pycache__/koreainvest.cpython-312.pyc +0 -0
- ksxt/api/__pycache__/upbit.cpython-312.pyc +0 -0
- ksxt/api/auto/api_generator.py +54 -0
- ksxt/api/auto/bithumb.py +35 -0
- ksxt/api/auto/koreainvest.py +49 -0
- ksxt/api/auto/upbit.py +39 -0
- ksxt/api/bithumb.py +42 -0
- ksxt/api/koreainvest.py +40 -0
- ksxt/api/upbit.py +54 -0
- ksxt/async_/__init__.py +4 -0
- ksxt/async_/__pycache__/__init__.cpython-312.pyc +0 -0
- ksxt/async_/__pycache__/bithumb.cpython-312.pyc +0 -0
- ksxt/async_/__pycache__/koreainvest.cpython-312.pyc +0 -0
- ksxt/async_/__pycache__/upbit.cpython-312.pyc +0 -0
- ksxt/async_/base/__init__.py +0 -0
- ksxt/async_/base/__pycache__/__init__.cpython-312.pyc +0 -0
- ksxt/async_/base/__pycache__/async_exchange.cpython-312.pyc +0 -0
- ksxt/async_/base/__pycache__/throttler.cpython-312.pyc +0 -0
- ksxt/async_/base/async_exchange.py +232 -0
- ksxt/async_/base/throttler.py +63 -0
- ksxt/async_/bithumb.py +455 -0
- ksxt/async_/koreainvest.py +849 -0
- ksxt/async_/upbit.py +488 -0
- ksxt/base/__pycache__/__init__.cpython-312.pyc +0 -0
- ksxt/base/__pycache__/errors.cpython-312.pyc +0 -0
- ksxt/base/__pycache__/exchange.cpython-312.pyc +0 -0
- ksxt/base/__pycache__/rest_exchange.cpython-312.pyc +0 -0
- ksxt/base/__pycache__/types.cpython-312.pyc +0 -0
- ksxt/base/com_exchange.py +2 -2
- ksxt/base/errors.py +10 -0
- ksxt/base/exchange.py +188 -497
- ksxt/base/rest_exchange.py +297 -113
- ksxt/base/types.py +1 -36
- ksxt/bithumb.py +504 -0
- ksxt/config/__init__.py +2 -1
- ksxt/config/__pycache__/__init__.cpython-312.pyc +0 -0
- ksxt/config/bithumb.toml +380 -0
- ksxt/config/koreainvest.toml +312 -0
- ksxt/config/token.toml +7 -0
- ksxt/config/upbit.toml +428 -0
- ksxt/koreainvest.py +409 -1055
- ksxt/market/__pycache__/base.cpython-312.pyc +0 -0
- ksxt/market/__pycache__/db.cpython-312.pyc +0 -0
- ksxt/market/__pycache__/logging.cpython-312.pyc +0 -0
- ksxt/market/__pycache__/manager.cpython-312.pyc +0 -0
- ksxt/market/__pycache__/markets.cpython-312.pyc +0 -0
- ksxt/market/base.py +50 -50
- ksxt/market/db.py +5 -4
- ksxt/market/krx/__pycache__/kosdaq.cpython-312.pyc +0 -0
- ksxt/market/krx/__pycache__/kospi.cpython-312.pyc +0 -0
- ksxt/market/krx/__pycache__/stock.cpython-312.pyc +0 -0
- ksxt/market/krx/kosdaq.py +150 -147
- ksxt/market/krx/kospi.py +179 -175
- ksxt/market/krx/stock.py +136 -134
- ksxt/market/logging.py +4 -4
- ksxt/market/manager.py +10 -12
- ksxt/market/markets.py +1 -1
- ksxt/market/us/__pycache__/amex.cpython-312.pyc +0 -0
- ksxt/market/us/__pycache__/nasdaq.cpython-312.pyc +0 -0
- ksxt/market/us/__pycache__/nyse.cpython-312.pyc +0 -0
- ksxt/market/us/__pycache__/stock.cpython-312.pyc +0 -0
- ksxt/market/us/amex.py +31 -31
- ksxt/market/us/nasdaq.py +31 -31
- ksxt/market/us/nyse.py +31 -31
- ksxt/market/us/stock.py +20 -28
- ksxt/models/__init__.py +16 -0
- ksxt/models/__pycache__/__init__.cpython-312.pyc +0 -0
- ksxt/models/__pycache__/balance.cpython-312.pyc +0 -0
- ksxt/models/__pycache__/cash.cpython-312.pyc +0 -0
- ksxt/models/__pycache__/common.cpython-312.pyc +0 -0
- ksxt/models/__pycache__/error.cpython-312.pyc +0 -0
- ksxt/models/__pycache__/historical.cpython-312.pyc +0 -0
- ksxt/models/__pycache__/market.cpython-312.pyc +0 -0
- ksxt/models/__pycache__/order.cpython-312.pyc +0 -0
- ksxt/models/__pycache__/orderbook.cpython-312.pyc +0 -0
- ksxt/models/__pycache__/ticker.cpython-312.pyc +0 -0
- ksxt/models/__pycache__/token.cpython-312.pyc +0 -0
- ksxt/models/__pycache__/transaction.cpython-312.pyc +0 -0
- ksxt/models/balance.py +30 -0
- ksxt/models/cash.py +15 -0
- ksxt/models/common.py +31 -0
- ksxt/models/error.py +13 -0
- ksxt/models/historical.py +26 -0
- ksxt/models/market.py +81 -0
- ksxt/models/order.py +42 -0
- ksxt/models/orderbook.py +32 -0
- ksxt/models/ticker.py +25 -0
- ksxt/models/token.py +14 -0
- ksxt/models/transaction.py +79 -0
- ksxt/parser/__pycache__/bithumb.cpython-312.pyc +0 -0
- ksxt/parser/__pycache__/koreainvest.cpython-312.pyc +0 -0
- ksxt/parser/__pycache__/parser.cpython-312.pyc +0 -0
- ksxt/parser/__pycache__/upbit.cpython-312.pyc +0 -0
- ksxt/parser/bithumb.py +300 -0
- ksxt/parser/koreainvest.py +323 -0
- ksxt/parser/parser.py +114 -0
- ksxt/parser/upbit.py +308 -0
- ksxt/upbit.py +499 -0
- ksxt/utils/__pycache__/safer.cpython-312.pyc +0 -0
- ksxt/utils/__pycache__/sorter.cpython-312.pyc +0 -0
- ksxt/utils/__pycache__/timer.cpython-312.pyc +0 -0
- ksxt/utils/safer.py +48 -0
- ksxt/utils/sorter.py +8 -0
- ksxt/utils/timer.py +47 -0
- {ksxt-0.0.8.dist-info → ksxt-0.0.9.dist-info}/METADATA +11 -1
- ksxt-0.0.9.dist-info/RECORD +119 -0
- {ksxt-0.0.8.dist-info → ksxt-0.0.9.dist-info}/WHEEL +1 -1
- ksxt/__pycache__/__init__.cpython-39.pyc +0 -0
- ksxt/__pycache__/koreainvest.cpython-39.pyc +0 -0
- ksxt/base/__pycache__/__init__.cpython-39.pyc +0 -0
- ksxt/base/__pycache__/exchange.cpython-39.pyc +0 -0
- ksxt/base/__pycache__/rest_exchange.cpython-39.pyc +0 -0
- ksxt/base/__pycache__/restexchange.cpython-39.pyc +0 -0
- ksxt/base/__pycache__/types.cpython-39.pyc +0 -0
- ksxt/base/api_response.py +0 -68
- ksxt/config/__pycache__/__init__.cpython-39.pyc +0 -0
- ksxt/config/tr_app.json +0 -381
- ksxt/config/tr_dev.json +0 -446
- ksxt/market/__pycache__/base.cpython-39.pyc +0 -0
- ksxt/market/__pycache__/db.cpython-39.pyc +0 -0
- ksxt/market/__pycache__/logging.cpython-39.pyc +0 -0
- ksxt/market/__pycache__/manager.cpython-39.pyc +0 -0
- ksxt/market/__pycache__/markets.cpython-39.pyc +0 -0
- ksxt/market/krx/__pycache__/kosdaq.cpython-39.pyc +0 -0
- ksxt/market/krx/__pycache__/kospi.cpython-39.pyc +0 -0
- ksxt/market/krx/__pycache__/stock.cpython-39.pyc +0 -0
- ksxt/market/us/__pycache__/amex.cpython-39.pyc +0 -0
- ksxt/market/us/__pycache__/nasdaq.cpython-39.pyc +0 -0
- ksxt/market/us/__pycache__/nyse.cpython-39.pyc +0 -0
- ksxt/market/us/__pycache__/stock.cpython-39.pyc +0 -0
- ksxt-0.0.8.dist-info/RECORD +0 -49
- {ksxt-0.0.8.dist-info → ksxt-0.0.9.dist-info}/LICENSE.txt +0 -0
- {ksxt-0.0.8.dist-info → ksxt-0.0.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,849 @@
|
|
1
|
+
from datetime import datetime, timedelta
|
2
|
+
import json
|
3
|
+
import os
|
4
|
+
import time
|
5
|
+
from typing import Any, Dict, Optional
|
6
|
+
from ksxt.api.koreainvest import ImplicitAPI
|
7
|
+
from ksxt.async_.base.async_exchange import AsyncExchange
|
8
|
+
from ksxt.market.manager import MarketManager
|
9
|
+
from ksxt.parser.koreainvest import KoreaInvestParser
|
10
|
+
|
11
|
+
|
12
|
+
class KoreaInvest(AsyncExchange, ImplicitAPI):
|
13
|
+
def __init__(self, config: Dict = None) -> None:
|
14
|
+
super().__init__(config, "koreainvest.toml")
|
15
|
+
self.parser = KoreaInvestParser()
|
16
|
+
|
17
|
+
def safe_symbol(self, base_market: str, security: str) -> str:
|
18
|
+
return f"{security}"
|
19
|
+
|
20
|
+
def is_activate(self, path, security_type) -> bool:
|
21
|
+
mode = "dev" if self.is_dev == True else "app"
|
22
|
+
|
23
|
+
tr_id = self.apis[self.type][security_type][path][mode]["tr_id"]
|
24
|
+
|
25
|
+
if not bool(tr_id):
|
26
|
+
return False
|
27
|
+
|
28
|
+
return super().is_activate(path=path, security_type=security_type)
|
29
|
+
|
30
|
+
@AsyncExchange.check_token
|
31
|
+
def sign(
|
32
|
+
self,
|
33
|
+
path,
|
34
|
+
security_type,
|
35
|
+
method_type,
|
36
|
+
api_type: Any = "public",
|
37
|
+
headers: Optional[Any] = None,
|
38
|
+
body: Optional[Any] = None,
|
39
|
+
params: Optional[Any] = None,
|
40
|
+
config={},
|
41
|
+
):
|
42
|
+
mode = "dev" if self.is_dev == True else "app"
|
43
|
+
|
44
|
+
host_url = self.apis[self.type][mode]["hostname"]
|
45
|
+
destination = self.apis[self.type][security_type][path]["url"]
|
46
|
+
version = self.apis[self.type]["version"]
|
47
|
+
params["version"] = version
|
48
|
+
destination = self.implode_params(destination, params)
|
49
|
+
|
50
|
+
url = host_url + "/" + destination
|
51
|
+
|
52
|
+
tr_id = self.apis[self.type][security_type][path][mode]["tr_id"]
|
53
|
+
authorization_token = f"Bearer {self.token}"
|
54
|
+
custtype = ""
|
55
|
+
if "custtype" in self.apis[self.type][security_type][path]:
|
56
|
+
custtype = self.apis[self.type][security_type][path]["custtype"]
|
57
|
+
|
58
|
+
if headers is None:
|
59
|
+
headers = {}
|
60
|
+
headers.update(
|
61
|
+
{
|
62
|
+
"content-type": "application/json",
|
63
|
+
"authorization": authorization_token,
|
64
|
+
"appkey": self.open_key,
|
65
|
+
"appsecret": self.secret_key,
|
66
|
+
"tr_id": tr_id,
|
67
|
+
"custtype": custtype,
|
68
|
+
}
|
69
|
+
)
|
70
|
+
|
71
|
+
if method_type.upper() == "POST":
|
72
|
+
body = json.dumps(params)
|
73
|
+
params = {}
|
74
|
+
|
75
|
+
return {"url": url, "method": method_type, "headers": headers, "body": body, "params": params}
|
76
|
+
|
77
|
+
# region _____
|
78
|
+
def create_token(self):
|
79
|
+
import logging
|
80
|
+
|
81
|
+
mode = "dev" if self.is_dev == True else "app"
|
82
|
+
host_url = self.apis[self.type][mode]["hostname"]
|
83
|
+
destination = self.apis[self.type]["token"]["url"]
|
84
|
+
url = host_url + "/" + destination
|
85
|
+
|
86
|
+
request_headers = self.prepare_request_headers()
|
87
|
+
|
88
|
+
body = {"grant_type": "client_credentials", "appkey": self.open_key, "appsecret": self.secret_key}
|
89
|
+
|
90
|
+
body = json.dumps(body, separators=(",", ":"))
|
91
|
+
|
92
|
+
try:
|
93
|
+
res = self.fetch(url=url, method="POST", headers=request_headers, body=body)
|
94
|
+
except Exception as ex:
|
95
|
+
logging.error(ex)
|
96
|
+
return
|
97
|
+
|
98
|
+
if "access_token" in res.keys():
|
99
|
+
token = res["access_token"]
|
100
|
+
token_expired = res["access_token_token_expired"]
|
101
|
+
self.save_token(self.open_key, token, token_expired)
|
102
|
+
else:
|
103
|
+
logging.error(res["error_description"])
|
104
|
+
|
105
|
+
def get_response(self, result, request_params: dict):
|
106
|
+
if self.safe_string(result, "rt_cd") == "0":
|
107
|
+
response = self.get_success_response(result)
|
108
|
+
else:
|
109
|
+
response = self.get_error_response(result)
|
110
|
+
|
111
|
+
response = self.update_header(response, request_params)
|
112
|
+
return response
|
113
|
+
|
114
|
+
def get_success_response(self, result):
|
115
|
+
return super().get_success_response(
|
116
|
+
# 성공 실패 여부
|
117
|
+
response_code="0",
|
118
|
+
# 응답 코드
|
119
|
+
msg_code=self.safe_string(result, "msg_cd"),
|
120
|
+
# 응답 메세지
|
121
|
+
msg=self.safe_string(result, "msg1"),
|
122
|
+
# 원본 데이터
|
123
|
+
org_data=result,
|
124
|
+
)
|
125
|
+
|
126
|
+
def get_error_response(self, result):
|
127
|
+
return super().get_error_response(
|
128
|
+
error_code=self.safe_string(result, "msg_cd"),
|
129
|
+
error_message=self.safe_string(result, "msg1"),
|
130
|
+
org_data=result,
|
131
|
+
)
|
132
|
+
|
133
|
+
def update_header(self, response: dict, request_params: dict):
|
134
|
+
response = self.update_success_response_header(
|
135
|
+
response, {"request_time": time.time(), "request_params": request_params}
|
136
|
+
)
|
137
|
+
return response
|
138
|
+
|
139
|
+
# endregion ____
|
140
|
+
|
141
|
+
# region public feeder
|
142
|
+
@AsyncExchange.check_token
|
143
|
+
async def fetch_markets(self, market_name: str):
|
144
|
+
db_path = os.path.join(os.getcwd(), f".ksxt-cache_market.{datetime.now().strftime('%Y%m%d')}.db")
|
145
|
+
manager = MarketManager(db_path=db_path)
|
146
|
+
manager._init()
|
147
|
+
if market_name.lower() == "kospi":
|
148
|
+
result = manager.kospi.all()
|
149
|
+
base_market = "KRW"
|
150
|
+
elif market_name.lower() == "kosdaq":
|
151
|
+
result = manager.kosdaq.all()
|
152
|
+
base_market = "KRW"
|
153
|
+
elif market_name.lower() == "nyse":
|
154
|
+
result = manager.nyse.all()
|
155
|
+
base_market = "USD"
|
156
|
+
elif market_name.lower() == "nasdaq":
|
157
|
+
result = manager.nasdaq.all()
|
158
|
+
base_market = "USD"
|
159
|
+
elif market_name.lower() == "amex":
|
160
|
+
result = manager.amex.all()
|
161
|
+
base_market = "USD"
|
162
|
+
else:
|
163
|
+
return self.get_error_response(
|
164
|
+
error_code="market_error", error_message=f"{market_name} market is not yet supported."
|
165
|
+
)
|
166
|
+
|
167
|
+
params = {}
|
168
|
+
response = self.get_response(result, params)
|
169
|
+
if response["response"]["success"] != "0":
|
170
|
+
return response
|
171
|
+
|
172
|
+
return self.parser.parse_markets(response=response, base_market=base_market)
|
173
|
+
|
174
|
+
async def fetch_trade_fee(self, symbol: Optional[str] = "", base_market: str = "KRW"):
|
175
|
+
params = {"base_market": base_market}
|
176
|
+
|
177
|
+
result = {"rt_cd": "0", "output": {}}
|
178
|
+
response = self.get_response(result, params)
|
179
|
+
if response["response"]["success"] != "0":
|
180
|
+
return response
|
181
|
+
|
182
|
+
return self.parser.parse_trade_fee(response, base_market)
|
183
|
+
|
184
|
+
async def fetch_ticker(self, symbol: str, base_market: str = "KRW"):
|
185
|
+
if base_market == "KRW":
|
186
|
+
params = {"FID_COND_MRKT_DIV_CODE": "J", "FID_INPUT_ISCD": symbol}
|
187
|
+
result = await self.private_get_fetch_ticker_price_krw(self.extend(params))
|
188
|
+
elif base_market == "USD":
|
189
|
+
market_code = await self.get_market_code_in_feeder(symbol=symbol, base_market=base_market)
|
190
|
+
params = {"AUTH": "", "EXCD": market_code, "SYMB": symbol}
|
191
|
+
result = await self.fetchTickerForUS(self.extend(params))
|
192
|
+
else:
|
193
|
+
return self.get_error_response(
|
194
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
195
|
+
)
|
196
|
+
|
197
|
+
response = self.get_response(result, params)
|
198
|
+
if response["response"]["success"] != "0":
|
199
|
+
return response
|
200
|
+
|
201
|
+
return self.parser.parse_ticker(response, base_market)
|
202
|
+
|
203
|
+
async def fetch_historical_data(
|
204
|
+
self,
|
205
|
+
symbol: str,
|
206
|
+
time_frame: str,
|
207
|
+
start: Optional[str] = None,
|
208
|
+
end: Optional[str] = None,
|
209
|
+
base_market: str = "KRW",
|
210
|
+
):
|
211
|
+
limit = 100
|
212
|
+
|
213
|
+
if end is None:
|
214
|
+
end = KoreaInvest.now(base_market=base_market)
|
215
|
+
|
216
|
+
if start is None:
|
217
|
+
|
218
|
+
if time_frame.endswith("m"):
|
219
|
+
start = end - timedelta(minutes=30)
|
220
|
+
|
221
|
+
if base_market == "KRW":
|
222
|
+
params = {
|
223
|
+
"FID_ETC_CLS_CODE": "",
|
224
|
+
"FID_COND_MRKT_DIV_CODE": "J", # "U"
|
225
|
+
"FID_INPUT_ISCD": symbol,
|
226
|
+
"FID_INPUT_HOUR_1": start.strftime("%H%M%S"), # 업종은 조회 간격만 입력 가능
|
227
|
+
"FID_PW_DATA_INCU_YN": "N",
|
228
|
+
}
|
229
|
+
|
230
|
+
result = await self.private_get_fetch_security_ohlcv_minute_krw(self.extend(params))
|
231
|
+
|
232
|
+
elif time_frame.endswith("D"):
|
233
|
+
start = end - timedelta(days=limit)
|
234
|
+
elif time_frame.endswith("W"):
|
235
|
+
start = end - timedelta(weeks=limit)
|
236
|
+
elif time_frame.endswith("M"):
|
237
|
+
start = end - timedelta(days=limit * 30)
|
238
|
+
elif time_frame.endswith("Y"):
|
239
|
+
start = end - timedelta(days=limit * 365)
|
240
|
+
else:
|
241
|
+
start = end
|
242
|
+
|
243
|
+
if base_market == "KRW":
|
244
|
+
params = {
|
245
|
+
"FID_COND_MRKT_DIV_CODE": "J",
|
246
|
+
"FID_INPUT_ISCD": symbol,
|
247
|
+
"FID_INPUT_DATE_1": start.strftime("%Y%m%d"),
|
248
|
+
"FID_INPUT_DATE_2": end.strftime("%Y%m%d"),
|
249
|
+
"FID_PERIOD_DIV_CODE": time_frame,
|
250
|
+
"FID_ORG_ADJ_PRC": "1",
|
251
|
+
}
|
252
|
+
|
253
|
+
result = await self.private_get_fetch_security_ohlcv_krw(self.extend(params))
|
254
|
+
# index는 구분해서 날려야함
|
255
|
+
# response = self.private_get_fetch_index_ohlcv_krw(self.extend(params))
|
256
|
+
elif base_market == "USD":
|
257
|
+
if time_frame == "D":
|
258
|
+
gubn = "0"
|
259
|
+
elif time_frame == "W":
|
260
|
+
gubn = "1"
|
261
|
+
elif time_frame == "M":
|
262
|
+
gubn = "2"
|
263
|
+
else:
|
264
|
+
return self.get_error_response(
|
265
|
+
error_code="time frame error", error_message=f"{time_frame} time-frame is not supported."
|
266
|
+
)
|
267
|
+
|
268
|
+
market_code = await self.get_market_code_in_feeder(symbol=symbol, base_market=base_market)
|
269
|
+
|
270
|
+
params = {
|
271
|
+
"AUTH": "",
|
272
|
+
"EXCD": market_code,
|
273
|
+
"SYMB": symbol,
|
274
|
+
"GUBN": gubn,
|
275
|
+
"BYMD": end.strftime("%Y%m%d"),
|
276
|
+
"MODP": "1",
|
277
|
+
"KEYB": "",
|
278
|
+
}
|
279
|
+
|
280
|
+
result = await self.fetchOHLCVforUS(self.extend(params))
|
281
|
+
else:
|
282
|
+
return self.get_error_response(
|
283
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
284
|
+
)
|
285
|
+
|
286
|
+
response = self.get_response(result, params)
|
287
|
+
if response["response"]["success"] != "0":
|
288
|
+
return response
|
289
|
+
|
290
|
+
return self.parser.parse_historical_data(response, base_market)
|
291
|
+
|
292
|
+
async def fetch_is_holiday(self, dt: datetime, base_market: str = "KRW"):
|
293
|
+
if base_market == "KRW":
|
294
|
+
params = {"BASS_DT": dt.strftime("%Y%m%d"), "CTX_AREA_NK": "", "CTX_AREA_FK": ""}
|
295
|
+
result = await self.private_get_fetch_calendar_holiday_krw(self.extend(params))
|
296
|
+
|
297
|
+
response = self.get_response(result, params)
|
298
|
+
if response["response"]["success"] != "0":
|
299
|
+
return response
|
300
|
+
|
301
|
+
return self.parser.parse_is_holiday(response, base_market)
|
302
|
+
else:
|
303
|
+
return self.get_error_response(
|
304
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
305
|
+
)
|
306
|
+
|
307
|
+
# endregion public feeder
|
308
|
+
|
309
|
+
# region private feeder
|
310
|
+
# @RestExchange.check_token
|
311
|
+
async def fetch_balance(self, acc_num: str, base_market: str = "KRW"):
|
312
|
+
if base_market == "KRW":
|
313
|
+
params = {
|
314
|
+
"CANO": acc_num[:8],
|
315
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
316
|
+
"AFHR_FLPR_YN": "N",
|
317
|
+
"OFL_YN": "",
|
318
|
+
"INQR_DVSN": "01",
|
319
|
+
"UNPR_DVSN": "01",
|
320
|
+
"FUND_STTL_ICLD_YN": "N",
|
321
|
+
"FNCG_AMT_AUTO_RDPT_YN": "N",
|
322
|
+
"PRCS_DVSN": "01",
|
323
|
+
"CTX_AREA_FK100": "",
|
324
|
+
"CTX_AREA_NK100": "",
|
325
|
+
}
|
326
|
+
|
327
|
+
result = await self.private_get_fetch_balance_krw(self.extend(params))
|
328
|
+
elif base_market == "USD":
|
329
|
+
market_code = await self.get_market_code_in_feeder(symbol="ALL", base_market=base_market)
|
330
|
+
params = {
|
331
|
+
"CANO": acc_num[:8],
|
332
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
333
|
+
"OVRS_EXCG_CD": market_code,
|
334
|
+
"TR_CRCY_CD": "USD",
|
335
|
+
"CTX_AREA_FK200": "",
|
336
|
+
"CTX_AREA_NK200": "",
|
337
|
+
}
|
338
|
+
|
339
|
+
result = await self.private_get_fetch_balance_usd(self.extend(params))
|
340
|
+
else:
|
341
|
+
return self.get_error_response(
|
342
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
343
|
+
)
|
344
|
+
|
345
|
+
response = self.get_response(result, params)
|
346
|
+
if response["response"]["success"] != "0":
|
347
|
+
return response
|
348
|
+
|
349
|
+
return self.parser.parse_balance(response, base_market)
|
350
|
+
|
351
|
+
async def fetch_cash(self, acc_num: str, base_market: str = "KRW"):
|
352
|
+
if base_market == "KRW":
|
353
|
+
params = {
|
354
|
+
"CANO": acc_num[:8],
|
355
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
356
|
+
"AFHR_FLPR_YN": "N",
|
357
|
+
"OFL_YN": "",
|
358
|
+
"INQR_DVSN": "01",
|
359
|
+
"UNPR_DVSN": "01",
|
360
|
+
"FUND_STTL_ICLD_YN": "N",
|
361
|
+
"FNCG_AMT_AUTO_RDPT_YN": "N",
|
362
|
+
"PRCS_DVSN": "01",
|
363
|
+
"CTX_AREA_FK100": "",
|
364
|
+
"CTX_AREA_NK100": "",
|
365
|
+
}
|
366
|
+
|
367
|
+
result = await self.private_get_fetch_cash_krw(self.extend(params))
|
368
|
+
elif base_market == "USD":
|
369
|
+
params = {
|
370
|
+
"CANO": acc_num[:8],
|
371
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
372
|
+
# 01: 원화, 02: 외화
|
373
|
+
"WCRC_FRCR_DVSN_CD": "02",
|
374
|
+
"NATN_CD": "840",
|
375
|
+
"TR_MKET_CD": "00",
|
376
|
+
"INQR_DVSN_CD": "00",
|
377
|
+
}
|
378
|
+
|
379
|
+
result = await self.fetchCashForUS(self.extend(params))
|
380
|
+
else:
|
381
|
+
return self.get_error_response(
|
382
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
383
|
+
)
|
384
|
+
|
385
|
+
response = self.get_response(result, params)
|
386
|
+
if response["response"]["success"] != "0":
|
387
|
+
return response
|
388
|
+
|
389
|
+
return self.parser.parse_cash(response, base_market)
|
390
|
+
|
391
|
+
async def fetch_orderbook(self, symbol: str, base_market: str = "KRW"):
|
392
|
+
if base_market == "KRW":
|
393
|
+
params = {"FID_COND_MRKT_DIV_CODE": "J", "FID_INPUT_ISCD": symbol}
|
394
|
+
|
395
|
+
result = await self.private_get_fetch_orderbook_krw(self.extend(params))
|
396
|
+
elif base_market == "USD":
|
397
|
+
params = {"FID_COND_MRKT_DIV_CODE": "J", "FID_INPUT_ISCD": symbol}
|
398
|
+
|
399
|
+
result = await self.fetchCashForUS(self.extend(params))
|
400
|
+
else:
|
401
|
+
return self.get_error_response(
|
402
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
403
|
+
)
|
404
|
+
|
405
|
+
response = self.get_response(result, params)
|
406
|
+
if response["response"]["success"] != "0":
|
407
|
+
return response
|
408
|
+
|
409
|
+
return self.parser.parse_orderbook(response, base_market)
|
410
|
+
|
411
|
+
async def fetch_pnl(self, acc_num: str, include_cost: bool = True, base_market: str = "KRW"):
|
412
|
+
if base_market == "KRW":
|
413
|
+
params = {
|
414
|
+
"CANO": acc_num[:8],
|
415
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
416
|
+
"AFHR_FLPR_YN": "N",
|
417
|
+
"OFL_YN": "",
|
418
|
+
"INQR_DVSN": "00",
|
419
|
+
"UNPR_DVSN": "01",
|
420
|
+
"FUND_STTL_ICLD_YN": "N",
|
421
|
+
"FNCG_AMT_AUTO_RDPT_YN": "N",
|
422
|
+
"PRCS_DVSN": "01",
|
423
|
+
"COST_ICLD_YN": "Y" if include_cost else "N",
|
424
|
+
"CTX_AREA_FK100": "",
|
425
|
+
"CTX_AREA_NK100": "",
|
426
|
+
}
|
427
|
+
|
428
|
+
result = await self.private_get_fetch_pnl_krw(self.extend(params))
|
429
|
+
elif base_market == "USD":
|
430
|
+
params = {
|
431
|
+
"CANO": acc_num[:8],
|
432
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
433
|
+
# 01: 원화, 02: 외화
|
434
|
+
"WCRC_FRCR_DVSN_CD": "02",
|
435
|
+
"NATN_CD": "840",
|
436
|
+
"TR_MKET_CD": "00",
|
437
|
+
"INQR_DVSN_CD": "00",
|
438
|
+
}
|
439
|
+
|
440
|
+
result = await self.fetchCashForUS(self.extend(params))
|
441
|
+
else:
|
442
|
+
return self.get_error_response(
|
443
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
444
|
+
)
|
445
|
+
|
446
|
+
response = self.get_response(result, params)
|
447
|
+
if response["response"]["success"] != "0":
|
448
|
+
return response
|
449
|
+
|
450
|
+
return self.parser.parse_pnl(response, base_market)
|
451
|
+
|
452
|
+
async def fetch_screener_list(self, user_id, base_market: str = "KRW"):
|
453
|
+
params = {"USER_ID": user_id}
|
454
|
+
|
455
|
+
if base_market == "KRW":
|
456
|
+
result = await self.private_get_fetch_screener_list_krw(self.extend(params))
|
457
|
+
else:
|
458
|
+
return self.get_error_response(
|
459
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
460
|
+
)
|
461
|
+
|
462
|
+
response = self.get_response(result, params)
|
463
|
+
if response["response"]["success"] != "0":
|
464
|
+
return response
|
465
|
+
|
466
|
+
return self.parser.parse_screener_list(response, base_market)
|
467
|
+
|
468
|
+
async def fetch_screener(self, user_id: str, screen_id: str, base_market: str = "KRW"):
|
469
|
+
if base_market == "KRW":
|
470
|
+
params = {"USER_ID": user_id, "SEQ": screen_id}
|
471
|
+
|
472
|
+
result = await self.private_get_fetch_screener_krw(self.extend(params))
|
473
|
+
elif base_market == "USD":
|
474
|
+
market_code = self.get_market_code_in_feeder(symbol="ALL", base_market=base_market)
|
475
|
+
params = {"AUTH": "", "EXCD": market_code, "CO_YN_PRICECUR": 1}
|
476
|
+
result = await self.fetchScreenerForUS(self.extend(params))
|
477
|
+
|
478
|
+
else:
|
479
|
+
return self.get_error_response(
|
480
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
481
|
+
)
|
482
|
+
|
483
|
+
response = self.get_response(result, params)
|
484
|
+
if response["response"]["success"] != "0":
|
485
|
+
return response
|
486
|
+
|
487
|
+
return self.parser.parse_screener(response, base_market)
|
488
|
+
|
489
|
+
async def fetch_security(self, symbol: str, base_market: str = "KRW"):
|
490
|
+
if base_market == "KRW":
|
491
|
+
params = {"PRDT_TYPE_CD": "300", "PDNO": symbol} # 주식
|
492
|
+
|
493
|
+
result = await self.private_get_fetch_security_info_krw(self.extend(params))
|
494
|
+
elif base_market == "USD":
|
495
|
+
params = {"PRDT_TYPE_CD": "J", "PDNO": symbol}
|
496
|
+
|
497
|
+
result = await self.fetchCashForUS(self.extend(params))
|
498
|
+
else:
|
499
|
+
return self.get_error_response(
|
500
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
501
|
+
)
|
502
|
+
|
503
|
+
response = self.get_response(result, params)
|
504
|
+
if response["response"]["success"] != "0":
|
505
|
+
return response
|
506
|
+
|
507
|
+
return self.parser.parse_security(response, base_market)
|
508
|
+
|
509
|
+
# endregion private feeder
|
510
|
+
|
511
|
+
# region broker
|
512
|
+
async def create_order(
|
513
|
+
self,
|
514
|
+
acc_num: str,
|
515
|
+
symbol: str,
|
516
|
+
ticket_type: str,
|
517
|
+
price: float,
|
518
|
+
qty: float,
|
519
|
+
otype: str,
|
520
|
+
base_market: str = "KRW",
|
521
|
+
):
|
522
|
+
if base_market == "KRW":
|
523
|
+
if otype.upper() == "limit".upper():
|
524
|
+
order_dvsn = "00"
|
525
|
+
elif otype.upper() == "market".upper():
|
526
|
+
order_dvsn = "01"
|
527
|
+
|
528
|
+
params = {
|
529
|
+
"CANO": acc_num[:8],
|
530
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
531
|
+
"PDNO": symbol,
|
532
|
+
"ORD_DVSN": order_dvsn,
|
533
|
+
"ORD_QTY": str(qty), # string type 으로 설정
|
534
|
+
"ORD_UNPR": str(price), # string type 으로 설정
|
535
|
+
}
|
536
|
+
|
537
|
+
if ticket_type == "EntryLong":
|
538
|
+
result = await self.private_post_send_order_entry_krw(self.extend(params))
|
539
|
+
elif ticket_type == "ExitLong":
|
540
|
+
result = await self.private_post_send_order_exit_krw(self.extend(params))
|
541
|
+
else:
|
542
|
+
return
|
543
|
+
elif base_market == "USD":
|
544
|
+
if otype.upper() == "limit".upper():
|
545
|
+
order_dvsn = "00"
|
546
|
+
elif otype.upper() == "market".upper():
|
547
|
+
# 미국장은 시장가를 세부적으로 구분하여 지원함. -> 시장가 거래를 우선 지원하지 않는다.
|
548
|
+
# https://apiportal.koreainvestment.com/apiservice/apiservice-overseas-stock#L_e4a7e5fd-eed5-4a85-93f0-f46b804dae5f
|
549
|
+
return self.get_error_response(
|
550
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
551
|
+
)
|
552
|
+
|
553
|
+
if ticket_type == "entry_long":
|
554
|
+
sell_type = ""
|
555
|
+
elif ticket_type == "exit_long":
|
556
|
+
sell_type = "00"
|
557
|
+
else:
|
558
|
+
return
|
559
|
+
|
560
|
+
market_code = await self.get_market_code_in_broker(symbol=symbol, base_market=base_market)
|
561
|
+
params = {
|
562
|
+
"CANO": acc_num[:8],
|
563
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
564
|
+
"OVRS_EXCG_CD": market_code,
|
565
|
+
"PDNO": symbol,
|
566
|
+
"ORD_DVSN": order_dvsn,
|
567
|
+
"ORD_QTY": str(qty), # string type 으로 설정
|
568
|
+
"SLL_TYPE": sell_type,
|
569
|
+
"OVRS_ORD_UNPR": str(price), # string type 으로 설정
|
570
|
+
"ORD_SVR_DVSN_CD": "0",
|
571
|
+
}
|
572
|
+
|
573
|
+
if ticket_type == "entry_long":
|
574
|
+
result = await self.sendOrderEntryForUS(self.extend(params))
|
575
|
+
elif ticket_type == "exit_long":
|
576
|
+
result = await self.sendOrderExitForUS(self.extend(params))
|
577
|
+
else:
|
578
|
+
return
|
579
|
+
else:
|
580
|
+
return self.get_error_response(
|
581
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
582
|
+
)
|
583
|
+
|
584
|
+
response = self.get_response(result, params)
|
585
|
+
if response["response"]["success"] != "0":
|
586
|
+
return response
|
587
|
+
|
588
|
+
return self.parser.parse_create_order(response, base_market)
|
589
|
+
|
590
|
+
async def cancel_order(
|
591
|
+
self,
|
592
|
+
acc_num: str,
|
593
|
+
order_id: str,
|
594
|
+
symbol: Optional[str] = "",
|
595
|
+
qty: float = 0,
|
596
|
+
base_market: str = "KRW",
|
597
|
+
**kwargs,
|
598
|
+
):
|
599
|
+
if base_market == "KRW":
|
600
|
+
params = {
|
601
|
+
"CANO": acc_num[:8],
|
602
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
603
|
+
"KRX_FWDG_ORD_ORGNO": "",
|
604
|
+
"ORGN_ODNO": str(order_id),
|
605
|
+
"RVSE_CNCL_DVSN_CD": "02",
|
606
|
+
"ORD_DVSN": "00",
|
607
|
+
"ORD_QTY": str(qty),
|
608
|
+
"ORD_UNPR": str(0),
|
609
|
+
"QTY_ALL_ORD_YN": "N",
|
610
|
+
}
|
611
|
+
|
612
|
+
# 수량 미입력시 전량 취소
|
613
|
+
if qty == 0:
|
614
|
+
params["QTY_ALL_ORD_YN"] = "Y"
|
615
|
+
|
616
|
+
result = await self.private_post_send_cancel_order_krw(self.extend(params))
|
617
|
+
elif base_market == "USD":
|
618
|
+
if qty == 0:
|
619
|
+
return self.get_error_response(
|
620
|
+
error_code="qty_error", error_message=f"{base_market} cancel order need to set qty."
|
621
|
+
)
|
622
|
+
|
623
|
+
market_code = await self.get_market_code_in_broker(symbol=symbol, base_market=base_market)
|
624
|
+
params = {
|
625
|
+
"CANO": acc_num[:8],
|
626
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
627
|
+
"OVRS_EXCG_CD": market_code,
|
628
|
+
"PDNO": symbol,
|
629
|
+
"ORGN_ODNO": str(order_id),
|
630
|
+
"RVSE_CNCL_DVSN_CD": "02",
|
631
|
+
"ORD_QTY": str(qty),
|
632
|
+
"OVRS_ORD_UNPR": str(0),
|
633
|
+
}
|
634
|
+
|
635
|
+
result = await self.sendCancelOrderForUS(self.extend(params))
|
636
|
+
else:
|
637
|
+
return self.get_error_response(
|
638
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
639
|
+
)
|
640
|
+
|
641
|
+
response = self.get_response(result, params)
|
642
|
+
if response["response"]["success"] != "0":
|
643
|
+
return response
|
644
|
+
|
645
|
+
return self.parser.parse_cancel_order(response, base_market)
|
646
|
+
|
647
|
+
async def modify_order(
|
648
|
+
self,
|
649
|
+
acc_num: str,
|
650
|
+
order_id: str,
|
651
|
+
price: float,
|
652
|
+
qty: float,
|
653
|
+
symbol: Optional[str] = "",
|
654
|
+
base_market: str = "KRW",
|
655
|
+
**kwargs,
|
656
|
+
):
|
657
|
+
if base_market == "KRW":
|
658
|
+
params = {
|
659
|
+
"CANO": acc_num[:8],
|
660
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
661
|
+
"KRX_FWDG_ORD_ORGNO": "",
|
662
|
+
"ORGN_ODNO": str(order_id),
|
663
|
+
"RVSE_CNCL_DVSN_CD": "01",
|
664
|
+
"ORD_DVSN": "00",
|
665
|
+
"ORD_QTY": str(qty),
|
666
|
+
"ORD_UNPR": str(price),
|
667
|
+
"QTY_ALL_ORD_YN": "N",
|
668
|
+
}
|
669
|
+
|
670
|
+
# 수량 미입력시 전량 수정
|
671
|
+
if qty == 0:
|
672
|
+
params["QTY_ALL_ORD_YN"] = "Y"
|
673
|
+
|
674
|
+
result = await self.private_post_send_modify_order_krw(self.extend(params))
|
675
|
+
elif base_market == "USD":
|
676
|
+
market_code = await self.get_market_code_in_broker(symbol=symbol, base_market=base_market)
|
677
|
+
params = {
|
678
|
+
"CANO": acc_num[:8],
|
679
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
680
|
+
"OVRS_EXCG_CD": market_code,
|
681
|
+
"PDNO": symbol,
|
682
|
+
"ORGN_ODNO": str(order_id),
|
683
|
+
"RVSE_CNCL_DVSN_CD": "01",
|
684
|
+
"ORD_QTY": str(qty),
|
685
|
+
"OVRS_ORD_UNPR": str(price),
|
686
|
+
}
|
687
|
+
|
688
|
+
result = await self.sendModifyOrderForUS(self.extend(params))
|
689
|
+
else:
|
690
|
+
return self.get_error_response(
|
691
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
692
|
+
)
|
693
|
+
|
694
|
+
response = self.get_response(result, params)
|
695
|
+
if response["response"]["success"] != "0":
|
696
|
+
return response
|
697
|
+
|
698
|
+
return self.parser.parse_modify_order(response, base_market)
|
699
|
+
|
700
|
+
async def fetch_open_order(
|
701
|
+
self,
|
702
|
+
acc_num: str,
|
703
|
+
symbol: Optional[str] = "",
|
704
|
+
start: Optional[str] = None,
|
705
|
+
end: Optional[str] = None,
|
706
|
+
base_market: str = "KRW",
|
707
|
+
):
|
708
|
+
if start is None:
|
709
|
+
start = KoreaInvest.now(base_market=base_market).strftime("%Y%m%d")
|
710
|
+
|
711
|
+
if end is None:
|
712
|
+
end = KoreaInvest.now(base_market=base_market).strftime("%Y%m%d")
|
713
|
+
|
714
|
+
if base_market == "KRW":
|
715
|
+
params = {
|
716
|
+
"CANO": acc_num[:8],
|
717
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
718
|
+
"INQR_STRT_DT": start,
|
719
|
+
"INQR_END_DT": end,
|
720
|
+
"SLL_BUY_DVSN_CD": "00",
|
721
|
+
"INQR_DVSN": "00",
|
722
|
+
"PDNO": symbol,
|
723
|
+
"CCLD_DVSN": "02",
|
724
|
+
"ORD_GNO_BRNO": "",
|
725
|
+
"ODNO": "",
|
726
|
+
"INQR_DVSN_3": "00",
|
727
|
+
"INQR_DVSN_1": "",
|
728
|
+
"CTX_AREA_FK100": "",
|
729
|
+
"CTX_AREA_NK100": "",
|
730
|
+
}
|
731
|
+
|
732
|
+
result = await self.private_get_fetch_opened_order_krw(self.extend(params))
|
733
|
+
elif base_market == "USD":
|
734
|
+
market_code = await self.get_market_code_in_broker("ALL", base_market=base_market)
|
735
|
+
params = {
|
736
|
+
"CANO": acc_num[:8],
|
737
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
738
|
+
"PDNO": symbol if symbol is not None else "%",
|
739
|
+
"OVRS_EXCG_CD": market_code,
|
740
|
+
"SORT_SQN": "DS", # DS : 정순, AS : 역순
|
741
|
+
"CTX_AREA_NK200": "",
|
742
|
+
"CTX_AREA_FK200": "",
|
743
|
+
}
|
744
|
+
|
745
|
+
result = await self.fetchOpenedOrderForUS(self.extend(params))
|
746
|
+
else:
|
747
|
+
return self.get_error_response(
|
748
|
+
error_code="market_error", error_message=f"{base_market} market is not yet supported."
|
749
|
+
)
|
750
|
+
|
751
|
+
response = self.get_response(result, params)
|
752
|
+
if response["response"]["success"] != "0":
|
753
|
+
return response
|
754
|
+
|
755
|
+
return self.parser.parse_open_order_history(response, base_market)
|
756
|
+
|
757
|
+
async def fetch_closed_order(
|
758
|
+
self,
|
759
|
+
acc_num: str,
|
760
|
+
symbol: Optional[str] = "",
|
761
|
+
start: Optional[str] = None,
|
762
|
+
end: Optional[str] = None,
|
763
|
+
base_market: str = "KRW",
|
764
|
+
):
|
765
|
+
if start is None:
|
766
|
+
start = KoreaInvest.now(base_market=base_market).strftime("%Y%m%d")
|
767
|
+
|
768
|
+
if end is None:
|
769
|
+
end = KoreaInvest.now(base_market=base_market).strftime("%Y%m%d")
|
770
|
+
|
771
|
+
if base_market == "KRW":
|
772
|
+
params = {
|
773
|
+
"CANO": acc_num[:8],
|
774
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
775
|
+
"INQR_STRT_DT": start,
|
776
|
+
"INQR_END_DT": end,
|
777
|
+
"SLL_BUY_DVSN_CD": "00",
|
778
|
+
"INQR_DVSN": "00",
|
779
|
+
"PDNO": symbol,
|
780
|
+
"CCLD_DVSN": "01",
|
781
|
+
"ORD_GNO_BRNO": "",
|
782
|
+
"ODNO": "",
|
783
|
+
"INQR_DVSN_3": "00",
|
784
|
+
"INQR_DVSN_1": "",
|
785
|
+
"CTX_AREA_FK100": "",
|
786
|
+
"CTX_AREA_NK100": "",
|
787
|
+
}
|
788
|
+
|
789
|
+
result = await self.private_get_fetch_closed_order_krw(self.extend(params))
|
790
|
+
elif base_market == "USD":
|
791
|
+
market_code = await self.get_market_code_in_broker("ALL", base_market=base_market)
|
792
|
+
params = {
|
793
|
+
"CANO": acc_num[:8],
|
794
|
+
"ACNT_PRDT_CD": acc_num[-2:],
|
795
|
+
"PDNO": symbol if symbol is not None else "%",
|
796
|
+
"ORD_STRT_DT": start,
|
797
|
+
"ORD_END_DT": end,
|
798
|
+
"SLL_BUY_DVSN": "00",
|
799
|
+
"CCLD_NCCS_DVSN": "01" if not self.is_dev else "00",
|
800
|
+
"OVRS_EXCG_CD": market_code,
|
801
|
+
"SORT_SQN": "DS", # DS : 정순, AS : 역순
|
802
|
+
"ORD_DT": "",
|
803
|
+
"ORD_GNO_BRNO": "",
|
804
|
+
"ODNO": "",
|
805
|
+
"CTX_AREA_NK200": "",
|
806
|
+
"CTX_AREA_FK200": "",
|
807
|
+
}
|
808
|
+
|
809
|
+
result = await self.fetchClosedOrderForUS(self.extend(params))
|
810
|
+
|
811
|
+
response = self.get_response(result, params)
|
812
|
+
if response["response"]["success"] != "0":
|
813
|
+
return response
|
814
|
+
|
815
|
+
return self.parser.parse_closed_order_history(response, base_market)
|
816
|
+
|
817
|
+
# endregion broker
|
818
|
+
|
819
|
+
async def get_market_code_in_feeder(self, symbol: str, base_market: str = "KRW"):
|
820
|
+
if base_market == "KRW":
|
821
|
+
return ""
|
822
|
+
elif base_market == "USD":
|
823
|
+
if symbol.upper() == "ALL":
|
824
|
+
return "NASD"
|
825
|
+
|
826
|
+
response = await self.fetch_security(symbol=symbol, base_market=base_market)
|
827
|
+
return response["exchange"]
|
828
|
+
else:
|
829
|
+
return ""
|
830
|
+
|
831
|
+
async def get_market_code_in_broker(self, symbol: str, base_market: str = "KRW"):
|
832
|
+
if base_market == "KRW":
|
833
|
+
return ""
|
834
|
+
elif base_market == "USD":
|
835
|
+
if symbol.upper() == "ALL":
|
836
|
+
return "NASD"
|
837
|
+
|
838
|
+
response = await self.fetch_security(symbol=symbol, base_market=base_market)
|
839
|
+
exname = response["exchange"]
|
840
|
+
if exname == "NYS":
|
841
|
+
return "NYSE"
|
842
|
+
elif exname == "NAS":
|
843
|
+
return "NASD"
|
844
|
+
elif exname == "AMS":
|
845
|
+
return "AMEX"
|
846
|
+
else:
|
847
|
+
return ""
|
848
|
+
else:
|
849
|
+
return ""
|