ksxt 1.0.4__py3-none-any.whl → 1.0.6__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/__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/__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/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/__pycache__/__init__.cpython-312.pyc +0 -0
- ksxt/async_/base/__pycache__/async_exchange.cpython-312.pyc +0 -0
- ksxt/async_/base/async_exchange.py +23 -17
- ksxt/async_/bithumb.py +56 -21
- ksxt/async_/koreainvest.py +87 -8
- ksxt/async_/upbit.py +67 -24
- 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__/rate_limiter.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/exchange.py +42 -23
- ksxt/base/rest_exchange.py +32 -28
- ksxt/bithumb.py +48 -14
- ksxt/config/__pycache__/__init__.cpython-312.pyc +0 -0
- ksxt/config/token.toml +3 -7
- ksxt/koreainvest.py +37 -8
- 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/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/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/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 +17 -4
- ksxt/models/transaction.py +22 -28
- 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 +124 -23
- ksxt/parser/koreainvest.py +84 -21
- ksxt/parser/parser.py +30 -10
- ksxt/parser/upbit.py +134 -27
- ksxt/upbit.py +55 -18
- 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-1.0.4.dist-info → ksxt-1.0.6.dist-info}/METADATA +1 -1
- ksxt-1.0.6.dist-info/RECORD +119 -0
- {ksxt-1.0.4.dist-info → ksxt-1.0.6.dist-info}/WHEEL +1 -1
- ksxt/async_/base/__pycache__/throttler.cpython-312.pyc +0 -0
- ksxt-1.0.4.dist-info/RECORD +0 -120
- {ksxt-1.0.4.dist-info → ksxt-1.0.6.dist-info}/LICENSE.txt +0 -0
- {ksxt-1.0.4.dist-info → ksxt-1.0.6.dist-info}/top_level.txt +0 -0
ksxt/models/balance.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from pydantic import BaseModel
|
1
|
+
from pydantic import BaseModel, model_validator
|
2
2
|
from typing import List
|
3
3
|
|
4
4
|
from ksxt.models.common import GeneralResponse
|
@@ -7,14 +7,27 @@ from ksxt.models.common import GeneralResponse
|
|
7
7
|
class BalanceData(BaseModel):
|
8
8
|
symbol: str
|
9
9
|
name: str
|
10
|
-
|
10
|
+
|
11
11
|
price: float
|
12
|
-
|
13
|
-
|
12
|
+
evaluation_price: float
|
13
|
+
|
14
14
|
qty: float
|
15
15
|
free_qty: float
|
16
16
|
used_qty: float
|
17
17
|
|
18
|
+
amount: float | None = 0.0
|
19
|
+
evaluation_amount: float | None = 0.0
|
20
|
+
pnl: float | None = 0.0
|
21
|
+
pnl_ratio: float | None = 0.0
|
22
|
+
|
23
|
+
@model_validator(mode="after")
|
24
|
+
def calculate_fields(cls, values):
|
25
|
+
values.amount = values.price * values.qty
|
26
|
+
values.evaluation_amount = values.evaluation_price * values.qty
|
27
|
+
values.pnl = values.evaluation_price - values.price
|
28
|
+
values.pnl_ratio = values.pnl / values.price if values.price != 0 else 0.0
|
29
|
+
return values
|
30
|
+
|
18
31
|
|
19
32
|
class BalanceInfo(BaseModel):
|
20
33
|
currency: str
|
ksxt/models/transaction.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
from datetime import datetime
|
2
|
-
from typing import List
|
2
|
+
from typing import List
|
3
3
|
from pydantic import BaseModel
|
4
4
|
|
5
5
|
from ksxt.models.common import GeneralResponse
|
@@ -8,43 +8,37 @@ from ksxt.models.common import GeneralResponse
|
|
8
8
|
class TransactionInfo(BaseModel):
|
9
9
|
# 주문 고유 아이디
|
10
10
|
uuid: str
|
11
|
-
# 주문 종류 (ask, bid, deposit, withdrawal)
|
12
|
-
type: str
|
13
|
-
|
14
|
-
# 금액
|
15
|
-
amount: float
|
16
|
-
# 수수료
|
17
|
-
fee: Optional[float] = 0
|
18
|
-
|
19
11
|
# 계좌 번호
|
20
|
-
account_id:
|
21
|
-
# 화폐 통화 정보
|
22
|
-
currency: Optional[str] = None
|
12
|
+
account_id: str | None = None
|
23
13
|
|
24
|
-
# 주문
|
25
|
-
|
14
|
+
# 주문 종류 (ask, bid, deposit, withdrawal)
|
15
|
+
transaction_type: str
|
26
16
|
|
17
|
+
# order type (limit, market, default)
|
18
|
+
order_type: str | None = None
|
19
|
+
|
20
|
+
# position (long, short)
|
21
|
+
tr_position: str | None = None
|
27
22
|
|
28
|
-
class OpenedOrderInfo(TransactionInfo):
|
29
23
|
# 종목 정보
|
30
24
|
symbol: str
|
31
25
|
# 가격
|
32
26
|
price: float
|
33
27
|
# 수량
|
34
28
|
qty: float
|
35
|
-
#
|
36
|
-
|
29
|
+
# 금액
|
30
|
+
amount: float
|
37
31
|
|
32
|
+
# 세금
|
33
|
+
tax: float | None = 0
|
34
|
+
# 수수료
|
35
|
+
fee: float | None = 0
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
#
|
43
|
-
|
44
|
-
# 수량
|
45
|
-
qty: float
|
46
|
-
# 주문 방식
|
47
|
-
order_type: Optional[str]
|
37
|
+
# 화폐 통화 정보
|
38
|
+
currency: str | None = None
|
39
|
+
|
40
|
+
# 주문 생성 시간
|
41
|
+
created_at: datetime
|
48
42
|
|
49
43
|
|
50
44
|
class WithdrawalHistory(BaseModel):
|
@@ -56,11 +50,11 @@ class DepositHistory(BaseModel):
|
|
56
50
|
|
57
51
|
|
58
52
|
class OpenedOrderHistory(BaseModel):
|
59
|
-
history: List[
|
53
|
+
history: List[TransactionInfo]
|
60
54
|
|
61
55
|
|
62
56
|
class ClosedOrderHistory(BaseModel):
|
63
|
-
history: List[
|
57
|
+
history: List[TransactionInfo]
|
64
58
|
|
65
59
|
|
66
60
|
class KsxtWithdrawalHistoryResponse(GeneralResponse[WithdrawalHistory]):
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
ksxt/parser/bithumb.py
CHANGED
@@ -1,13 +1,10 @@
|
|
1
|
-
from datetime import datetime
|
2
|
-
from operator import attrgetter
|
1
|
+
from datetime import datetime, timezone
|
3
2
|
from typing import Dict, List, Optional
|
4
3
|
|
5
4
|
from ksxt.models.transaction import (
|
6
5
|
ClosedOrderHistory,
|
7
|
-
ClosedOrderInfo,
|
8
6
|
DepositHistory,
|
9
7
|
OpenedOrderHistory,
|
10
|
-
OpenedOrderInfo,
|
11
8
|
TransactionInfo,
|
12
9
|
WithdrawalHistory,
|
13
10
|
)
|
@@ -24,6 +21,10 @@ from ksxt.utils import safer, sorter
|
|
24
21
|
|
25
22
|
class BithumbParser(BaseParser):
|
26
23
|
def safe_symbol(self, base_market: str, security: str) -> str:
|
24
|
+
# If security already contains a hyphen, assume it's correctly formatted
|
25
|
+
if "-" in security:
|
26
|
+
return security
|
27
|
+
|
27
28
|
return f"{base_market}-{security}"
|
28
29
|
|
29
30
|
def parse_markets(self, response: List[Dict], base_market: str = "KRW") -> MarketInfo:
|
@@ -46,8 +47,22 @@ class BithumbParser(BaseParser):
|
|
46
47
|
warning_code=False if safer.safe_string(market, "market_warning") == "NONE" else True,
|
47
48
|
)
|
48
49
|
|
49
|
-
def parse_historical_data(
|
50
|
+
def parse_historical_data(
|
51
|
+
self,
|
52
|
+
response: List[Dict],
|
53
|
+
symbol: str,
|
54
|
+
start: datetime | None = None,
|
55
|
+
end: datetime | None = None,
|
56
|
+
base_market: str = "KRW",
|
57
|
+
) -> HistoricalDataInfo:
|
50
58
|
ohlcv = [self._parse_ohlcva(_, base_market) for _ in response]
|
59
|
+
|
60
|
+
# Filter by start and end datetime
|
61
|
+
if start:
|
62
|
+
ohlcv = [data for data in ohlcv if data.datetime >= start]
|
63
|
+
if end:
|
64
|
+
ohlcv = [data for data in ohlcv if data.datetime <= end]
|
65
|
+
|
51
66
|
sorted_ohlcv = sorter.sort_by(ohlcv, key="datetime")
|
52
67
|
|
53
68
|
security_name = symbol
|
@@ -161,7 +176,13 @@ class BithumbParser(BaseParser):
|
|
161
176
|
return ask_data, bid_data
|
162
177
|
|
163
178
|
def parse_balance(
|
164
|
-
self,
|
179
|
+
self,
|
180
|
+
response: List[Dict],
|
181
|
+
base_market: str = "KRW",
|
182
|
+
excluded_symbols: Optional[list[str]] = None,
|
183
|
+
included_symbols: Optional[list[str]] = None,
|
184
|
+
filter_delisted: bool = True,
|
185
|
+
min_amount: float = 0,
|
165
186
|
) -> BalanceInfo:
|
166
187
|
# 1. Filter out entries where 'currency' is not the base_market
|
167
188
|
# 2. Ensure 'unit_currency' is the base_market
|
@@ -172,11 +193,18 @@ class BithumbParser(BaseParser):
|
|
172
193
|
if data["currency"] != base_market
|
173
194
|
and data["unit_currency"] == base_market
|
174
195
|
and (excluded_symbols is None or data["currency"] not in excluded_symbols)
|
196
|
+
and (included_symbols is None or self.safe_symbol(base_market, data["currency"]) in included_symbols)
|
175
197
|
]
|
176
198
|
|
177
199
|
# Initialize balance list
|
178
200
|
balances = [self._parse_balance(data, base_market) for data in filtered_data]
|
179
201
|
|
202
|
+
if filter_delisted:
|
203
|
+
balances = [balance for balance in balances if balance.price > 0]
|
204
|
+
|
205
|
+
if min_amount > 0:
|
206
|
+
balances = [balance for balance in balances if balance.amount > min_amount]
|
207
|
+
|
180
208
|
# 총 매입금액
|
181
209
|
total_amount = sum(bal.price * bal.qty for bal in balances)
|
182
210
|
total_evaluation_amount = sum(bal.evaluation_price * bal.qty for bal in balances)
|
@@ -198,8 +226,6 @@ class BithumbParser(BaseParser):
|
|
198
226
|
name=self.safe_symbol(base_market, safer.safe_string(data, "currency")),
|
199
227
|
evaluation_price=0, # 필요에 따라 적절히 설정
|
200
228
|
price=safer.safe_number(data, "avg_buy_price"),
|
201
|
-
pnl_amount=0, # 필요에 따라 적절히 설정
|
202
|
-
pnl_ratio=0, # 필요에 따라 적절히 설정
|
203
229
|
qty=safer.safe_number(data, "balance"),
|
204
230
|
free_qty=safer.safe_number(data, "balance") - safer.safe_number(data, "locked"),
|
205
231
|
used_qty=safer.safe_number(data, "locked"),
|
@@ -256,38 +282,72 @@ class BithumbParser(BaseParser):
|
|
256
282
|
market_ask_fee=safer.safe_number(response, "maker_ask_fee"),
|
257
283
|
)
|
258
284
|
|
259
|
-
def parse_open_order_history(
|
285
|
+
def parse_open_order_history(
|
286
|
+
self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
|
287
|
+
) -> OpenedOrderHistory:
|
260
288
|
orders = [self._parse_open_order_info(order, base_market) for order in response]
|
289
|
+
|
290
|
+
# Filter by start and end datetime
|
291
|
+
if start:
|
292
|
+
orders = [
|
293
|
+
order for order in orders if order.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
|
294
|
+
]
|
295
|
+
if end:
|
296
|
+
orders = [
|
297
|
+
order for order in orders if order.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
|
298
|
+
]
|
299
|
+
|
261
300
|
return OpenedOrderHistory(history=orders)
|
262
301
|
|
263
|
-
def _parse_open_order_info(self, order: dict, base_market: str = "KRW") ->
|
264
|
-
return
|
302
|
+
def _parse_open_order_info(self, order: dict, base_market: str = "KRW") -> TransactionInfo:
|
303
|
+
return TransactionInfo(
|
265
304
|
uuid=safer.safe_string(order, "uuid"),
|
266
|
-
|
305
|
+
account_id="",
|
306
|
+
transaction_type=safer.safe_string(order, "side"),
|
307
|
+
order_type=safer.safe_string(order, "ord_type"),
|
308
|
+
tr_position="long",
|
267
309
|
symbol=self.safe_symbol(base_market, safer.safe_string(order, "market")),
|
268
310
|
price=safer.safe_number(order, "price"),
|
269
311
|
qty=safer.safe_number(order, "volume"),
|
270
312
|
amount=safer.safe_number(order, "price") * safer.safe_number(order, "volume"),
|
313
|
+
tax=0,
|
271
314
|
fee=safer.safe_number(order, "reserved_fee"),
|
315
|
+
currency=base_market,
|
272
316
|
created_at=datetime.fromisoformat(safer.safe_string(order, "created_at")),
|
273
|
-
order_type=safer.safe_string(order, "ord_type"),
|
274
317
|
)
|
275
318
|
|
276
|
-
def parse_closed_order_history(
|
319
|
+
def parse_closed_order_history(
|
320
|
+
self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
|
321
|
+
) -> ClosedOrderHistory:
|
277
322
|
orders = [self._parse_closed_order_info(order, base_market) for order in response]
|
323
|
+
|
324
|
+
# Filter by start and end datetime
|
325
|
+
if start:
|
326
|
+
orders = [
|
327
|
+
order for order in orders if order.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
|
328
|
+
]
|
329
|
+
if end:
|
330
|
+
orders = [
|
331
|
+
order for order in orders if order.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
|
332
|
+
]
|
333
|
+
|
278
334
|
return ClosedOrderHistory(history=orders)
|
279
335
|
|
280
|
-
def _parse_closed_order_info(self, order: dict, base_market: str = "KRW") ->
|
281
|
-
return
|
336
|
+
def _parse_closed_order_info(self, order: dict, base_market: str = "KRW") -> TransactionInfo:
|
337
|
+
return TransactionInfo(
|
282
338
|
uuid=safer.safe_string(order, "uuid"),
|
283
|
-
|
284
|
-
|
339
|
+
account_id="",
|
340
|
+
transaction_type=safer.safe_string(order, "side"),
|
341
|
+
order_type=safer.safe_string(order, "ord_type"),
|
342
|
+
tr_position="long",
|
343
|
+
symbol=self.safe_symbol(base_market, safer.safe_string(order, "market")),
|
285
344
|
price=safer.safe_number(order, "price"),
|
286
345
|
qty=safer.safe_number(order, "volume"),
|
287
346
|
amount=safer.safe_number(order, "price") * safer.safe_number(order, "volume"),
|
347
|
+
tax=0,
|
288
348
|
fee=safer.safe_number(order, "reserved_fee"),
|
349
|
+
currency=base_market,
|
289
350
|
created_at=datetime.fromisoformat(safer.safe_string(order, "created_at")),
|
290
|
-
order_type=safer.safe_string(order, "ord_type"),
|
291
351
|
)
|
292
352
|
|
293
353
|
def parse_cancel_order(self, response: dict, base_market: str = "KRW") -> CancelOrderResponse:
|
@@ -296,20 +356,61 @@ class BithumbParser(BaseParser):
|
|
296
356
|
def parse_create_order(self, response: dict, base_market: str = "KRW") -> CreateOrderResponse:
|
297
357
|
return CreateOrderResponse(order_datetime=datetime.now(), order_id=safer.safe_string(response, "uuid"))
|
298
358
|
|
299
|
-
def parse_withdrawal_history(
|
359
|
+
def parse_withdrawal_history(
|
360
|
+
self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
|
361
|
+
) -> WithdrawalHistory:
|
300
362
|
parsed_items = [self._parse_transaction_history_item(item, base_market) for item in response]
|
363
|
+
|
364
|
+
# Filter by start and end datetime
|
365
|
+
if start:
|
366
|
+
parsed_items = [
|
367
|
+
item
|
368
|
+
for item in parsed_items
|
369
|
+
if item.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
|
370
|
+
]
|
371
|
+
if end:
|
372
|
+
parsed_items = [
|
373
|
+
item
|
374
|
+
for item in parsed_items
|
375
|
+
if item.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
|
376
|
+
]
|
377
|
+
|
301
378
|
return WithdrawalHistory(history=parsed_items)
|
302
379
|
|
303
|
-
def parse_deposit_history(
|
380
|
+
def parse_deposit_history(
|
381
|
+
self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
|
382
|
+
) -> DepositHistory:
|
304
383
|
parsed_items = [self._parse_transaction_history_item(item, base_market) for item in response]
|
384
|
+
|
385
|
+
# Filter by start and end datetime
|
386
|
+
if start:
|
387
|
+
parsed_items = [
|
388
|
+
item
|
389
|
+
for item in parsed_items
|
390
|
+
if item.created_at.astimezone(timezone.utc) >= start.astimezone(timezone.utc)
|
391
|
+
]
|
392
|
+
if end:
|
393
|
+
parsed_items = [
|
394
|
+
item
|
395
|
+
for item in parsed_items
|
396
|
+
if item.created_at.astimezone(timezone.utc) <= end.astimezone(timezone.utc)
|
397
|
+
]
|
398
|
+
|
305
399
|
return DepositHistory(history=parsed_items)
|
306
400
|
|
307
401
|
def _parse_transaction_history_item(self, item: Dict, base_market: str = "KRW") -> TransactionInfo:
|
308
402
|
return TransactionInfo(
|
309
403
|
uuid=safer.safe_string(item, "uuid"),
|
310
|
-
|
404
|
+
account_id="",
|
405
|
+
transaction_type=safer.safe_string(item, "type"),
|
406
|
+
order_type=safer.safe_string(item, "transaction_type"),
|
407
|
+
tr_position="",
|
408
|
+
symbol=safer.safe_string(item, "currency"),
|
409
|
+
price=1,
|
410
|
+
qty=safer.safe_number(item, "amount"),
|
311
411
|
amount=safer.safe_number(item, "amount"),
|
412
|
+
tax=0,
|
312
413
|
fee=safer.safe_number(item, "fee"),
|
313
414
|
currency=safer.safe_string(item, "currency"),
|
314
|
-
created_at=datetime.fromisoformat(safer.safe_string(item, "
|
415
|
+
created_at=datetime.fromisoformat(safer.safe_string(item, "done_at")),
|
315
416
|
)
|
ksxt/parser/koreainvest.py
CHANGED
@@ -11,10 +11,9 @@ from ksxt.models.ticker import MultiSymbolTickerInfo, TickerInfo
|
|
11
11
|
from ksxt.models.token import TokenInfo
|
12
12
|
from ksxt.models.transaction import (
|
13
13
|
ClosedOrderHistory,
|
14
|
-
ClosedOrderInfo,
|
15
14
|
DepositHistory,
|
16
15
|
OpenedOrderHistory,
|
17
|
-
|
16
|
+
TransactionInfo,
|
18
17
|
)
|
19
18
|
from ksxt.parser.parser import BaseParser
|
20
19
|
from ksxt.utils import safer, sorter, timer
|
@@ -52,9 +51,23 @@ class KoreaInvestParser(BaseParser):
|
|
52
51
|
warning_code=safer.safe_boolean(market, " market_warning"),
|
53
52
|
)
|
54
53
|
|
55
|
-
def parse_historical_data(
|
54
|
+
def parse_historical_data(
|
55
|
+
self,
|
56
|
+
response: List[Dict],
|
57
|
+
symbol: str,
|
58
|
+
start: datetime | None = None,
|
59
|
+
end: datetime | None = None,
|
60
|
+
base_market: str = "KRW",
|
61
|
+
) -> HistoricalDataInfo:
|
56
62
|
safe_response = safer.safe_value(response, "output2")
|
57
63
|
ohlcv = [self._parse_ohlcva(_, base_market) for _ in safe_response]
|
64
|
+
|
65
|
+
# Filter by start and end datetime
|
66
|
+
if start:
|
67
|
+
ohlcv = [data for data in ohlcv if data.datetime >= start]
|
68
|
+
if end:
|
69
|
+
ohlcv = [data for data in ohlcv if data.datetime <= end]
|
70
|
+
|
58
71
|
sorted_ohlcv = sorter.sort_by(ohlcv, key="datetime")
|
59
72
|
|
60
73
|
security_name = symbol
|
@@ -77,9 +90,23 @@ class KoreaInvestParser(BaseParser):
|
|
77
90
|
acml_amount=safer.safe_number(ohlcva, "acml_tr_pbmn"),
|
78
91
|
)
|
79
92
|
|
80
|
-
def parse_historical_index_data(
|
93
|
+
def parse_historical_index_data(
|
94
|
+
self,
|
95
|
+
response: Dict,
|
96
|
+
symbol: str,
|
97
|
+
start: datetime | None = None,
|
98
|
+
end: datetime | None = None,
|
99
|
+
base_market: str = "KRW",
|
100
|
+
) -> HistoricalDataInfo:
|
81
101
|
safe_response = safer.safe_value(response, "output2")
|
82
102
|
ohlcv = [self._parse_index_ohlcva(_, base_market) for _ in safe_response]
|
103
|
+
|
104
|
+
# Filter by start and end datetime
|
105
|
+
if start:
|
106
|
+
ohlcv = [data for data in ohlcv if data.datetime >= start]
|
107
|
+
if end:
|
108
|
+
ohlcv = [data for data in ohlcv if data.datetime <= end]
|
109
|
+
|
83
110
|
sorted_ohlcv = sorter.sort_by(ohlcv, key="datetime")
|
84
111
|
|
85
112
|
security_name = symbol
|
@@ -154,7 +181,13 @@ class KoreaInvestParser(BaseParser):
|
|
154
181
|
return ask_data, bid_data
|
155
182
|
|
156
183
|
def parse_balance(
|
157
|
-
self,
|
184
|
+
self,
|
185
|
+
response: List[Dict],
|
186
|
+
base_market: str = "KRW",
|
187
|
+
excluded_symbols: Optional[list[str]] = None,
|
188
|
+
included_symbols: list[str] | None = None,
|
189
|
+
filter_delisted: bool = True,
|
190
|
+
min_amount: float = 0,
|
158
191
|
) -> BalanceInfo:
|
159
192
|
safe_response = safer.safe_value(response, "output1")
|
160
193
|
|
@@ -162,9 +195,16 @@ class KoreaInvestParser(BaseParser):
|
|
162
195
|
balances = [
|
163
196
|
self._parse_balance(_, base_market)
|
164
197
|
for _ in safe_response
|
165
|
-
if excluded_symbols is None or safer.safe_string(_, "pdno") not in excluded_symbols
|
198
|
+
if (excluded_symbols is None or safer.safe_string(_, "pdno") not in excluded_symbols)
|
199
|
+
and (included_symbols is None or safer.safe_string(_, "pdno") in included_symbols)
|
166
200
|
]
|
167
201
|
|
202
|
+
if filter_delisted:
|
203
|
+
balances = [balance for balance in balances if balance.price > 0]
|
204
|
+
|
205
|
+
if min_amount > 0:
|
206
|
+
balances = [balance for balance in balances if balance.amount > min_amount]
|
207
|
+
|
168
208
|
# 총 매입금액
|
169
209
|
total_amount = sum(bal.price * bal.qty for bal in balances)
|
170
210
|
total_evaluation_amount = sum(bal.evaluation_price * bal.qty for bal in balances)
|
@@ -186,7 +226,7 @@ class KoreaInvestParser(BaseParser):
|
|
186
226
|
name=safer.safe_string(data, "prdt_name"),
|
187
227
|
evaluation_price=safer.safe_number(data, "evlu_amt"),
|
188
228
|
price=safer.safe_number(data, "pchs_avg_pric"),
|
189
|
-
|
229
|
+
pnl=safer.safe_number(data, "evlu_pfls_amt"),
|
190
230
|
pnl_ratio=safer.safe_number(data, "evlu_pfls_rt"),
|
191
231
|
qty=safer.safe_number(data, "hldg_qty"),
|
192
232
|
free_qty=safer.safe_number(data, "ord_psbl_qty"),
|
@@ -242,52 +282,75 @@ class KoreaInvestParser(BaseParser):
|
|
242
282
|
market_ask_fee=0.0140527 + trading_fee,
|
243
283
|
)
|
244
284
|
|
245
|
-
def parse_closed_order_history(
|
285
|
+
def parse_closed_order_history(
|
286
|
+
self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
|
287
|
+
) -> ClosedOrderHistory:
|
246
288
|
safe_response = safer.safe_value(response, "output1")
|
247
|
-
|
248
|
-
|
289
|
+
orders = [self._parse_closed_order_info(_, base_market) for _ in safe_response]
|
290
|
+
|
291
|
+
# Filter by start and end datetime
|
292
|
+
if start:
|
293
|
+
orders = [order for order in orders if order.created_at >= start]
|
294
|
+
if end:
|
295
|
+
orders = [order for order in orders if order.created_at <= end]
|
296
|
+
|
297
|
+
return ClosedOrderHistory(history=orders)
|
249
298
|
|
250
|
-
def _parse_closed_order_info(self, order: Dict, base_market: str = "KRW") ->
|
299
|
+
def _parse_closed_order_info(self, order: Dict, base_market: str = "KRW") -> TransactionInfo:
|
251
300
|
if safer.safe_string(order, "sll_buy_dvsn_cd") == "01":
|
252
301
|
_type = "ask"
|
253
302
|
else:
|
254
303
|
_type = "bid"
|
255
304
|
|
256
|
-
return
|
305
|
+
return TransactionInfo(
|
257
306
|
uuid=safer.safe_string(order, "odno"),
|
258
|
-
|
307
|
+
account_id="",
|
308
|
+
# account_id=f'{safer.safe_string(order, "CANO")}-{safer.safe_string(order, "ACNT_PRDT_CD")}',
|
309
|
+
transaction_type=_type,
|
259
310
|
symbol=safer.safe_string(order, "pdno"),
|
260
311
|
price=safer.safe_number(order, "avg_prvs"),
|
261
312
|
qty=safer.safe_number(order, "tot_ccld_qty"),
|
262
313
|
amount=safer.safe_number(order, "tot_ccld_amt"),
|
314
|
+
tax=0,
|
263
315
|
# 단일 주문 이력에는 수수료 관련 정보가 없고, 조회한 전체 목록에 대해 수수료 정보만 존재한다.
|
264
316
|
fee=0,
|
265
|
-
created_at=datetime.fromisoformat(safer.safe_string(order, "ord_dt")),
|
266
317
|
order_type=safer.safe_string(order, "avg_prvs"),
|
318
|
+
created_at=datetime.fromisoformat(safer.safe_string(order, "ord_dt")),
|
267
319
|
)
|
268
320
|
|
269
|
-
def parse_open_order_history(
|
321
|
+
def parse_open_order_history(
|
322
|
+
self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
|
323
|
+
) -> OpenedOrderHistory:
|
270
324
|
safe_response = safer.safe_value(response, "output1")
|
271
|
-
|
272
|
-
|
325
|
+
orders = [self._parse_open_order_info(_, base_market) for _ in safe_response]
|
326
|
+
|
327
|
+
# Filter by start and end datetime
|
328
|
+
if start:
|
329
|
+
orders = [order for order in orders if order.created_at >= start]
|
330
|
+
if end:
|
331
|
+
orders = [order for order in orders if order.created_at <= end]
|
332
|
+
|
333
|
+
return OpenedOrderHistory(history=orders)
|
273
334
|
|
274
|
-
def _parse_open_order_info(self, order: Dict, base_market: str = "KRW") ->
|
335
|
+
def _parse_open_order_info(self, order: Dict, base_market: str = "KRW") -> TransactionInfo:
|
275
336
|
if safer.safe_string(order, "sll_buy_dvsn_cd") == "01":
|
276
337
|
_type = "ask"
|
277
338
|
else:
|
278
339
|
_type = "bid"
|
279
340
|
|
280
|
-
return
|
341
|
+
return TransactionInfo(
|
281
342
|
uuid=safer.safe_string(order, "odno"),
|
282
|
-
|
343
|
+
transaction_type=_type,
|
283
344
|
symbol=safer.safe_string(order, "pdno"),
|
284
345
|
price=safer.safe_number(order, "avg_prvs"),
|
285
346
|
qty=safer.safe_number(order, "tot_ccld_qty"),
|
286
347
|
amount=safer.safe_number(order, "tot_ccld_amt"),
|
348
|
+
tax=0,
|
287
349
|
# 단일 주문 이력에는 수수료 관련 정보가 없고, 조회한 전체 목록에 대해 수수료 정보만 존재한다.
|
288
350
|
fee=0,
|
289
|
-
|
351
|
+
currency=base_market,
|
290
352
|
order_type=safer.safe_string(order, "avg_prvs"),
|
353
|
+
created_at=datetime.fromisoformat(safer.safe_string(order, "ord_dt")),
|
291
354
|
)
|
292
355
|
|
293
356
|
def parse_create_order(self, response: Dict, base_market: str = "KRW") -> CreateOrderResponse:
|
ksxt/parser/parser.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from datetime import datetime
|
1
2
|
from typing import Dict, List, Optional
|
2
3
|
import inspect
|
3
4
|
|
@@ -10,10 +11,8 @@ from ksxt.models.orderbook import MultiSymbolOrderBookInfos, OrderBookData, Orde
|
|
10
11
|
from ksxt.models.ticker import MultiSymbolTickerInfo, TickerInfo
|
11
12
|
from ksxt.models.transaction import (
|
12
13
|
ClosedOrderHistory,
|
13
|
-
ClosedOrderInfo,
|
14
14
|
DepositHistory,
|
15
15
|
OpenedOrderHistory,
|
16
|
-
OpenedOrderInfo,
|
17
16
|
TransactionInfo,
|
18
17
|
WithdrawalHistory,
|
19
18
|
)
|
@@ -30,7 +29,14 @@ class BaseParser:
|
|
30
29
|
def _parse_market(self, market: Dict) -> SecurityData:
|
31
30
|
self._raise_not_implemented_error()
|
32
31
|
|
33
|
-
def parse_historical_data(
|
32
|
+
def parse_historical_data(
|
33
|
+
self,
|
34
|
+
response: List[Dict],
|
35
|
+
symbol: str,
|
36
|
+
start: datetime | None = None,
|
37
|
+
end: datetime | None = None,
|
38
|
+
base_market: str = "KRW",
|
39
|
+
) -> HistoricalDataInfo:
|
34
40
|
self._raise_not_implemented_error()
|
35
41
|
|
36
42
|
def _parse_ohlcva(self, ohlcva: dict, base_market: str = "KRW") -> OHLCVData:
|
@@ -63,7 +69,13 @@ class BaseParser:
|
|
63
69
|
self._raise_not_implemented_error()
|
64
70
|
|
65
71
|
def parse_balance(
|
66
|
-
self,
|
72
|
+
self,
|
73
|
+
response: List[Dict],
|
74
|
+
base_market: str = "KRW",
|
75
|
+
excluded_symbols: Optional[list[str]] = None,
|
76
|
+
included_symbols: list[str] | None = None,
|
77
|
+
filter_delisted: bool = True,
|
78
|
+
min_amount: float = 0,
|
67
79
|
) -> BalanceInfo:
|
68
80
|
self._raise_not_implemented_error()
|
69
81
|
|
@@ -79,16 +91,20 @@ class BaseParser:
|
|
79
91
|
def parse_trade_fee(self, response: dict, base_market: str = "KRW") -> TradeFeeInfo:
|
80
92
|
self._raise_not_implemented_error()
|
81
93
|
|
82
|
-
def parse_open_order_history(
|
94
|
+
def parse_open_order_history(
|
95
|
+
self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
|
96
|
+
) -> OpenedOrderHistory:
|
83
97
|
self._raise_not_implemented_error()
|
84
98
|
|
85
|
-
def _parse_open_order_info(self, order: dict, base_market: str = "KRW") ->
|
99
|
+
def _parse_open_order_info(self, order: dict, base_market: str = "KRW") -> TransactionInfo:
|
86
100
|
self._raise_not_implemented_error()
|
87
101
|
|
88
|
-
def parse_closed_order_history(
|
102
|
+
def parse_closed_order_history(
|
103
|
+
self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
|
104
|
+
) -> ClosedOrderHistory:
|
89
105
|
self._raise_not_implemented_error()
|
90
106
|
|
91
|
-
def _parse_closed_order_info(self, order: dict, base_market: str = "KRW") ->
|
107
|
+
def _parse_closed_order_info(self, order: dict, base_market: str = "KRW") -> TransactionInfo:
|
92
108
|
self._raise_not_implemented_error()
|
93
109
|
|
94
110
|
def parse_modify_order(self, response: dict, base_market: str = "KRW") -> ModifyOrderResponse:
|
@@ -100,10 +116,14 @@ class BaseParser:
|
|
100
116
|
def parse_create_order(self, response: dict, base_market: str = "KRW") -> CreateOrderResponse:
|
101
117
|
self._raise_not_implemented_error()
|
102
118
|
|
103
|
-
def parse_withdrawal_history(
|
119
|
+
def parse_withdrawal_history(
|
120
|
+
self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
|
121
|
+
) -> WithdrawalHistory:
|
104
122
|
self._raise_not_implemented_error()
|
105
123
|
|
106
|
-
def parse_deposit_history(
|
124
|
+
def parse_deposit_history(
|
125
|
+
self, response: List[Dict], start: datetime | None = None, end: datetime | None = None, base_market: str = "KRW"
|
126
|
+
) -> DepositHistory:
|
107
127
|
self._raise_not_implemented_error()
|
108
128
|
|
109
129
|
def _parse_transaction_history_item(self, item: Dict, base_market: str = "KRW") -> TransactionInfo:
|