직접 한국투자증권 Open API를 이용한 ‘무언가’를 개발하기 이전에, mojito2 파이썬 라이브러리를 실습하고 어떤 방식으로 개발을 해야할지 감을 잡아보자.
개발관련 지식이 전무한 상황이기 때문에, 다 훔쳐서 내것으로 만들자
1. 객체 생성 및 토근 발행
한국투자증권 Open API를 이용하려면(mojito2를 통해…?) 앞서 생성한 API KEY
와 API SECRET
을 이용해서 토근을 발급해야 한다.
mojito2의 KoreaInvestment 클래스 객체를 호출하면 생성자를 통해 자동으로 토큰(token.dat)을 생성하게 되는데 유효 기간이 1일이며 하루가 지나면 다시 생성해야 한다.
KoreaInvestment 인스턴스 생성
투자계좌를 넣는 acc_no
부분에 자신의 계좌번호를 기입, 모의투자계좌라면 -
하이픈 뒤가 없다. 하이픈(-)과 임의의 숫자를 넣지 않으면 에러가 발생.
import mojito
key = "PStmW7....t"
secret = "7gPSHO....="
acc_no = "12345678-0"
# 객체 생성
broker = mojito.KoreaInvestment(
api_key=key,
api_secret=secret,
acc_no=acc_no # 모의투자계좌라면 mock=True 추가
)
KoreaInvestment 인스턴스를 생성하는 관련 코드는 다음과 같다.
++ Java나 C에서는 Class와 동일한 이름으로 Method명을 지정하면 생성자로 선언
++ Python의 생성자는 def init(self, a, b, …):로 선언한다.
class KoreaInvestment:
'''
한국투자증권 REST API
'''
def __init__(self, api_key: str, api_secret: str, acc_no: str,exchange: str = "서울", mock: bool = False):
"""생성자
Args:
api_key (str): 발급받은 API key
api_secret (str): 발급받은 API secret
acc_no (str): 계좌번호 체계의 앞 8자리-뒤 2자리
exchange (str): "서울", "나스닥", "뉴욕", "아멕스", "홍콩", "상해", "심천", "도쿄", "하노이", "호치민"
mock (bool): True (mock trading), False (real trading)
"""
self.mock = mock
self.set_base_url(mock)
self.api_key = api_key
self.api_secret = api_secret
# account number
self.acc_no = acc_no
self.acc_no_prefix = acc_no.split('-')[0]
self.acc_no_postfix = acc_no.split('-')[1]
self.exchange = exchange
# access token
self.access_token = None
if self.check_access_token():
self.load_access_token()
else:
self.issue_access_token()
def set_base_url(self, mock: bool = True):
""" 생략 """
def issue_access_token(self):
# OAuth인증/접근토큰발급
path = "oauth2/tokenP"
url = f"{self.base_url}/{path}"
headers = {"content-type": "application/json"}
data = {
"grant_type": "client_credentials",
"appkey": self.api_key,
"appsecret": self.api_secret
}
resp = requests.post(url, headers=headers, data=json.dumps(data))
resp_data = resp.json()
self.access_token = f'Bearer {resp_data["access_token"]}'
# add extra information for the token verification
now = datetime.datetime.now()
resp_data['timestamp'] = int(now.timestamp()) + resp_data["expires_in"]
resp_data['api_key'] = self.api_key
resp_data['api_secret'] = self.api_secret
# dump access token
with open("token.dat", "wb") as f:
pickle.dump(resp_data, f)
def check_access_token(self):
# Returns: Bool: True: token is valid, False: token is not valid
try:
f = open("token.dat", "rb")
data = pickle.load(f)
f.close()
expire_epoch = data['timestamp']
now_epoch = int(datetime.datetime.now().timestamp())
status = False
if ((now_epoch - expire_epoch > 0) or
(data['api_key'] != self.api_key) or
(data['api_secret'] != self.api_secret)):
status = False
else:
status = True
return status
except IOError:
return False
def load_access_token(self):
with open("token.dat", "rb") as f:
data = pickle.load(f)
self.access_token = f'Bearer {data["access_token"]}'
위 코드에서 다음의 코드를 자세히 살펴보자.
# access token
self.access_token = None
if self.check_access_token():
self.load_access_token()
else:
self.issue_access_token()
self.access_token = None
를 통해서 access_token에 대한 객체를 None
으로 초기화 하고 있다. 현재는 access_token 객체 혹은 변수가 값을 가지지 않고 메모리 공간을 할당. 개발에 있어 가독성을 높이고 일관성 유지에 유리함.
이후, check_access_token
메서드를 통해 token.dat
파일이 존재하는지 확인을 한 후, 있다면 load_access_token
를 통해 토큰을 사용하고 없다면 issue_access_token
를 통해 token.dat
새로 생성한다.
2. 기능 확인하기
2-1. 잔고조회
import pprint
resp = broker.fetch_balance()
pprint.pprint(resp)
** output2에 대한 **
키 값 | 의미 |
---|---|
asst_icdc_amt | 자산의 총 금액 (일반적으로 특정 자산의 평가 금액) |
asst_icdc_erng_rt | 자산의 수익률 (수익을 나타내는 비율) |
bfdy_buy_amt | 이전 거래일의 매수 금액 |
bfdy_sll_amt | 이전 거래일의 매도 금액 |
bfdy_tlex_amt | 이전 거래일의 이체 금액 (전환 금액) |
bfdy_tot_asst_evlu_amt | 이전 거래일의 총 자산 평가 금액 |
cma_evlu_amt | CMA(종합자산관리계좌)의 평가 금액 |
d2_auto_rdpt_amt | D2 자동 상환 금액 |
dnca_tot_amt | DNCA(직접자산관리계좌)의 총 금액 |
evlu_amt_smtl_amt | 평가 금액 관련 특정 금액 |
evlu_pfls_smtl_amt | 평가 손실 관련 특정 금액 |
fncg_gld_auto_rdpt_yn | 금융 골드 자동 상환 여부 (Y/N) |
nass_amt | 특정 자산의 총 금액 |
nxdy_auto_rdpt_amt | 다음 거래일의 자동 상환 금액 |
nxdy_excc_amt | 다음 거래일의 초과 금액 |
pchs_amt_smtl_amt | 구매 관련 특정 금액 |
prvs_rcdl_excc_amt | 이전 기록의 초과 금액 |
scts_evlu_amt | 특정 증권의 평가 금액 |
thdt_buy_amt | 오늘 거래일의 매수 금액 |
thdt_sll_amt | 오늘 거래일의 매도 금액 |
thdt_tlex_amt | 오늘 거래일의 이체 금액 |
tot_evlu_amt | 총 평가 금액 |
tot_loan_amt | 총 대출 금액. |
tot_stln_slng_chgs | 총 스탠딩 대출 수수료 |
rt_cd | 응답 코드 |
+++ output1의 경우에는 별도로로 보유 종목에 대한 정보를 확인할 수 있는 것 같다.
+++ 현재는 보유하고 있는 정보가 없기 때문에 사용 방법만 기재… 테스트로 주식을 구매한 후 업데이트 예정
resp = broker.fetch_balance()
for comp in resp['output1']:
print(comp['pdno'])
print(comp['prdt_name'])
print(comp['hldg_qty'])
print(comp['pchs_amt'])
print(comp['evlu_amt'])
print("-" * 40)