initial commit
This commit is contained in:
64
한국투자증권(API)/legacy/rest/README.md
Normal file
64
한국투자증권(API)/legacy/rest/README.md
Normal file
@@ -0,0 +1,64 @@
|
||||
&descAlignY=51&descAlign=62)
|
||||
|
||||
## 1. 개발 환경 준비 (파이썬 세팅하기)
|
||||
|
||||
### **1.1. 아나콘다(파이썬) 설치**
|
||||
|
||||
① 아나콘다 홈페이지([https://www.anaconda.com/download#downloads](https://www.anaconda.com/download#downloads))에서 본인 OS에 맞는 인스톨러를 다운로드
|
||||
|
||||

|
||||
|
||||
② 설치파일 관리자 권한으로 실행 > Next 클릭 > 라이선스 화면 I Agree 클릭 > 설치타입 ‘All Users’ 클릭 > 설치 경로 C:\Anaconda3 으로 변경 & Next 클릭 > Advanced Options 화면에서 기본 값 그대로 Install 클릭
|
||||
|
||||
③ 설치 완료
|
||||
|
||||
### **1.2. 가상환경 생성 + 모듈 설치**
|
||||
|
||||
① Windows 로고 키 클릭 > Anaconda prompt 클릭
|
||||
|
||||

|
||||
|
||||
② prompt 창에서 아래 명령어를 순서대로 입력
|
||||
|
||||
**- (가상환경 생성 명령어) conda create –n koreainvest python=3.8**
|
||||
|
||||
**- (가상환경 실행 명령어) conda activate koreainvest**
|
||||
|
||||
**- (모듈 설치 명령어) pip install websockets pycryptodome requests pyyaml**
|
||||
|
||||

|
||||
|
||||
- python websocket 사용을 위해 websockets 라이브러리를 설치하며, 주식체결통보 AES256 복호화 사용을 위한 pycrypodome 를 설치합니다.
|
||||
- REST api 호출을 위해 requests 라이브러리를 설치하며, 개인정보(appkey, appsecret 등) 파일을 읽어들이기 위해 pyyaml 라이브러리를 설치합니다.
|
||||
|
||||
### **1.3. Pycharm 설치**
|
||||
|
||||
Pycharm은 파이썬 개발에 가장 널리 사용되는 통합 개발 환경으로 community 버전은 무료로 설치 가능합니다.
|
||||
|
||||
① Jetbrain 홈페이지(https://www.jetbrains.com/ko-kr/pycharm/download) 에서 Pycharm 인스톨러를 다운로드
|
||||
|
||||

|
||||
|
||||
② 인스톨러 실행 > Next 클릭 > 설치 완료 시 Finish 클릭 > Pycharm 실행
|
||||
|
||||

|
||||
|
||||
### **1.4. Project 생성 및 Python Interpreter 설정하기**
|
||||
|
||||
① New Project 클릭 > 하단에 Previously configured Interpreter 클릭 > 우측의 Add Interpreter – Add Local Interpreter 클릭
|
||||
|
||||

|
||||
|
||||
② 새 창이 뜨면 좌측의 Conda Environment 클릭 > Use existing environment - koreainvest 선택 > OK 클릭 > Create 클릭
|
||||
|
||||

|
||||
|
||||
### **1.5. (option) Postman 설치 및 사용하기**
|
||||
|
||||
포스트맨(Postman)은 개발자들이 API를 디자인하고 빌드하고 테스트하고 반복하기 위한 API 플랫폼입니다.
|
||||
|
||||
Postman는 테스트베드 기능을 효율적으로 제공하여, 코드를 작성하기 전 API를 호출해보고 응답값을 확인해볼 수 있는 테스트해볼 수 있으며, 여러 언어(C, Java, Python 등)로 샘플코드를 자동으로 생성해주는 기능을 제공합니다.
|
||||
|
||||

|
||||
|
||||
- 진행방법: 한국투자 Github ([https://github.com/koreainvestment/open-trading-api/tree/main/postman](https://github.com/koreainvestment/open-trading-api/tree/main/postman)) 의 순서대로 진행
|
||||
14
한국투자증권(API)/legacy/rest/config.yaml
Normal file
14
한국투자증권(API)/legacy/rest/config.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
# 홈페이지에서 API서비스 신청시 받은 Appkey, Appsecret 값 설정
|
||||
APP_KEY: "PS..."
|
||||
APP_SECRET: "UH..."
|
||||
|
||||
# 계좌번호 앞 8자리
|
||||
C ANO: "12345678"
|
||||
# 계좌번호 뒤 2자리
|
||||
ACNT_PRDT_CD: "01"
|
||||
|
||||
# 도메인
|
||||
URL_BASE: "https://openapi.koreainvestment.com:9443"
|
||||
|
||||
# HTS ID
|
||||
HTS_ID: "hantu34"
|
||||
54
한국투자증권(API)/legacy/rest/current_price_samle.py
Normal file
54
한국투자증권(API)/legacy/rest/current_price_samle.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import requests
|
||||
import json
|
||||
|
||||
APP_KEY = ""
|
||||
APP_SECRET = ""
|
||||
ACCESS_TOKEN = ""
|
||||
URL_BASE = "https://openapi.koreainvestment.com:9443" #실전투자
|
||||
|
||||
# Auth
|
||||
def auth():
|
||||
headers = {"content-type":"application/json"}
|
||||
body = {
|
||||
"grant_type":"client_credentials",
|
||||
"appkey":APP_KEY,
|
||||
"appsecret":APP_SECRET
|
||||
}
|
||||
PATH = "oauth2/tokenP"
|
||||
URL = f"{URL_BASE}/{PATH}"
|
||||
res = requests.post(URL, headers=headers, data=json.dumps(body))
|
||||
|
||||
global ACCESS_TOKEN
|
||||
ACCESS_TOKEN = res.json()["access_token"]
|
||||
|
||||
# 주식현재가 시세
|
||||
def get_current_price(stock_no):
|
||||
PATH = "uapi/domestic-stock/v1/quotations/inquire-price"
|
||||
URL = f"{URL_BASE}/{PATH}"
|
||||
|
||||
# 헤더 설정
|
||||
headers = {"Content-Type":"application/json",
|
||||
"authorization": f"Bearer {ACCESS_TOKEN}",
|
||||
"appKey":APP_KEY,
|
||||
"appSecret":APP_SECRET,
|
||||
"tr_id":"FHKST01010100"}
|
||||
|
||||
params = {
|
||||
"fid_cond_mrkt_div_code":"J",
|
||||
"fid_input_iscd": stock_no
|
||||
}
|
||||
|
||||
# 호출
|
||||
res = requests.get(URL, headers=headers, params=params)
|
||||
|
||||
if res.status_code == 200 and res.json()["rt_cd"] == "0" :
|
||||
return(res.json())
|
||||
# 토큰 만료 시
|
||||
elif res.status_code == 200 and res.json()["msg_cd"] == "EGW00123" :
|
||||
auth()
|
||||
get_current_price(stock_no)
|
||||
else:
|
||||
print("Error Code : " + str(res.status_code) + " | " + res.text)
|
||||
return None
|
||||
|
||||
get_current_price("005930")
|
||||
141
한국투자증권(API)/legacy/rest/get_interest_stocks_price.py
Normal file
141
한국투자증권(API)/legacy/rest/get_interest_stocks_price.py
Normal file
@@ -0,0 +1,141 @@
|
||||
## 관심종목 복수시세조회 파이썬 샘플코드
|
||||
### config.yaml 파일을 설정한 뒤 get_interest_stocks_price.py를 실행하여
|
||||
### 1) 관심종목 그룹조회 → 2) 관심종목 그룹별 종목조회 → 3) 관심종목(멀티종목) 시세조회 순서대로 호출하여 관심종목 복수시세 조회 가능
|
||||
|
||||
# (0) 필요 모듈 임포트
|
||||
import pandas as pd
|
||||
import requests
|
||||
import json
|
||||
import yaml
|
||||
import time
|
||||
|
||||
# (1) 개인정보 파일 가져오기
|
||||
with open('config.yaml', encoding='UTF-8') as f:
|
||||
_cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
APP_KEY = _cfg['APP_KEY']
|
||||
APP_SECRET = _cfg['APP_SECRET']
|
||||
ACCESS_TOKEN = ""
|
||||
ACCESS_TOKEN_EXPIRED = ""
|
||||
CANO = _cfg['CANO']
|
||||
ACNT_PRDT_CD = _cfg['ACNT_PRDT_CD']
|
||||
URL_BASE = _cfg['URL_BASE']
|
||||
HTS_ID = _cfg['HTS_ID']
|
||||
|
||||
# (2) 함수 정의
|
||||
## 1. 접근 토큰 발급
|
||||
def get_access_token():
|
||||
""" OAuth 인증 > 접근토큰발급 """
|
||||
headers = {"content-type": "application/json"}
|
||||
body = {"grant_type": "client_credentials",
|
||||
"appkey": APP_KEY,
|
||||
"appsecret": APP_SECRET}
|
||||
PATH = "oauth2/tokenP"
|
||||
URL = f"{URL_BASE}/{PATH}"
|
||||
res = requests.post(URL, headers=headers, data=json.dumps(body))
|
||||
time.sleep(0.1) # 유량제한 예방 (REST: 1초당 20건 제한)
|
||||
try:
|
||||
ACCESS_TOKEN = res.json()["access_token"]
|
||||
ACCESS_TOKEN_EXPIRED = res.json()["access_token_token_expired"]
|
||||
return ACCESS_TOKEN, ACCESS_TOKEN_EXPIRED
|
||||
except:
|
||||
print("접근 토큰 발급이 불가능합니다")
|
||||
print(res.json())
|
||||
|
||||
## 2. 관심종목 그룹 조회
|
||||
def get_interest_groups():
|
||||
PATH = "/uapi/domestic-stock/v1/quotations/intstock-grouplist"
|
||||
URL = f"{URL_BASE}/{PATH}"
|
||||
params = {
|
||||
"TYPE": "1",
|
||||
"FID_ETC_CLS_CODE": "00",
|
||||
"USER_ID": HTS_ID
|
||||
}
|
||||
headers = {
|
||||
"content-type": "application/json",
|
||||
"authorization": f"Bearer {ACCESS_TOKEN}",
|
||||
"appKey": APP_KEY,
|
||||
"appSecret": APP_SECRET,
|
||||
"tr_id": "HHKCM113004C7", # 실전투자
|
||||
"custtype": "P"
|
||||
}
|
||||
time.sleep(0.05) # 유량제한 예방 (REST: 1초당 20건 제한)
|
||||
res = requests.get(URL, headers=headers, params=params)
|
||||
return res.json()
|
||||
|
||||
## 3. 관심종목 그룹별 종목 조회
|
||||
def get_group_stocks(interest_group):
|
||||
PATH = "/uapi/domestic-stock/v1/quotations/intstock-stocklist-by-group"
|
||||
URL = f"{URL_BASE}/{PATH}"
|
||||
params = {
|
||||
"TYPE": "1",
|
||||
"USER_ID": HTS_ID,
|
||||
"DATA_RANK": "",
|
||||
"INTER_GRP_CODE": interest_group,
|
||||
"INTER_GRP_NAME": "",
|
||||
"HTS_KOR_ISNM": "",
|
||||
"CNTG_CLS_CODE": "",
|
||||
"FID_ETC_CLS_CODE": "4"
|
||||
}
|
||||
headers = {
|
||||
"content-type": "application/json",
|
||||
"authorization": f"Bearer {ACCESS_TOKEN}",
|
||||
"appKey": APP_KEY,
|
||||
"appSecret": APP_SECRET,
|
||||
"tr_id": "HHKCM113004C6", # 실전투자
|
||||
"custtype": "P"
|
||||
}
|
||||
time.sleep(0.05) # 유량제한 예방 (REST: 1초당 20건 제한)
|
||||
res = requests.get(URL, headers=headers, params=params)
|
||||
return res.json()
|
||||
|
||||
## 4. 관심종목(멀티종목) 시세조회 (30종목까지 조회 가능)
|
||||
def get_multi_stock_prices(jongs):
|
||||
PATH = "/uapi/domestic-stock/v1/quotations/intstock-multprice"
|
||||
URL = f"{URL_BASE}/{PATH}"
|
||||
headers = {
|
||||
"content-type": "application/json",
|
||||
"authorization": f"Bearer {ACCESS_TOKEN}",
|
||||
"appKey": APP_KEY,
|
||||
"appSecret": APP_SECRET,
|
||||
"tr_id": "FHKST11300006", # 실전투자
|
||||
"custtype": "P"
|
||||
}
|
||||
time.sleep(0.05) # 유량제한 예방 (REST: 1초당 20건 제한)
|
||||
res = requests.get(URL, headers=headers, params=jongs)
|
||||
print(res.text)
|
||||
print(res.json())
|
||||
return res.json()
|
||||
|
||||
# (3) 함수 실행
|
||||
## 1. 접근토큰 발급
|
||||
ACCESS_TOKEN, ACCESS_TOKEN_EXPIRED = get_access_token()
|
||||
|
||||
## 2. 관심종목 그룹 조회
|
||||
interest_groups = get_interest_groups()
|
||||
print(interest_groups)
|
||||
|
||||
## 3. 사용자로부터 관심종목 그룹 선택
|
||||
interest_group = str(input("관심종목 그룹 번호(inter_grp_code)를 입력하세요: "))
|
||||
|
||||
## 4. 선택한 관심종목 그룹별 종목 조회
|
||||
group_stocks = get_group_stocks(interest_group)
|
||||
print(group_stocks)
|
||||
interest_mrkt_code_list = [item["fid_mrkt_cls_code"] for item in group_stocks["output2"]]
|
||||
interest_jong_code_list = [item["jong_code"] for item in group_stocks["output2"]]
|
||||
|
||||
print(interest_mrkt_code_list)
|
||||
print(interest_jong_code_list)
|
||||
|
||||
## 5. 관심종목 최대30종목 리스트 생성
|
||||
jongs = {}
|
||||
for i in range(min(len(interest_jong_code_list), 30)): # 관심종목 (최대) 30종목 리스트 생성
|
||||
jongs[f"FID_COND_MRKT_DIV_CODE_{i}"] = interest_mrkt_code_list[i]
|
||||
jongs[f"FID_INPUT_ISCD_{i}"] = interest_jong_code_list[i]
|
||||
print("jongs",jongs)
|
||||
print("jongs 길이",len(jongs))
|
||||
|
||||
## 6. 관심종목 시세 조회
|
||||
multi_stock_prices = get_multi_stock_prices(jongs)
|
||||
print(multi_stock_prices) # 최대 30개 관심종목 데이터 출력
|
||||
print(pd.DataFrame(multi_stock_prices['output'])) # 전체 데이터 데이터프레임으로 변환
|
||||
print(pd.DataFrame(multi_stock_prices['output'])[['inter_kor_isnm','inter_shrn_iscd','inter2_prpr']]) #종목 한글명, 종목코드, 현재가만 출력
|
||||
150
한국투자증권(API)/legacy/rest/get_ovsfut_chart_price.py
Normal file
150
한국투자증권(API)/legacy/rest/get_ovsfut_chart_price.py
Normal file
@@ -0,0 +1,150 @@
|
||||
## 해외선물분봉조회: 해외선물 상품의 특정 기간동안의 분봉을 받아서 엑셀로 저장하는 파이썬 샘플코드
|
||||
## config.yaml 파일을 설정한 뒤 get_ovsfut_chart_price.py를 실행해서 분봉 데이터 저장 가능
|
||||
## 해외선물 종목정보 마스터파일 다운로드 : https://new.real.download.dws.co.kr/common/master/ffcode.mst.zip
|
||||
|
||||
# (0) 필요 모듈 임포트
|
||||
import pandas as pd
|
||||
import requests
|
||||
import json
|
||||
import yaml
|
||||
import time
|
||||
|
||||
|
||||
# (1) 개인정보 파일 가져오기
|
||||
with open('config.yaml', encoding='UTF-8') as f:
|
||||
_cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
APP_KEY = _cfg['APP_KEY']
|
||||
APP_SECRET = _cfg['APP_SECRET']
|
||||
ACCESS_TOKEN = ""
|
||||
ACCESS_TOKEN_EXPIRED = ""
|
||||
CANO = _cfg['CANO']
|
||||
ACNT_PRDT_CD = _cfg['ACNT_PRDT_CD']
|
||||
URL_BASE = _cfg['URL_BASE']
|
||||
HTS_ID = _cfg['HTS_ID']
|
||||
|
||||
|
||||
# (2) 함수 정의
|
||||
def get_access_token():
|
||||
""" OAuth 인증 > 접근토큰발급 """
|
||||
headers = {"content-type": "application/json"}
|
||||
body = {"grant_type": "client_credentials",
|
||||
"appkey": APP_KEY,
|
||||
"appsecret": APP_SECRET}
|
||||
PATH = "oauth2/tokenP"
|
||||
URL = f"{URL_BASE}/{PATH}"
|
||||
res = requests.post(URL, headers=headers, data=json.dumps(body))
|
||||
time.sleep(0.1)
|
||||
try:
|
||||
ACCESS_TOKEN = res.json()["access_token"]
|
||||
ACCESS_TOKEN_EXPIRED = res.json()["access_token_token_expired"]
|
||||
return ACCESS_TOKEN, ACCESS_TOKEN_EXPIRED
|
||||
except:
|
||||
print("접근 토큰 발급이 불가능합니다")
|
||||
print(res.json())
|
||||
|
||||
|
||||
def get_overseas_future_time_price(SRS="6AZ23", EXCH="CME", STRT_DT="",END_DT="", CNT="100", GAP="1", dataframe=None, file_name=None): # 해외선물옵션시세 > 해외선물 분봉조회
|
||||
"""
|
||||
해외선물옵션시세 > 해외선물 분봉조회를 위한 함수
|
||||
|
||||
Parameters:
|
||||
- SRS: 종목 코드
|
||||
- EXCH: 거래소 코드
|
||||
- STRT_DT: 조회 시작일
|
||||
- END_DT: 조회 종료일
|
||||
- CNT: 호출건당 조회 갯수
|
||||
- GAP: 조회 간격
|
||||
- dataframe: 초기 데이터프레임
|
||||
- file_name: 저장할 파일 이름
|
||||
|
||||
Returns"":
|
||||
- 최종 데이터프레임
|
||||
"""
|
||||
PATH = "/uapi/overseas-futureoption/v1/quotations/inquire-time-futurechartprice"
|
||||
URL = f"{URL_BASE}/{PATH}"
|
||||
headers = {"Content-Type":"application/json",
|
||||
"authorization":f"Bearer {ACCESS_TOKEN}",
|
||||
"appKey":APP_KEY,
|
||||
"appSecret":APP_SECRET,
|
||||
"tr_id":"HHDFC55020400", # 실전투자
|
||||
"custtype":"P"
|
||||
}
|
||||
params = {
|
||||
"SRS_CD": SRS,
|
||||
"EXCH_CD": EXCH,
|
||||
"START_DATE_TIME": "",
|
||||
"CLOSE_DATE_TIME": END_DT,
|
||||
"QRY_TP": "",
|
||||
"QRY_CNT": CNT,
|
||||
"QRY_GAP": GAP,
|
||||
"INDEX_KEY": ""
|
||||
}
|
||||
res = requests.get(URL, headers=headers, params=params)
|
||||
time.sleep(0.1) # 유량제한 예방 (REST: 1초당 20건 제한)
|
||||
output = res.json()['output1']
|
||||
|
||||
# 파일 이름이 제공되지 않은 경우에만 생성
|
||||
if file_name is None:
|
||||
file_name = f"{SRS}_{EXCH}_{STRT_DT}_{END_DT}"
|
||||
|
||||
# 초기 dataframe 생성
|
||||
if dataframe is None:
|
||||
dataframe = pd.DataFrame()
|
||||
|
||||
current_dataframe = pd.DataFrame(output)
|
||||
|
||||
# 이전 응답값이 존재하고, 이전 응답값과 현재 응답값이 동일하면
|
||||
if not dataframe.empty and dataframe.iloc[-1,0] == current_dataframe.iloc[-1,0] and dataframe.iloc[-1,1] == current_dataframe.iloc[-1,1]:
|
||||
|
||||
# 이전 데이터프레임에 현재 데이터프레임 추가
|
||||
dataframe = pd.concat([dataframe, current_dataframe], ignore_index=True)
|
||||
|
||||
# STRT_DT와 END_DT가 같은 경우 현재 dataframe을 return
|
||||
if STRT_DT == END_DT:
|
||||
dataframe = dataframe.drop_duplicates(ignore_index=True)
|
||||
|
||||
# 최종 데이터프레임 정제 작업(이상치 삭제 및 date, time으로 정렬)
|
||||
## 'data_date'와 'data_time'을 문자열로 변환하여 문자열을 합쳐 datetime 형식으로 만들기
|
||||
dataframe['datetime'] = pd.to_datetime(dataframe['data_date'].astype(str) + dataframe['data_time'].astype(str).str.zfill(6), errors='coerce', format='%Y%m%d%H%M%S')
|
||||
## 유효한 datetime이 아닌 행 삭제
|
||||
dataframe = dataframe.dropna(subset=['datetime'])
|
||||
## 'datetime'을 기준으로 정렬 후 'datetime' 열 삭제
|
||||
dataframe = dataframe.sort_values(by=['datetime']).drop(columns=['datetime']).reset_index(drop=True)
|
||||
|
||||
# 현재 위치에 엑셀파일로 저장
|
||||
dataframe.to_excel(f"{file_name}.xlsx",index=False)
|
||||
print("File Saved")
|
||||
|
||||
# 최종 데이터프레임 return
|
||||
return dataframe
|
||||
|
||||
else:
|
||||
# END_DT를 하루 이전으로 설정하고 재귀호출
|
||||
END_DT = pd.to_datetime(END_DT) - pd.DateOffset(days=1)
|
||||
END_DT = END_DT.strftime("%Y%m%d")
|
||||
return get_overseas_future_time_price(SRS, EXCH, STRT_DT, END_DT, "100", GAP, dataframe, file_name)
|
||||
else:
|
||||
# 이전 데이터프레임에 현재 데이터프레임 추가
|
||||
dataframe = pd.concat([dataframe, current_dataframe], ignore_index=True)
|
||||
|
||||
# 이전 응답값과 현재 응답값이 다르면 CNT를 100늘려서 재호출
|
||||
CNT = str(int(CNT) + 100)
|
||||
# print(SRS, EXCH, STRT_DT, END_DT, CNT, GAP)
|
||||
return get_overseas_future_time_price(SRS, EXCH, STRT_DT, END_DT, CNT, GAP, dataframe, file_name)
|
||||
|
||||
|
||||
# (3) 함수 실행
|
||||
# 접근토큰 발급
|
||||
ACCESS_TOKEN, ACCESS_TOKEN_EXPIRED = get_access_token()
|
||||
|
||||
# 사용자 입력 받기
|
||||
SRS = input("종목 코드를 입력하세요(ex. 6AZ23): ")
|
||||
EXCH = input("거래소 코드를 입력하세요(ex. CME): ")
|
||||
STRT_DT = input("조회 시작일을 입력하세요 (YYYYMMDD 형식): ")
|
||||
END_DT = input("조회 종료일을 입력하세요 (YYYYMMDD 형식): ")
|
||||
GAP = input("조회 간격(분)을 입력하세요 (ex. 1) : ")
|
||||
|
||||
# 해외선물분봉조회 호출
|
||||
print("Downloading...")
|
||||
result_dataframe = get_overseas_future_time_price(SRS=SRS, EXCH=EXCH, STRT_DT=STRT_DT, END_DT=END_DT, CNT="100", GAP=GAP)
|
||||
result_dataframe[:3]
|
||||
182
한국투자증권(API)/legacy/rest/get_ovsstk_chart_price.py
Normal file
182
한국투자증권(API)/legacy/rest/get_ovsstk_chart_price.py
Normal file
@@ -0,0 +1,182 @@
|
||||
## 해외주식분봉조회: 해외주식 종목의 특정 기간동안의 분봉을 받아서 엑셀로 저장하는 파이썬 샘플코드 (최대 약 1개월 기간 분봉 확인 가능)
|
||||
## config.yaml 파일을 설정한 뒤 get_ovsstk_chart_price.py를 실행해서 분봉 데이터 저장 가능
|
||||
## 해외주식 종목정보 마스터파일 다운로드 :
|
||||
### (나스닥) https://new.real.download.dws.co.kr/common/master/nasmst.cod.zip
|
||||
### (뉴욕) https://new.real.download.dws.co.kr/common/master/nysmst.cod.zip
|
||||
### (아멕스) https://new.real.download.dws.co.kr/common/master/amsmst.cod.zip
|
||||
|
||||
# (0) 필요 모듈 임포트
|
||||
from datetime import datetime, timedelta
|
||||
import pandas as pd
|
||||
import requests
|
||||
import json
|
||||
import yaml
|
||||
import time
|
||||
|
||||
# (1) 개인정보 파일 가져오기
|
||||
with open('config.yaml', encoding='UTF-8') as f:
|
||||
_cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
APP_KEY = _cfg['APP_KEY']
|
||||
APP_SECRET = _cfg['APP_SECRET']
|
||||
ACCESS_TOKEN = ""
|
||||
ACCESS_TOKEN_EXPIRED = ""
|
||||
CANO = _cfg['CANO']
|
||||
ACNT_PRDT_CD = _cfg['ACNT_PRDT_CD']
|
||||
URL_BASE = _cfg['URL_BASE']
|
||||
HTS_ID = _cfg['HTS_ID']
|
||||
print(APP_KEY, APP_SECRET, ACCESS_TOKEN, HTS_ID)
|
||||
|
||||
# (2) 함수 정의
|
||||
## 1. 접근 토큰 발급
|
||||
def get_access_token():
|
||||
""" OAuth 인증 > 접근토큰발급 """
|
||||
headers = {"content-type": "application/json"}
|
||||
body = {
|
||||
"grant_type": "client_credentials",
|
||||
"appkey": APP_KEY,
|
||||
"appsecret": APP_SECRET
|
||||
}
|
||||
PATH = "oauth2/tokenP"
|
||||
URL = f"{URL_BASE}/{PATH}"
|
||||
|
||||
time.sleep(0.1) # 유량제한 예방 (REST: 1초당 20건 제한)
|
||||
res = requests.post(URL, headers=headers, data=json.dumps(body))
|
||||
|
||||
if res.status_code == 200:
|
||||
try:
|
||||
access_token = res.json().get("access_token")
|
||||
access_token_expired = res.json().get("access_token_expired") # 수정된 키
|
||||
return access_token, access_token_expired
|
||||
except KeyError as e:
|
||||
print(f"토큰 발급 중 키 에러 발생: {e}")
|
||||
print(res.json())
|
||||
return None, None
|
||||
else:
|
||||
print("접근 토큰 발급이 불가능합니다. 응답 코드:", res.status_code)
|
||||
print("응답 내용:", res.json())
|
||||
return None, None
|
||||
|
||||
## 2. 해외 주식 분봉 조회
|
||||
def call_api(exc_code, sym_code, nmin, keyb="", next_value="", access_token=""):
|
||||
PATH = "/uapi/overseas-price/v1/quotations/inquire-time-itemchartprice"
|
||||
URL = f"{URL_BASE}/{PATH}"
|
||||
|
||||
params = {
|
||||
"AUTH": "",
|
||||
"EXCD": exc_code, # 거래소 코드
|
||||
"SYMB": sym_code, # 종목 코드
|
||||
"NMIN": nmin, # 분봉 주기
|
||||
"PINC": "1",
|
||||
"NEXT": next_value,
|
||||
"NREC": "120",
|
||||
"FILL": "",
|
||||
"KEYB": keyb
|
||||
}
|
||||
|
||||
print(params)
|
||||
|
||||
headers = {
|
||||
'content-type': 'application/json',
|
||||
'authorization': f'Bearer {access_token}', # 매개변수로 받은 access_token 사용
|
||||
'appkey': APP_KEY, # 미리 정의된 appkey 사용
|
||||
'appsecret': APP_SECRET, # 미리 정의된 appsecret 사용
|
||||
'tr_id': 'HHDFS76950200',
|
||||
'custtype': 'P'
|
||||
}
|
||||
|
||||
time.sleep(0.1) # 유량제한 예방 (REST: 1초당 20건 제한)
|
||||
response = requests.get(URL, headers=headers, params=params)
|
||||
|
||||
if response.status_code == 200:
|
||||
data = response.json()
|
||||
print("API 호출 성공")
|
||||
return data
|
||||
else:
|
||||
print(f"API 호출 실패, 상태 코드: {response.status_code}")
|
||||
try:
|
||||
print("오류 메시지:", response.json())
|
||||
except json.JSONDecodeError:
|
||||
print("JSON 디코딩 실패. 응답:", response.text)
|
||||
return None
|
||||
|
||||
## 3. 다음 조회용 keyb 값 계산 함수 (nmin에 따라 시간 조정)
|
||||
def get_next_keyb(output2, nmin):
|
||||
last_record = output2[-1]
|
||||
last_time_str = last_record["xymd"] + last_record["xhms"] # YYYYMMDDHHMMSS 형태의 문자열
|
||||
last_time = datetime.strptime(last_time_str, "%Y%m%d%H%M%S") # 문자열을 datetime 객체로 변환
|
||||
next_keyb_time = last_time - timedelta(minutes=nmin) # nmin 값만큼 이전 시간 계산
|
||||
return next_keyb_time.strftime("%Y%m%d%H%M%S") # 다시 문자열로 변환하여 반환
|
||||
|
||||
## 4. 데이터를 판다스 DataFrame으로 변환하는 함수
|
||||
def convert_to_dataframe(data):
|
||||
if "output2" in data:
|
||||
# output2 데이터를 데이터프레임으로 변환
|
||||
df = pd.DataFrame(data["output2"])
|
||||
|
||||
# 필요한 열만 선택 및 시간 데이터 처리
|
||||
df = df[['tymd', 'xhms', 'open', 'high', 'low', 'last', 'evol', 'eamt']]
|
||||
df['datetime'] = pd.to_datetime(df['tymd'] + df['xhms'], format='%Y%m%d%H%M%S')
|
||||
|
||||
# 데이터프레임 정리 (시간 순서대로 정렬)
|
||||
df = df.sort_values(by='datetime').reset_index(drop=True)
|
||||
|
||||
# 필요 없는 열 삭제
|
||||
df.drop(columns=['tymd', 'xhms'], inplace=True)
|
||||
|
||||
return df
|
||||
else:
|
||||
return pd.DataFrame()
|
||||
|
||||
## 5. 반복 조회 및 데이터 저장 함수
|
||||
def fetch_and_save_data(exc_code, sym_code, nmin, period, access_token):
|
||||
all_data = pd.DataFrame() # 전체 데이터를 저장할 데이터프레임
|
||||
first_call = call_api(exc_code, sym_code, nmin, access_token=access_token)
|
||||
|
||||
if not first_call:
|
||||
return
|
||||
|
||||
# 첫 조회 데이터 변환 및 저장
|
||||
df = convert_to_dataframe(first_call)
|
||||
all_data = pd.concat([all_data, df], ignore_index=True)
|
||||
|
||||
# 다음 조회를 위한 변수 초기화
|
||||
next_value = first_call["output1"]["next"]
|
||||
keyb = get_next_keyb(first_call["output2"], nmin) # nmin에 따라 1분 또는 n분 전 시간 계산
|
||||
|
||||
for _ in range(period - 1):
|
||||
# 다음 조회 실행
|
||||
next_call = call_api(exc_code, sym_code, nmin, keyb=keyb, next_value=next_value, access_token=access_token)
|
||||
|
||||
if not next_call:
|
||||
break
|
||||
|
||||
# 다음 조회 데이터 변환 및 저장
|
||||
df = convert_to_dataframe(next_call)
|
||||
all_data = pd.concat([all_data, df], ignore_index=True)
|
||||
|
||||
# 다음 조회를 위한 keyb 및 next 값 갱신
|
||||
next_value = next_call["output1"]["next"]
|
||||
keyb = get_next_keyb(next_call["output2"], nmin) # nmin에 따라 갱신된 keyb 값
|
||||
|
||||
# 결과 데이터프레임을 시간순으로 정렬하여 저장
|
||||
all_data = all_data.sort_values(by='datetime').reset_index(drop=True).drop_duplicates() # 중복 제거
|
||||
all_data.to_csv(f'{sym_code}_fetched_data.csv', index=False) # CSV 파일로 저장
|
||||
print(f"{sym_code} 데이터가 CSV 파일로 저장되었습니다.")
|
||||
|
||||
return all_data
|
||||
|
||||
# (4) 함수 실행
|
||||
## 1. 접근토큰 발급
|
||||
ACCESS_TOKEN, ACCESS_TOKEN_EXPIRED = get_access_token()
|
||||
|
||||
if ACCESS_TOKEN:
|
||||
## 2. 사용자 입력 받기
|
||||
exc_code = input("거래소 코드를 입력하세요 (예: NAS): ")
|
||||
sym_code = input("종목 코드를 입력하세요 (예: TSLA): ")
|
||||
nmin = int(input("분봉 주기를 입력하세요 (예: 1, 5, 10): "))
|
||||
period = int(input("반복 조회할 횟수를 입력하세요 (예: 4): "))
|
||||
|
||||
## 3. 사용자 입력에 따른 분봉 조회 후 데이터 저장
|
||||
all_data = fetch_and_save_data(exc_code, sym_code, nmin, period, ACCESS_TOKEN)
|
||||
else:
|
||||
print("토큰 발급 실패로 프로그램이 종료됩니다.")
|
||||
663
한국투자증권(API)/legacy/rest/kis_api.py
Normal file
663
한국투자증권(API)/legacy/rest/kis_api.py
Normal file
@@ -0,0 +1,663 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Mon Apr 18 11:23:04 2022
|
||||
|
||||
@author: KIS Developers
|
||||
"""
|
||||
|
||||
import time, copy
|
||||
import yaml
|
||||
import requests
|
||||
import json
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from collections import namedtuple
|
||||
from datetime import datetime
|
||||
|
||||
with open(r'kisdev_vi.yaml', encoding='UTF-8') as f:
|
||||
_cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
_TRENV = tuple()
|
||||
_last_auth_time = datetime.now()
|
||||
_autoReAuth = False
|
||||
_DEBUG = True
|
||||
_isPaper = True
|
||||
|
||||
_base_headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "text/plain",
|
||||
"charset": "UTF-8",
|
||||
'User-Agent': _cfg['my_agent']
|
||||
}
|
||||
|
||||
|
||||
def _getBaseHeader():
|
||||
if _autoReAuth: reAuth()
|
||||
return copy.deepcopy(_base_headers)
|
||||
|
||||
|
||||
def _setTRENV(cfg):
|
||||
nt1 = namedtuple('KISEnv', ['my_app','my_sec','my_acct', 'my_prod', 'my_token', 'my_url'])
|
||||
d = {
|
||||
'my_app': cfg['my_app'],
|
||||
'my_sec': cfg['my_sec'],
|
||||
'my_acct': cfg['my_acct'],
|
||||
'my_prod': cfg['my_prod'],
|
||||
'my_token': cfg['my_token'],
|
||||
'my_url' : cfg['my_url']
|
||||
}
|
||||
|
||||
global _TRENV
|
||||
_TRENV = nt1(**d)
|
||||
|
||||
def isPaperTrading():
|
||||
return _isPaper
|
||||
|
||||
def changeTREnv(token_key, svr='prod', product='01'):
|
||||
cfg = dict()
|
||||
|
||||
global _isPaper
|
||||
if svr == 'prod':
|
||||
ak1 = 'my_app'
|
||||
ak2 = 'my_sec'
|
||||
_isPaper = False
|
||||
elif svr == 'vps':
|
||||
ak1 = 'paper_app'
|
||||
ak2 = 'paper_sec'
|
||||
_isPaper = True
|
||||
|
||||
cfg['my_app'] = _cfg[ak1]
|
||||
cfg['my_sec'] = _cfg[ak2]
|
||||
|
||||
if svr == 'prod' and product == '01':
|
||||
cfg['my_acct'] = _cfg['my_acct_stock']
|
||||
elif svr == 'prod' and product == '03':
|
||||
cfg['my_acct'] = _cfg['my_acct_future']
|
||||
elif svr == 'vps' and product == '01':
|
||||
cfg['my_acct'] = _cfg['my_paper_stock']
|
||||
elif svr == 'vps' and product == '03':
|
||||
cfg['my_acct'] = _cfg['my_paper_future']
|
||||
|
||||
cfg['my_prod'] = product
|
||||
cfg['my_token'] = token_key
|
||||
cfg['my_url'] = _cfg[svr]
|
||||
|
||||
_setTRENV(cfg)
|
||||
|
||||
|
||||
def _getResultObject(json_data):
|
||||
_tc_ = namedtuple('res', json_data.keys())
|
||||
|
||||
return _tc_(**json_data)
|
||||
|
||||
def auth(svr='prod', product='01'):
|
||||
|
||||
p = {
|
||||
"grant_type": "client_credentials",
|
||||
}
|
||||
print(svr)
|
||||
if svr == 'prod':
|
||||
ak1 = 'my_app'
|
||||
ak2 = 'my_sec'
|
||||
elif svr == 'vps':
|
||||
ak1 = 'paper_app'
|
||||
ak2 = 'paper_sec'
|
||||
|
||||
p["appkey"] = _cfg[ak1]
|
||||
p["appsecret"] = _cfg[ak2]
|
||||
|
||||
|
||||
url = f'{_cfg[svr]}/oauth2/tokenP'
|
||||
|
||||
res = requests.post(url, data=json.dumps(p), headers=_getBaseHeader())
|
||||
rescode = res.status_code
|
||||
if rescode == 200:
|
||||
my_token = _getResultObject(res.json()).access_token
|
||||
else:
|
||||
print('Get Authentification token fail!\nYou have to restart your app!!!')
|
||||
return
|
||||
|
||||
changeTREnv(f"Bearer {my_token}", svr, product)
|
||||
|
||||
_base_headers["authorization"] = _TRENV.my_token
|
||||
_base_headers["appkey"] = _TRENV.my_app
|
||||
_base_headers["appsecret"] = _TRENV.my_sec
|
||||
|
||||
global _last_auth_time
|
||||
_last_auth_time = datetime.now()
|
||||
|
||||
if (_DEBUG):
|
||||
print(f'[{_last_auth_time}] => get AUTH Key completed!')
|
||||
|
||||
#end of initialize
|
||||
def reAuth(svr='prod', product='01'):
|
||||
n2 = datetime.now()
|
||||
if (n2-_last_auth_time).seconds >= 86400:
|
||||
auth(svr, product)
|
||||
|
||||
def getEnv():
|
||||
return _cfg
|
||||
def getTREnv():
|
||||
return _TRENV
|
||||
|
||||
#주문 API에서 사용할 hash key값을 받아 header에 설정해 주는 함수
|
||||
# Input: HTTP Header, HTTP post param
|
||||
# Output: None
|
||||
def set_order_hash_key(h, p):
|
||||
|
||||
url = f"{getTREnv().my_url}/uapi/hashkey"
|
||||
|
||||
res = requests.post(url, data=json.dumps(p), headers=h)
|
||||
rescode = res.status_code
|
||||
if rescode == 200:
|
||||
h['hashkey'] = _getResultObject(res.json()).HASH
|
||||
else:
|
||||
print("Error:", rescode)
|
||||
|
||||
class APIResp:
|
||||
def __init__(self, resp):
|
||||
self._rescode = resp.status_code
|
||||
self._resp = resp
|
||||
self._header = self._setHeader()
|
||||
self._body = self._setBody()
|
||||
self._err_code = self._body.rt_cd
|
||||
self._err_message = self._body.msg1
|
||||
|
||||
def getResCode(self):
|
||||
return self._rescode
|
||||
|
||||
def _setHeader(self):
|
||||
fld = dict()
|
||||
for x in self._resp.headers.keys():
|
||||
if x.islower():
|
||||
fld[x] = self._resp.headers.get(x)
|
||||
_th_ = namedtuple('header', fld.keys())
|
||||
|
||||
return _th_(**fld)
|
||||
|
||||
def _setBody(self):
|
||||
_tb_ = namedtuple('body', self._resp.json().keys())
|
||||
|
||||
return _tb_(**self._resp.json())
|
||||
|
||||
def getHeader(self):
|
||||
return self._header
|
||||
|
||||
def getBody(self):
|
||||
return self._body
|
||||
|
||||
def getResponse(self):
|
||||
return self._resp
|
||||
|
||||
def isOK(self):
|
||||
try:
|
||||
if(self.getBody().rt_cd == '0'):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def getErrorCode(self):
|
||||
return self._err_code
|
||||
|
||||
def getErrorMessage(self):
|
||||
return self._err_message
|
||||
|
||||
def printAll(self):
|
||||
print("<Header>")
|
||||
for x in self.getHeader()._fields:
|
||||
print(f'\t-{x}: {getattr(self.getHeader(), x)}')
|
||||
print("<Body>")
|
||||
for x in self.getBody()._fields:
|
||||
print(f'\t-{x}: {getattr(self.getBody(), x)}')
|
||||
|
||||
def printError(self):
|
||||
print('-------------------------------\nError in response: ', self.getResCode())
|
||||
print(self.getBody().rt_cd, self.getErrorCode(), self.getErrorMessage())
|
||||
print('-------------------------------')
|
||||
|
||||
# end of class APIResp
|
||||
|
||||
|
||||
########### API call wrapping
|
||||
|
||||
def _url_fetch(api_url, ptr_id, params, appendHeaders=None, postFlag=False, hashFlag=True):
|
||||
url = f"{getTREnv().my_url}{api_url}"
|
||||
|
||||
headers = _getBaseHeader()
|
||||
|
||||
#추가 Header 설정
|
||||
tr_id = ptr_id
|
||||
if ptr_id[0] in ('T', 'J', 'C'):
|
||||
if isPaperTrading():
|
||||
tr_id = 'V' + ptr_id[1:]
|
||||
|
||||
headers["tr_id"] = tr_id
|
||||
headers["custtype"] = "P"
|
||||
|
||||
if appendHeaders is not None:
|
||||
if len(appendHeaders) > 0:
|
||||
for x in appendHeaders.keys():
|
||||
headers[x] = appendHeaders.get(x)
|
||||
|
||||
if(_DEBUG):
|
||||
print("< Sending Info >")
|
||||
print(f"URL: {url}, TR: {tr_id}")
|
||||
print(f"<header>\n{headers}")
|
||||
print(f"<body>\n{params}")
|
||||
|
||||
if (postFlag):
|
||||
if(hashFlag): set_order_hash_key(headers, params)
|
||||
res = requests.post(url, headers=headers, data=json.dumps(params))
|
||||
else:
|
||||
res = requests.get(url, headers=headers, params=params)
|
||||
|
||||
if res.status_code == 200:
|
||||
ar = APIResp(res)
|
||||
if (_DEBUG): ar.printAll()
|
||||
return ar
|
||||
else:
|
||||
print("Error Code : " + str(res.status_code) + " | " + res.text)
|
||||
return None
|
||||
|
||||
|
||||
# 계좌 잔고를 DataFrame 으로 반환
|
||||
# Input: None (Option) rtCashFlag=True 면 예수금 총액을 반환하게 된다
|
||||
# Output: DataFrame (Option) rtCashFlag=True 면 예수금 총액을 반환하게 된다
|
||||
|
||||
def get_acct_balance(rtCashFlag=False):
|
||||
url = '/uapi/domestic-stock/v1/trading/inquire-balance'
|
||||
tr_id = "TTTC8434R"
|
||||
|
||||
params = {
|
||||
'CANO': getTREnv().my_acct,
|
||||
'ACNT_PRDT_CD': '01',
|
||||
'AFHR_FLPR_YN': 'N',
|
||||
'FNCG_AMT_AUTO_RDPT_YN': 'N',
|
||||
'FUND_STTL_ICLD_YN': 'N',
|
||||
'INQR_DVSN': '01',
|
||||
'OFL_YN': 'N',
|
||||
'PRCS_DVSN': '01',
|
||||
'UNPR_DVSN': '01',
|
||||
'CTX_AREA_FK100': '',
|
||||
'CTX_AREA_NK100': ''
|
||||
}
|
||||
|
||||
t1 = _url_fetch(url, tr_id, params)
|
||||
if rtCashFlag and t1.isOK():
|
||||
r2 = t1.getBody().output2
|
||||
return int(r2[0]['dnca_tot_amt'])
|
||||
|
||||
output1 = t1.getBody().output1
|
||||
if t1.isOK() and output1: #body 의 rt_cd 가 0 인 경우만 성공
|
||||
tdf = pd.DataFrame(output1)
|
||||
tdf.set_index('pdno', inplace=True)
|
||||
cf1 = ['prdt_name','hldg_qty', 'ord_psbl_qty', 'pchs_avg_pric', 'evlu_pfls_rt', 'prpr', 'bfdy_cprs_icdc', 'fltt_rt']
|
||||
cf2 = ['종목명', '보유수량', '매도가능수량', '매입단가', '수익율', '현재가' ,'전일대비', '등락']
|
||||
tdf = tdf[cf1]
|
||||
tdf[cf1[1:]] = tdf[cf1[1:]].apply(pd.to_numeric)
|
||||
ren_dict = dict(zip(cf1, cf2))
|
||||
return tdf.rename(columns=ren_dict)
|
||||
|
||||
else:
|
||||
t1.printError()
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
|
||||
#종목의 주식, ETF, 선물/옵션 등의 구분값을 반환. 현재는 무조건 주식(J)만 반환
|
||||
def _getStockDiv(stock_no):
|
||||
return 'J'
|
||||
|
||||
# 종목별 현재가를 dict 로 반환
|
||||
# Input: 종목코드
|
||||
# Output: 현재가 Info dictionary. 반환된 dict 가 len(dict) < 1 경우는 에러로 보면 됨
|
||||
|
||||
def get_current_price(stock_no):
|
||||
url = "/uapi/domestic-stock/v1/quotations/inquire-price"
|
||||
tr_id = "FHKST01010100"
|
||||
|
||||
params = {
|
||||
'FID_COND_MRKT_DIV_CODE': _getStockDiv(stock_no),
|
||||
'FID_INPUT_ISCD': stock_no
|
||||
}
|
||||
|
||||
t1 = _url_fetch(url, tr_id, params)
|
||||
|
||||
if t1.isOK():
|
||||
return t1.getBody().output
|
||||
else:
|
||||
t1.printError()
|
||||
return dict()
|
||||
|
||||
# 주문 base function
|
||||
# Input: 종목코드, 주문수량, 주문가격, Buy Flag(If True, it's Buy order), order_type="00"(지정가)
|
||||
# Output: HTTP Response
|
||||
|
||||
def do_order(stock_code, order_qty, order_price, prd_code="01", buy_flag=True, order_type="00"):
|
||||
|
||||
url = "/uapi/domestic-stock/v1/trading/order-cash"
|
||||
|
||||
if buy_flag:
|
||||
tr_id = "TTTC0802U" #buy
|
||||
else:
|
||||
tr_id = "TTTC0801U" #sell
|
||||
|
||||
params = {
|
||||
'CANO': getTREnv().my_acct,
|
||||
'ACNT_PRDT_CD': prd_code,
|
||||
'PDNO': stock_code,
|
||||
'ORD_DVSN': order_type,
|
||||
'ORD_QTY': str(order_qty),
|
||||
'ORD_UNPR': str(order_price),
|
||||
'CTAC_TLNO': '',
|
||||
'SLL_TYPE': '01',
|
||||
'ALGO_NO': ''
|
||||
}
|
||||
|
||||
t1 = _url_fetch(url, tr_id, params, postFlag=True, hashFlag=True)
|
||||
|
||||
if t1.isOK():
|
||||
return t1
|
||||
else:
|
||||
t1.printError()
|
||||
return None
|
||||
|
||||
# 사자 주문. 내부적으로는 do_order 를 호출한다.
|
||||
# Input: 종목코드, 주문수량, 주문가격
|
||||
# Output: True, False
|
||||
|
||||
def do_sell(stock_code, order_qty, order_price, prd_code="01", order_type="00"):
|
||||
t1 = do_order(stock_code, order_qty, order_price, buy_flag=False, order_type=order_type)
|
||||
return t1.isOK()
|
||||
|
||||
# 팔자 주문. 내부적으로는 do_order 를 호출한다.
|
||||
# Input: 종목코드, 주문수량, 주문가격
|
||||
# Output: True, False
|
||||
|
||||
def do_buy(stock_code, order_qty, order_price, prd_code="01", order_type="00"):
|
||||
t1 = do_order(stock_code, order_qty, order_price, buy_flag=True, order_type=order_type)
|
||||
return t1.isOK()
|
||||
|
||||
# 정정취소 가능한 주문 목록을 DataFrame 으로 반환
|
||||
# Input: None
|
||||
# Output: DataFrame
|
||||
|
||||
def get_orders(prd_code='01'):
|
||||
url = "/uapi/domestic-stock/v1/trading/inquire-psbl-rvsecncl"
|
||||
|
||||
tr_id = "TTTC8036R"
|
||||
|
||||
params = {
|
||||
"CANO": getTREnv().my_acct,
|
||||
"ACNT_PRDT_CD": prd_code,
|
||||
"CTX_AREA_FK100": '',
|
||||
"CTX_AREA_NK100": '',
|
||||
"INQR_DVSN_1": '0',
|
||||
"INQR_DVSN_2": '0'
|
||||
}
|
||||
|
||||
t1 = _url_fetch(url, tr_id, params)
|
||||
if t1.isOK():
|
||||
tdf = pd.DataFrame(t1.getBody().output)
|
||||
tdf.set_index('odno', inplace=True)
|
||||
cf1 = ['pdno', 'ord_qty', 'ord_unpr', 'ord_tmd', 'ord_gno_brno','orgn_odno']
|
||||
cf2 = ['종목코드', '주문수량', '주문가격', '시간', '주문점', '원번호']
|
||||
tdf = tdf[cf1]
|
||||
ren_dict = dict(zip(cf1, cf2))
|
||||
|
||||
return tdf.rename(columns=ren_dict)
|
||||
|
||||
else:
|
||||
t1.printError()
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
# 특정 주문 취소(01)/정정(02)
|
||||
# Input: 주문 번호(get_orders 를 호출하여 얻은 DataFrame 의 index column 값이 취소 가능한 주문번호임)
|
||||
# 주문점(통상 06010), 주문수량, 주문가격, 상품코드(01), 주문유형(00), 정정구분(취소-02, 정정-01)
|
||||
# Output: APIResp object
|
||||
|
||||
def _do_cancel_revise(order_no, order_branch, order_qty, order_price, prd_code, order_dv, cncl_dv, qty_all_yn):
|
||||
url = "/uapi/domestic-stock/v1/trading/order-rvsecncl"
|
||||
|
||||
tr_id = "TTTC0803U"
|
||||
|
||||
params = {
|
||||
"CANO": getTREnv().my_acct,
|
||||
"ACNT_PRDT_CD": prd_code,
|
||||
"KRX_FWDG_ORD_ORGNO": order_branch,
|
||||
"ORGN_ODNO": order_no,
|
||||
"ORD_DVSN": order_dv,
|
||||
"RVSE_CNCL_DVSN_CD": cncl_dv, #취소(02)
|
||||
"ORD_QTY": str(order_qty),
|
||||
"ORD_UNPR": str(order_price),
|
||||
"QTY_ALL_ORD_YN": qty_all_yn
|
||||
}
|
||||
|
||||
t1 = _url_fetch(url, tr_id, params=params, postFlag=True)
|
||||
|
||||
if t1.isOK():
|
||||
return t1
|
||||
else:
|
||||
t1.printError()
|
||||
return None
|
||||
|
||||
# 특정 주문 취소
|
||||
#
|
||||
def do_cancel(order_no, order_qty, order_price="01", order_branch='06010', prd_code='01', order_dv='00', cncl_dv='02',qty_all_yn="Y"):
|
||||
return _do_cancel_revise(order_no, order_branch, order_qty, order_price, prd_code, order_dv, cncl_dv, qty_all_yn)
|
||||
|
||||
# 특정 주문 정정
|
||||
#
|
||||
def do_revise(order_no, order_qty, order_price, order_branch='06010', prd_code='01', order_dv='00', cncl_dv='01', qty_all_yn="Y"):
|
||||
return _do_cancel_revise(order_no, order_branch, order_qty, order_price, prd_code, order_dv, cncl_dv, qty_all_yn)
|
||||
|
||||
# 모든 주문 취소
|
||||
# Input: None
|
||||
# Output: None
|
||||
|
||||
def do_cancel_all():
|
||||
tdf = get_orders()
|
||||
od_list = tdf.index.to_list()
|
||||
qty_list = tdf['주문수량'].to_list()
|
||||
price_list = tdf['주문가격'].to_list()
|
||||
branch_list = tdf['주문점'].to_list()
|
||||
cnt = 0
|
||||
for x in od_list:
|
||||
ar = do_cancel(x, qty_list[cnt], price_list[cnt], branch_list[cnt])
|
||||
cnt += 1
|
||||
print(ar.getErrorCode(), ar.getErrorMessage())
|
||||
time.sleep(.2)
|
||||
|
||||
|
||||
|
||||
# 내 계좌의 일별 주문 체결 조회
|
||||
# Input: 시작일, 종료일 (Option)지정하지 않으면 현재일
|
||||
# output: DataFrame
|
||||
|
||||
def get_my_complete(sdt, edt=None, prd_code='01', zipFlag=True):
|
||||
url = "/uapi/domestic-stock/v1/trading/inquire-daily-ccld"
|
||||
tr_id = "TTTC8001R"
|
||||
|
||||
if (edt is None):
|
||||
ltdt = datetime.now().strftime('%Y%m%d')
|
||||
else:
|
||||
ltdt = edt
|
||||
|
||||
params = {
|
||||
"CANO": getTREnv().my_acct,
|
||||
"ACNT_PRDT_CD": prd_code,
|
||||
"INQR_STRT_DT": sdt,
|
||||
"INQR_END_DT": ltdt,
|
||||
"SLL_BUY_DVSN_CD": '00',
|
||||
"INQR_DVSN": '00',
|
||||
"PDNO": "",
|
||||
"CCLD_DVSN": "00",
|
||||
"ORD_GNO_BRNO": "",
|
||||
"ODNO":"",
|
||||
"INQR_DVSN_3": "00",
|
||||
"INQR_DVSN_1": "",
|
||||
"INQR_DVSN_2": "",
|
||||
"CTX_AREA_FK100": "",
|
||||
"CTX_AREA_NK100": ""
|
||||
}
|
||||
|
||||
t1 = _url_fetch(url, tr_id, params)
|
||||
|
||||
#output1 과 output2 로 나뉘어서 결과가 옴. 지금은 output1만 DF 로 변환
|
||||
if t1.isOK():
|
||||
tdf = pd.DataFrame(t1.getBody().output1)
|
||||
tdf.set_index('odno', inplace=True)
|
||||
if (zipFlag):
|
||||
return tdf[['ord_dt','orgn_odno', 'sll_buy_dvsn_cd_name', 'pdno', 'ord_qty', 'ord_unpr', 'avg_prvs', 'cncl_yn','tot_ccld_amt','rmn_qty']]
|
||||
else:
|
||||
return tdf
|
||||
else:
|
||||
t1.printError()
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
# 매수 가능(현금) 조회
|
||||
# Input: None
|
||||
# Output: 매수 가능 현금 액수
|
||||
def get_buyable_cash(stock_code='', qry_price=0, prd_code='01'):
|
||||
url = "/uapi/domestic-stock/v1/trading/inquire-daily-ccld"
|
||||
tr_id = "TTTC8908R"
|
||||
|
||||
params = {
|
||||
"CANO": getTREnv().my_acct,
|
||||
"ACNT_PRDT_CD": prd_code,
|
||||
"PDNO": stock_code,
|
||||
"ORD_UNPR": str(qry_price),
|
||||
"ORD_DVSN": "02",
|
||||
"CMA_EVLU_AMT_ICLD_YN": "Y", #API 설명부분 수정 필요 (YN)
|
||||
"OVRS_ICLD_YN": "N"
|
||||
}
|
||||
|
||||
t1 = _url_fetch(url, tr_id, params)
|
||||
|
||||
if t1.isOK():
|
||||
return int(t1.getBody().output['ord_psbl_cash'])
|
||||
else:
|
||||
t1.printError()
|
||||
return 0
|
||||
|
||||
|
||||
# 시세 Function
|
||||
|
||||
# 종목별 체결 Data
|
||||
# Input: 종목코드
|
||||
# Output: 체결 Data DataFrame
|
||||
# 주식체결시간, 주식현재가, 전일대비, 전일대비부호, 체결거래량, 당일 체결강도, 전일대비율
|
||||
def get_stock_completed(stock_no):
|
||||
url = "/uapi/domestic-stock/v1/quotations/inquire-ccnl"
|
||||
|
||||
tr_id = "FHKST01010300"
|
||||
|
||||
params = {
|
||||
"FID_COND_MRKT_DIV_CODE": "J",
|
||||
"FID_INPUT_ISCD": stock_no
|
||||
}
|
||||
|
||||
t1 = _url_fetch(url, tr_id, params)
|
||||
|
||||
if t1.isOK():
|
||||
return pd.DataFrame(t1.getBody().output)
|
||||
else:
|
||||
t1.printError()
|
||||
return pd.DataFrame()
|
||||
|
||||
# 종목별 history data (현재 기준 30개만 조회 가능)
|
||||
# Input: 종목코드, 구분(D, W, M 기본값은 D)
|
||||
# output: 시세 History DataFrame
|
||||
def get_stock_history(stock_no, gb_cd='D'):
|
||||
url = "/uapi/domestic-stock/v1/quotations/inquire-daily-price"
|
||||
tr_id = "FHKST01010400"
|
||||
|
||||
params = {
|
||||
"FID_COND_MRKT_DIV_CODE": _getStockDiv(stock_no),
|
||||
"FID_INPUT_ISCD": stock_no,
|
||||
"FID_PERIOD_DIV_CODE": gb_cd,
|
||||
"FID_ORG_ADJ_PRC": "0000000001"
|
||||
}
|
||||
|
||||
t1 = _url_fetch(url, tr_id, params)
|
||||
|
||||
if t1.isOK():
|
||||
return pd.DataFrame(t1.getBody().output)
|
||||
else:
|
||||
t1.printError()
|
||||
return pd.DataFrame()
|
||||
|
||||
# 종목별 history data 를 표준 OHLCV DataFrame 으로 반환
|
||||
# Input: 종목코드, 구분(D, W, M 기본값은 D), (Option)adVar 을 True 로 설정하면
|
||||
# OHLCV 외에 inter_volatile 과 pct_change 를 추가로 반환한다.
|
||||
# output: 시세 History OHLCV DataFrame
|
||||
def get_stock_history_by_ohlcv(stock_no, gb_cd='D', adVar=False):
|
||||
hdf1 = get_stock_history(stock_no, gb_cd)
|
||||
|
||||
chosend_fld = ['stck_bsop_date', 'stck_oprc', 'stck_hgpr', 'stck_lwpr', 'stck_clpr', 'acml_vol']
|
||||
renamed_fld = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume']
|
||||
|
||||
hdf1 = hdf1[chosend_fld]
|
||||
ren_dict = dict()
|
||||
i = 0
|
||||
for x in chosend_fld:
|
||||
ren_dict[x] = renamed_fld[i]
|
||||
i += 1
|
||||
|
||||
hdf1.rename(columns = ren_dict, inplace=True)
|
||||
hdf1[['Date']] = hdf1[['Date']].apply(pd.to_datetime)
|
||||
hdf1[['Open','High','Low','Close','Volume']] = hdf1[['Open','High','Low','Close','Volume']].apply(pd.to_numeric)
|
||||
hdf1.set_index('Date', inplace=True)
|
||||
|
||||
if(adVar):
|
||||
hdf1['inter_volatile'] = (hdf1['High']-hdf1['Low'])/hdf1['Close']
|
||||
hdf1['pct_change'] = (hdf1['Close'] - hdf1['Close'].shift(-1))/hdf1['Close'].shift(-1) * 100
|
||||
|
||||
|
||||
return hdf1
|
||||
|
||||
|
||||
# 투자자별 매매 동향
|
||||
# Input: 종목코드
|
||||
# output: 매매 동향 History DataFrame (Date, PerBuy, ForBuy, OrgBuy) 30개 row를 반환
|
||||
def get_stock_investor(stock_no):
|
||||
url = "/uapi/domestic-stock/v1/quotations/inquire-investor"
|
||||
tr_id = "FHKST01010900"
|
||||
|
||||
params = {
|
||||
"FID_COND_MRKT_DIV_CODE": _getStockDiv(stock_no),
|
||||
"FID_INPUT_ISCD": stock_no
|
||||
}
|
||||
|
||||
t1 = _url_fetch(url, tr_id, params)
|
||||
|
||||
if t1.isOK():
|
||||
hdf1 = pd.DataFrame(t1.getBody().output)
|
||||
|
||||
chosend_fld = ['stck_bsop_date', 'prsn_ntby_qty', 'frgn_ntby_qty', 'orgn_ntby_qty']
|
||||
renamed_fld = ['Date', 'PerBuy', 'ForBuy', 'OrgBuy']
|
||||
|
||||
hdf1 = hdf1[chosend_fld]
|
||||
ren_dict = dict()
|
||||
i = 0
|
||||
for x in chosend_fld:
|
||||
ren_dict[x] = renamed_fld[i]
|
||||
i += 1
|
||||
|
||||
hdf1.rename(columns = ren_dict, inplace=True)
|
||||
hdf1[['Date']] = hdf1[['Date']].apply(pd.to_datetime)
|
||||
hdf1[['PerBuy','ForBuy','OrgBuy']] = hdf1[['PerBuy','ForBuy','OrgBuy']].apply(pd.to_numeric)
|
||||
hdf1['EtcBuy'] = (hdf1['PerBuy'] + hdf1['ForBuy'] + hdf1['OrgBuy']) * -1
|
||||
hdf1.set_index('Date', inplace=True)
|
||||
#sum을 맨 마지막에 추가하는 경우
|
||||
#tdf.append(tdf.sum(numeric_only=True), ignore_index=True) <- index를 없애고 만드는 경우
|
||||
#tdf.loc['Total'] = tdf.sum() <- index 에 Total 을 추가하는 경우
|
||||
return hdf1
|
||||
else:
|
||||
t1.printError()
|
||||
return pd.DataFrame()
|
||||
208
한국투자증권(API)/legacy/rest/kis_auth.py
Normal file
208
한국투자증권(API)/legacy/rest/kis_auth.py
Normal file
@@ -0,0 +1,208 @@
|
||||
"""
|
||||
해당 접근토큰발급관리 파일을 그대로 사용하시면 오류 발생할 수 있습니다.
|
||||
토큰저장 및 발급 함수 등을 참고하시되 본인 환경에 맞게 코드 수정하셔서 사용하시기 바랍니다.
|
||||
|
||||
Created on Wed Feb 15 16:57:19 2023
|
||||
|
||||
@author: Administrator
|
||||
"""
|
||||
|
||||
import time, copy
|
||||
import yaml
|
||||
import requests
|
||||
import json
|
||||
|
||||
import os
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from collections import namedtuple
|
||||
from datetime import datetime
|
||||
|
||||
config_root = os.getcwd() + '\\'
|
||||
# config_root = 'd:\\KIS\\config\\' # 토큰 파일이 저장될 폴더, 제3자가 찾지 어렵도록 경로 설정하시기 바랍니다.
|
||||
#token_tmp = config_root + 'KIS000000' # 토큰 로컬저장시 파일 이름 지정, 파일이름을 토큰값이 유추가능한 파일명은 삼가바랍니다.
|
||||
#token_tmp = config_root + 'KIS' + datetime.today().strftime("%Y%m%d%H%M%S") # 토큰 로컬저장시 파일명 년월일시분초
|
||||
token_tmp = config_root + 'KIS' + datetime.today().strftime("%Y%m%d") # 토큰 로컬저장시 파일명 년월일
|
||||
|
||||
# 접근토큰 관리하는 파일 존재여부 체크, 없으면 생성
|
||||
if os.path.exists(token_tmp) == False:
|
||||
f = open(token_tmp, "w+")
|
||||
|
||||
# 앱키, 앱시크리트, 토큰, 계좌번호 등 저장관리, 자신만의 경로와 파일명으로 설정하시기 바랍니다.
|
||||
# pip install PyYAML (패키지설치)
|
||||
with open(config_root + 'kis_devlp.yaml', encoding='UTF-8') as f:
|
||||
_cfg = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
_TRENV = tuple()
|
||||
_last_auth_time = datetime.now()
|
||||
_autoReAuth = False
|
||||
_DEBUG = False
|
||||
_isPaper = False
|
||||
|
||||
# 기본 헤더값 정의
|
||||
_base_headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "text/plain",
|
||||
"charset": "UTF-8",
|
||||
'User-Agent': _cfg['my_agent']
|
||||
}
|
||||
|
||||
# 토큰 발급 받아 저장 (토큰값, 토큰 유효시간,1일, 6시간 이내 발급신청시는 기존 토큰값과 동일, 발급시 알림톡 발송)
|
||||
def save_token(my_token, my_expired):
|
||||
valid_date = datetime.strptime(my_expired, '%Y-%m-%d %H:%M:%S')
|
||||
print('Save token date: ', valid_date)
|
||||
with open(token_tmp, 'w', encoding='utf-8') as f:
|
||||
f.write(f'token: {my_token}\n')
|
||||
f.write(f'valid-date: {valid_date}\n')
|
||||
|
||||
|
||||
# 토큰 확인 (토큰값, 토큰 유효시간_1일, 6시간 이내 발급신청시는 기존 토큰값과 동일, 발급시 알림톡 발송)
|
||||
def read_token():
|
||||
try:
|
||||
# 토큰이 저장된 파일 읽기
|
||||
with open(token_tmp, encoding='UTF-8') as f:
|
||||
tkg_tmp = yaml.load(f, Loader=yaml.FullLoader)
|
||||
|
||||
# 토큰 만료 일,시간
|
||||
exp_dt = datetime.strftime(tkg_tmp['valid-date'], '%Y-%m-%d %H:%M:%S')
|
||||
# 현재일자,시간
|
||||
now_dt = datetime.today().strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
print('expire dt: ', exp_dt, ' vs now dt:', now_dt)
|
||||
# 저장된 토큰 만료일자 체크 (만료일시 > 현재일시 인경우 보관 토큰 리턴)
|
||||
if exp_dt > now_dt:
|
||||
return tkg_tmp['token']
|
||||
else:
|
||||
# print('Need new token: ', tkg_tmp['valid-date'])
|
||||
return None
|
||||
except Exception as e:
|
||||
# print('read token error: ', e)
|
||||
return None
|
||||
|
||||
# 토큰 유효시간 체크해서 만료된 토큰이면 재발급처리
|
||||
def _getBaseHeader():
|
||||
if _autoReAuth: reAuth()
|
||||
return copy.deepcopy(_base_headers)
|
||||
|
||||
|
||||
# 가져오기 : 앱키, 앱시크리트, 종합계좌번호(계좌번호 중 숫자8자리), 계좌상품코드(계좌번호 중 숫자2자리), 토큰, 도메인
|
||||
def _setTRENV(cfg):
|
||||
nt1 = namedtuple('KISEnv', ['my_app', 'my_sec', 'my_acct', 'my_prod', 'my_token', 'my_url'])
|
||||
d = {
|
||||
'my_app': cfg['my_app'], # 앱키
|
||||
'my_sec': cfg['my_sec'], # 앱시크리트
|
||||
'my_acct': cfg['my_acct'], # 종합계좌번호(8자리)
|
||||
'my_prod': cfg['my_prod'], # 계좌상품코드(2자리)
|
||||
'my_token': cfg['my_token'], # 토큰
|
||||
'my_url': cfg['my_url'] # 실전 도메인 (https://openapi.koreainvestment.com:9443)
|
||||
} # 모의 도메인 (https://openapivts.koreainvestment.com:29443)
|
||||
|
||||
global _TRENV
|
||||
_TRENV = nt1(**d)
|
||||
|
||||
|
||||
def isPaperTrading(): # 모의투자 매매
|
||||
return _isPaper
|
||||
|
||||
|
||||
# 실전투자면 'prod', 모의투자면 'vps'를 셋팅 하시기 바랍니다.
|
||||
def changeTREnv(token_key, svr='prod', product='01'):
|
||||
cfg = dict()
|
||||
|
||||
global _isPaper
|
||||
if svr == 'prod': # 실전투자
|
||||
ak1 = 'my_app' # 실전투자용 앱키
|
||||
ak2 = 'my_sec' # 실전투자용 앱시크리트
|
||||
_isPaper = False
|
||||
elif svr == 'vps': # 모의투자
|
||||
ak1 = 'paper_app' # 모의투자용 앱키
|
||||
ak2 = 'paper_sec' # 모의투자용 앱시크리트
|
||||
_isPaper = True
|
||||
|
||||
cfg['my_app'] = _cfg[ak1]
|
||||
cfg['my_sec'] = _cfg[ak2]
|
||||
|
||||
if svr == 'prod' and product == '01': # 실전투자 주식투자, 위탁계좌, 투자계좌
|
||||
cfg['my_acct'] = _cfg['my_acct_stock']
|
||||
elif svr == 'prod' and product == '03': # 실전투자 선물옵션(파생)
|
||||
cfg['my_acct'] = _cfg['my_acct_future']
|
||||
elif svr == 'vps' and product == '01': # 모의투자 주식투자, 위탁계좌, 투자계좌
|
||||
cfg['my_acct'] = _cfg['my_paper_stock']
|
||||
elif svr == 'vps' and product == '03': # 모의투자 선물옵션(파생)
|
||||
cfg['my_acct'] = _cfg['my_paper_future']
|
||||
|
||||
cfg['my_prod'] = product
|
||||
cfg['my_token'] = token_key
|
||||
cfg['my_url'] = _cfg[svr]
|
||||
|
||||
_setTRENV(cfg)
|
||||
|
||||
|
||||
def _getResultObject(json_data):
|
||||
_tc_ = namedtuple('res', json_data.keys())
|
||||
|
||||
return _tc_(**json_data)
|
||||
|
||||
|
||||
# Token 발급, 유효기간 1일, 6시간 이내 발급시 기존 token값 유지, 발급시 알림톡 무조건 발송
|
||||
def auth(svr='prod', product='01', url=None):
|
||||
p = {
|
||||
"grant_type": "client_credentials",
|
||||
}
|
||||
# 개인 환경파일 "kis_devlp.yaml" 파일을 참조하여 앱키, 앱시크리트 정보 가져오기
|
||||
# 개인 환경파일명과 위치는 고객님만 아는 위치로 설정 바랍니다.
|
||||
if svr == 'prod': # 실전투자
|
||||
ak1 = 'my_app' # 앱키 (실전투자용)
|
||||
ak2 = 'my_sec' # 앱시크리트 (실전투자용)
|
||||
elif svr == 'vps': # 모의투자
|
||||
ak1 = 'paper_app' # 앱키 (모의투자용)
|
||||
ak2 = 'paper_sec' # 앱시크리트 (모의투자용)
|
||||
|
||||
# 앱키, 앱시크리트 가져오기
|
||||
p["appkey"] = _cfg[ak1]
|
||||
p["appsecret"] = _cfg[ak2]
|
||||
|
||||
# 기존 발급된 토큰이 있는지 확인
|
||||
saved_token = read_token() # 기존 발급 토큰 확인
|
||||
print("saved_token: ", saved_token)
|
||||
if saved_token is None: # 기존 발급 토큰 확인이 안되면 발급처리
|
||||
url = f'{_cfg[svr]}/oauth2/tokenP'
|
||||
res = requests.post(url, data=json.dumps(p), headers=_getBaseHeader()) # 토큰 발급
|
||||
rescode = res.status_code
|
||||
if rescode == 200: # 토큰 정상 발급
|
||||
my_token = _getResultObject(res.json()).access_token # 토큰값 가져오기
|
||||
my_expired= _getResultObject(res.json()).access_token_token_expired # 토큰값 만료일시 가져오기
|
||||
save_token(my_token, my_expired) # 새로 발급 받은 토큰 저장
|
||||
else:
|
||||
print('Get Authentification token fail!\nYou have to restart your app!!!')
|
||||
return
|
||||
else:
|
||||
my_token = saved_token # 기존 발급 토큰 확인되어 기존 토큰 사용
|
||||
|
||||
# 발급토큰 정보 포함해서 헤더값 저장 관리, API 호출시 필요
|
||||
changeTREnv(f"Bearer {my_token}", svr, product)
|
||||
|
||||
_base_headers["authorization"] = _TRENV.my_token
|
||||
_base_headers["appkey"] = _TRENV.my_app
|
||||
_base_headers["appsecret"] = _TRENV.my_sec
|
||||
|
||||
global _last_auth_time
|
||||
_last_auth_time = datetime.now()
|
||||
|
||||
if (_DEBUG):
|
||||
print(f'[{_last_auth_time}] => get AUTH Key completed!')
|
||||
|
||||
|
||||
# end of initialize, 토큰 재발급, 토큰 발급시 유효시간 1일
|
||||
# 프로그램 실행시 _last_auth_time에 저장하여 유효시간 체크, 유효시간 만료시 토큰 발급 처리
|
||||
def reAuth(svr='prod', product='01'):
|
||||
n2 = datetime.now()
|
||||
if (n2 - _last_auth_time).seconds >= 86400: # 유효시간 1일
|
||||
auth(svr, product)
|
||||
|
||||
# 접근토큰발급 저장
|
||||
auth()
|
||||
# 접근토큰 조회
|
||||
gettoken = read_token()
|
||||
print(gettoken)
|
||||
28
한국투자증권(API)/legacy/rest/kis_devlp.yaml
Normal file
28
한국투자증권(API)/legacy/rest/kis_devlp.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
#홈페이지에서 API서비스 신청시 받은 Appkey, Appsecret 값 설정
|
||||
#실전투자
|
||||
my_app: ""
|
||||
my_sec: ""
|
||||
|
||||
#모의투자
|
||||
paper_app: ""
|
||||
paper_sec: ""
|
||||
|
||||
#계좌번호 앞 8자리
|
||||
my_acct_stock: "12345678"
|
||||
my_acct_future: ""
|
||||
my_paper_stock: ""
|
||||
my_paper_future: ""
|
||||
|
||||
#계좌번호 뒤 2자리
|
||||
my_prod: "01"
|
||||
|
||||
#domain info
|
||||
prod: "https://openapi.koreainvestment.com:9443" #서비스
|
||||
ops: "ws://ops.koreainvestment.com:21000" #웹소켓
|
||||
vps: "https://openapivts.koreainvestment.com:29443" #모의투자서비스
|
||||
vops: "ws://ops.koreainvestment.com:31000" #모의투자웹소켓
|
||||
|
||||
my_token: ""
|
||||
|
||||
# User-Agent; Chrome > F12 개발자 모드 > Console > navigator.userAgent > 자신의 userAgent 확인가능
|
||||
my_agent : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36"
|
||||
20
한국투자증권(API)/legacy/rest/kisdev_vi.yaml
Normal file
20
한국투자증권(API)/legacy/rest/kisdev_vi.yaml
Normal file
@@ -0,0 +1,20 @@
|
||||
my_app: ""
|
||||
my_sec: ""
|
||||
paper_app: ""
|
||||
paper_sec: ""
|
||||
my_acct_stock: ""
|
||||
my_acct_future: ""
|
||||
my_paper_stock: ""
|
||||
my_paper_future: ""
|
||||
|
||||
|
||||
# User Agent
|
||||
my_agent: "'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36"
|
||||
|
||||
|
||||
|
||||
#domain info
|
||||
prod: "https://openapi.koreainvestment.com:9443" #서비스
|
||||
ops: "ws://ops.koreainvestment.com:21000" #웹소켓
|
||||
vps: "https://openapivts.koreainvestment.com:29443" #모의투자서비스
|
||||
vops: "ws://ops.koreainvestment.com:31000" #모의투자웹소켓
|
||||
BIN
한국투자증권(API)/legacy/rest/vba_sample.xlsm
Normal file
BIN
한국투자증권(API)/legacy/rest/vba_sample.xlsm
Normal file
Binary file not shown.
BIN
한국투자증권(API)/legacy/rest/한국투자증권 오픈API엑셀_샘플(국내주식시세주문).xlsm
Normal file
BIN
한국투자증권(API)/legacy/rest/한국투자증권 오픈API엑셀_샘플(국내주식시세주문).xlsm
Normal file
Binary file not shown.
Reference in New Issue
Block a user