pyqqq 0.12.178__tar.gz → 0.12.180__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.
- {pyqqq-0.12.178 → pyqqq-0.12.180}/PKG-INFO +1 -1
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyproject.toml +1 -1
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/backtest/broker.py +32 -1
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/backtest/environment.py +10 -2
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/position_classifier.py +1 -1
- {pyqqq-0.12.178 → pyqqq-0.12.180}/README.md +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/__init__.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/ai/__init__.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/ai/daily.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/ai/domestic.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/ai/market_schedule.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/ai/minute.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/backtest/__init__.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/backtest/logger.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/backtest/positionprovider.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/backtest/strategy.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/backtest/utils.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/backtest/wallclock.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/__init__.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/ebest/__init__.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/ebest/domestic_stock.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/ebest/oauth.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/ebest/simple.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/ebest/tr_client.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/helper.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/kis/__init__.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/kis/domestic_stock.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/kis/oauth.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/kis/overseas_stock.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/kis/simple.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/kis/simple_overseas.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/kis/tr_client.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/multiprocess_tracker.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/brokerage/tracker.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/config.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/data/__init__.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/data/daily.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/data/domestic.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/data/index.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/data/minutes.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/data/overseas.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/data/realtime.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/data/ticks.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/data/us_stocks.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/datatypes.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/executors/__init__.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/executors/hook.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/__init__.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/api_client.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/array.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/casting.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/compute.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/copycat.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/daily_tickers.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/display.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/kvstore.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/limiter.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/local_cache.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/logger.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/market_schedule.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/mock_api.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/retry.py +0 -0
- {pyqqq-0.12.178 → pyqqq-0.12.180}/pyqqq/utils/singleton.py +0 -0
|
@@ -25,6 +25,7 @@ from pyqqq.data.us_stocks import get_ticker_info as get_us_ticker_info
|
|
|
25
25
|
from pyqqq.datatypes import *
|
|
26
26
|
from pyqqq.utils.casting import casting
|
|
27
27
|
from pyqqq.utils.market_schedule import get_last_trading_day, get_market_schedule, get_trading_day_with_offset
|
|
28
|
+
from pyqqq.utils.position_classifier import PositionClassifier
|
|
28
29
|
|
|
29
30
|
MarketType = Literal["kr_stock", "us_stock"]
|
|
30
31
|
|
|
@@ -249,6 +250,8 @@ class TradingBroker(BaseBroker):
|
|
|
249
250
|
trading_api: Union[KISSimpleDomesticStock, KISSimpleOverseasStock],
|
|
250
251
|
clock: WallClock,
|
|
251
252
|
market_nxt_on: bool = False,
|
|
253
|
+
strategy_name: Optional[str] = None,
|
|
254
|
+
classifier_type: Optional[str] = None,
|
|
252
255
|
):
|
|
253
256
|
"""TradingBroker 클래스의 초기화 메서드입니다.
|
|
254
257
|
|
|
@@ -266,6 +269,14 @@ class TradingBroker(BaseBroker):
|
|
|
266
269
|
self.data_api = data_api
|
|
267
270
|
self.trading_api = trading_api
|
|
268
271
|
self.market_nxt_on = market_nxt_on
|
|
272
|
+
self.position_classifier = None
|
|
273
|
+
|
|
274
|
+
if classifier_type and classifier_type in ["auto", "direct"]:
|
|
275
|
+
if strategy_name:
|
|
276
|
+
kv_store_collection = f"{strategy_name}_classifier"
|
|
277
|
+
else:
|
|
278
|
+
kv_store_collection = f"account_{self.trading_api.account_no}_classifier"
|
|
279
|
+
self.position_classifier = PositionClassifier(simple_data_api=self.trading_api, kv_store_collection=kv_store_collection, default_type=classifier_type)
|
|
269
280
|
|
|
270
281
|
def get_account(self) -> dict:
|
|
271
282
|
return self.trading_api.get_account()
|
|
@@ -350,7 +361,20 @@ class TradingBroker(BaseBroker):
|
|
|
350
361
|
|
|
351
362
|
def create_order(self, asset_code: str, side: OrderSide, quantity: int, order_type: OrderType = OrderType.MARKET, price: int | Decimal = 0, exchange: OrderExchange = OrderExchange.KRX) -> str:
|
|
352
363
|
self.logger.debug(f"create_order: {asset_code} {side} {quantity} {order_type} {price} {exchange}")
|
|
353
|
-
|
|
364
|
+
|
|
365
|
+
if side is OrderSide.SELL and self.position_classifier:
|
|
366
|
+
# 포지션 분류기를 사용하여 전략에서 보유중인 수량으로 매도 가능 수량 재 계산
|
|
367
|
+
calc_quantity = self.position_classifier.get_sellable_quantity(asset_code, quantity)
|
|
368
|
+
|
|
369
|
+
if calc_quantity != quantity:
|
|
370
|
+
self.logger.warning(f"Requested sell quantity {quantity} for {asset_code} adjusted to {calc_quantity} based on position classifier.")
|
|
371
|
+
quantity = calc_quantity
|
|
372
|
+
|
|
373
|
+
order_no = self.trading_api.create_order(asset_code, side, quantity, order_type, price, exchange=exchange)
|
|
374
|
+
if order_no and self.position_classifier:
|
|
375
|
+
# 주문이 성공적으로 생성되면 포지션 분류기에 주문 정보를 업데이트
|
|
376
|
+
self.position_classifier.tagging_order_auto(order_no)
|
|
377
|
+
return order_no
|
|
354
378
|
|
|
355
379
|
def update_order(self, org_order_no: str, order_type: OrderType, price: int | Decimal, quantity: int = 0, exchange: OrderExchange = OrderExchange.KRX):
|
|
356
380
|
if isinstance(self.trading_api, KISSimpleOverseasStock):
|
|
@@ -387,6 +411,13 @@ class TradingBroker(BaseBroker):
|
|
|
387
411
|
self.logger.debug(f"cancel_order: {order_no} {quantity}")
|
|
388
412
|
return self.trading_api.cancel_order(order_no, quantity)
|
|
389
413
|
|
|
414
|
+
async def start_classifier(self):
|
|
415
|
+
""" 포지션 분류기를 시작합니다.
|
|
416
|
+
이 메서드는 포지션 분류기를 사용한다면 반드시 호출해야 합니다.
|
|
417
|
+
"""
|
|
418
|
+
if self.position_classifier:
|
|
419
|
+
await self.position_classifier.start()
|
|
420
|
+
|
|
390
421
|
|
|
391
422
|
# 백테스팅 할때 사용되는 브로커
|
|
392
423
|
class MockBroker(BaseBroker):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import datetime as dtm
|
|
2
2
|
import os
|
|
3
3
|
from abc import ABC
|
|
4
|
-
from typing import Literal
|
|
4
|
+
from typing import Literal, Optional
|
|
5
5
|
from zoneinfo import ZoneInfo
|
|
6
6
|
|
|
7
7
|
from pyqqq.backtest.broker import BaseBroker, MockBroker, TradingBroker
|
|
@@ -142,12 +142,18 @@ class KISDomesticEnvironment(TradingEnvironment):
|
|
|
142
142
|
```
|
|
143
143
|
"""
|
|
144
144
|
|
|
145
|
-
def __init__(self, paper_trading: bool = False, market_nxt_on: bool = False):
|
|
145
|
+
def __init__(self, paper_trading: bool = False, market_nxt_on: bool = False, strategy_name: Optional[str] = None, classifier_type: Optional[str] = None):
|
|
146
146
|
"""KISDomesticEnvironment 클래스의 초기화 메서드입니다.
|
|
147
147
|
|
|
148
148
|
Args:
|
|
149
149
|
paper_trading (bool, optional): 모의투자 사용 여부. Defaults to False.
|
|
150
150
|
True일 경우 모의투자 계좌 사용
|
|
151
|
+
market_nxt_on (bool, optional): NXT 시장 처리 여부. Defaults to False.
|
|
152
|
+
strategy_name (str, optional): 전략 이름. Defaults to None.
|
|
153
|
+
position classifier 데이터가 저장되는 kvstore collection 이름에 사용됨.
|
|
154
|
+
classifier_type (str, optional): "direct", "auto" 중 하나로 설정시 position classifier 사용하게 됨. Defaults to None.
|
|
155
|
+
해당 값은 분류기가 기본 포지션을 어떤 방식으로 결정할지에 대한 설정.
|
|
156
|
+
|
|
151
157
|
|
|
152
158
|
Raises:
|
|
153
159
|
KeyError: 필요한 환경변수가 설정되지 않은 경우
|
|
@@ -160,6 +166,8 @@ class KISDomesticEnvironment(TradingEnvironment):
|
|
|
160
166
|
trading_api=conn.broker_simple if not paper_trading else conn.paper_broker_simple,
|
|
161
167
|
clock=self.clock,
|
|
162
168
|
market_nxt_on=market_nxt_on,
|
|
169
|
+
strategy_name=strategy_name,
|
|
170
|
+
classifier_type=classifier_type,
|
|
163
171
|
)
|
|
164
172
|
|
|
165
173
|
|
|
@@ -107,7 +107,7 @@ class PositionClassifier:
|
|
|
107
107
|
self.logger.info(f"set_initial_position.\npositions: {cur_pos}\nauto_positions: {self.auto_positions}\ndirect_positions: {self.direct_positions}")
|
|
108
108
|
|
|
109
109
|
def set_initial_order(self):
|
|
110
|
-
cur_order = self.api.get_pending_orders()
|
|
110
|
+
cur_order = self.api.get_pending_orders(exchanges=[OrderExchange.KRX, OrderExchange.NXT, OrderExchange.SOR])
|
|
111
111
|
kv_auto_orders = self.kv_store.get(self.DEF_AUTO_ORDER_KEY) or {}
|
|
112
112
|
kv_direct_orders = self.kv_store.get(self.DEF_DIRECT_ORDER_KEY) or {}
|
|
113
113
|
kv_auto_tag_orders = self.kv_store.get(self.DEF_TAG_AUTO_ORDER_KEY) or []
|
|
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
|
|
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
|