Source code for pyqqq.brokerage.helper
import json
import os
import pyqqq.config as c
from pyqqq.brokerage.ebest.domestic_stock import EBestDomesticStock
from pyqqq.brokerage.ebest.oauth import EBestAuth
from pyqqq.brokerage.ebest.simple import EBestSimpleDomesticStock
from pyqqq.brokerage.kis.domestic_stock import KISDomesticStock
from pyqqq.brokerage.kis.oauth import KISAuth
from pyqqq.brokerage.kis.simple import KISSimpleDomesticStock
from pyqqq.utils.api_client import send_request
from pyqqq.utils.logger import get_bare_logger as get_logger
logger = get_logger(__name__)
[docs]
class NoConnection:
""".env 파일 등 계정 정보가 없을 경우"""
def __init__(self):
pass
[docs]
class KISConnection:
"""
환경변수 파일(.env)에 한투 계정 정보가 있을 경우 브로커 연결을 생성하는 클래스입니다.
Attributes:
auth (KISAuth): 인증 객체
broker_code (str): 브로커 코드
broker (KISDomesticStock): 일반 거래용 브로커 객체
broker_simple (KISSimpleDomesticStock): 간편 거래용 브로커 객체
paper_auth (Optional[KISAuth]): 모의투자 인증 객체
paper_broker_simple (Optional[KISSimpleDomesticStock]): 모의투자 간편 브로커 객체
"""
logger = get_logger(__name__ + ".KISConnection")
def __init__(self):
app_key = os.getenv("KIS_APP_KEY")
app_secret = os.getenv("KIS_APP_SECRET")
account_no = os.getenv("KIS_CANO")
account_product_code = os.getenv("KIS_ACNT_PRDT_CD")
hts_id = os.getenv("KIS_HTS_ID")
self.auth = KISAuth(app_key, app_secret)
self.broker_code = "kis"
self.broker = KISDomesticStock(self.auth)
self.broker_simple = KISSimpleDomesticStock(self.auth, account_no, account_product_code, hts_id)
self.paper_auth = None
self.paper_broker_simple = None
self.logger.info("Connected to KIS")
if all(k in os.environ for k in ["PAPER_KIS_APP_KEY", "PAPER_KIS_APP_SECRET", "PAPER_KIS_CANO", "PAPER_KIS_ACNT_PRDT_CD"]):
self.logger.info("Using paper broker")
paper_app_key = os.getenv("PAPER_KIS_APP_KEY")
paper_app_secret = os.getenv("PAPER_KIS_APP_SECRET")
paper_account_no = os.getenv("PAPER_KIS_CANO")
paper_account_product_code = os.getenv("PAPER_KIS_ACNT_PRDT_CD")
self.paper_auth = KISAuth(paper_app_key, paper_app_secret, paper_trading=True)
self.paper_broker_simple = KISSimpleDomesticStock(self.paper_auth, paper_account_no, paper_account_product_code)
[docs]
class EBestConnection:
"""
환경변수 파일(.env)에 LS(구 이베스트투자)증권 계정 정보가 있을 경우 브로커 연결을 생성하는 클래스입니다.
Attributes:
auth (EBestAuth): 인증 객체
broker_code (str): 브로커 코드
broker (EBestDomesticStock): 일반 거래용 브로커 객체
broker_simple (EBestSimpleDomesticStock): 간편 거래용 브로커 객체
account_no (str): 계좌 번호
paper_auth (Optional[EBestAuth]): 모의투자 인증 객체
paper_broker_simple (Optional[EBestSimpleDomesticStock]): 모의투자 간편 브로커 객체
"""
logger = get_logger(__name__ + ".EBestConnection")
def __init__(self):
self.auth = EBestAuth(os.getenv("EBEST_APP_KEY"), os.getenv("EBEST_APP_SECRET"))
self.broker_code = "ebest"
self.broker = EBestDomesticStock(self.auth)
self.broker_simple = EBestSimpleDomesticStock(self.auth)
self.account_no = self.broker_simple.get_account().get("account_no")
self.paper_auth = None
self.paper_broker_simple = None
self.logger.info("Connected to EBEST")
if os.getenv("PAPER_TRADING") == "1":
self.logger.info("Using paper broker")
self.paper_auth = EBestAuth(os.getenv("EBEST_APP_KEY"), os.getenv("EBEST_APP_SECRET"), paper_trading=os.getenv("PAPER_TRADING") == "1")
self.paper_broker_simple = EBestSimpleDomesticStock(self.paper_auth)
def get_base_class():
if os.getenv("KIS_APP_KEY"):
return KISConnection
elif os.getenv("EBEST_APP_KEY"):
return EBestConnection
elif os.getenv("ACCOUNT_NICK"):
return __set_account_info_env(os.getenv("ACCOUNT_NICK"))
else:
return NoConnection
def __lookup_account_info(nick: str):
url = f"{c.PYQQQ_API_URL}/users/me/accounts/{nick}/access"
r = send_request(
"POST",
url,
)
if r.status_code > 204:
logger.error(f"Failed to get day data: {r.text}")
r.raise_for_status()
return json.loads(r.json())
def __set_account_info_env(nick: str):
account = __lookup_account_info(nick)
if account.get("KIS_APP_KEY"):
os.environ["KIS_APP_KEY"] = account.get("KIS_APP_KEY") or os.getenv("KIS_APP_KEY")
os.environ["KIS_APP_SECRET"] = account.get("KIS_APP_SECRET") or os.getenv("KIS_APP_SECRET")
os.environ["KIS_CANO"] = account.get("KIS_CANO") or os.getenv("KIS_CANO")
os.environ["KIS_ACNT_PRDT_CD"] = account.get("KIS_ACNT_PRDT_CD") or os.getenv("KIS_ACNT_PRDT_CD")
os.environ["KIS_HTS_ID"] = account.get("KIS_HTS_ID") or os.getenv("KIS_HTS_ID")
return KISConnection
elif account.get("EBEST_APP_KEY"):
os.environ["EBEST_APP_KEY"] = account.get("EBEST_APP_KEY") or os.getenv("EBEST_APP_KEY")
os.environ["EBEST_APP_SECRET"] = account.get("EBEST_APP_SECRET") or os.getenv("EBEST_APP_SECRET")
return EBestConnection
else:
return NoConnection
[docs]
class PyQQQAutoConnectionSingleton(get_base_class()):
"""
환경변수를 읽어서 자동으로 브로커 연결을 생성하는 싱글톤 클래스입니다.
이 클래스는 환경변수에 따라 다음과 같은 클래스 중 하나의 인스턴스를 반환합니다:
- NoConnection: 계정 정보가 없는 경우
- KISConnection: 한투 계정 정보가 있는 경우
- EBestConnection: LS(구 이베스트투자)증권 계정 정보가 있는 경우
Example:
>>> conn = PyQQQAutoConnectionSingleton()
>>> conn.broker_simple.get_account()
Returns:
NoConnection | KISConnection | EBestConnection: 환경변수에 따라 결정된 브로커 연결 인스턴스
"""
_instance = None
_initialized = False
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
[docs]
def __init__(self):
if self._initialized:
return
self._initialized = True
super().__init__()