국내주식 공매도 상위종목[국내주식-133]
기본정보
-
MethodGET
-
실전 Domainhttps://openapi.koreainvestment.com:9443
-
모의 Domain미지원
-
URL/uapi/domestic-stock/v1/ranking/short-sale
-
Format
-
Content-Type
개요
공매도 상위종목 API입니다.
한국투자 HTS(eFriend Plus) > [0482] 공매도 상위 화면의 기능을 API로 개발한 사항으로, 해당 화면을 참고하시면 기능을 이해하기 쉽습니다.
최대 30건 확인 가능하며, 다음 조회가 불가합니다.
※ 30건 이상의 목록 조회가 필요한 경우, 대안으로 종목조건검색 API를 이용해서 원하는 종목 100개까지 검색할 수 있는 기능을 제공하고 있습니다.
종목조건검색 API는 HTS(efriend Plus) [0110] 조건검색에서 등록 및 서버저장한 나의 조건 목록을 확인할 수 있는 API로,
자세한 사용 방법은 공지사항 - [조건검색 필독] 조건검색 API 이용안내 참고 부탁드립니다.
한국투자 HTS(eFriend Plus) > [0482] 공매도 상위 화면의 기능을 API로 개발한 사항으로, 해당 화면을 참고하시면 기능을 이해하기 쉽습니다.
최대 30건 확인 가능하며, 다음 조회가 불가합니다.
※ 30건 이상의 목록 조회가 필요한 경우, 대안으로 종목조건검색 API를 이용해서 원하는 종목 100개까지 검색할 수 있는 기능을 제공하고 있습니다.
종목조건검색 API는 HTS(efriend Plus) [0110] 조건검색에서 등록 및 서버저장한 나의 조건 목록을 확인할 수 있는 API로,
자세한 사용 방법은 공지사항 - [조건검색 필독] 조건검색 API 이용안내 참고 부탁드립니다.
LAYOUT
Request
Header
Element한글명TypeRequiredLengthDescription
| content-type | 컨텐츠타입 | String | Y | 40 | application/json; charset=utf-8 |
| authorization | 접근토큰 | String | Y | 40 | OAuth 토큰이 필요한 API 경우 발급한 Access token 일반고객(Access token 유효기간 1일, OAuth 2.0의 Client Credentials Grant 절차를 준용) 법인(Access token 유효기간 3개월, Refresh token 유효기간 1년, OAuth 2.0의 Authorization Code Grant 절차를 준용) |
| appkey | 앱키 | String | Y | 36 | 한국투자증권 홈페이지에서 발급받은 appkey (절대 노출되지 않도록 주의해주세요.) |
| appsecret | 앱시크릿키 | String | Y | 180 | 한국투자증권 홈페이지에서 발급받은 appkey (절대 노출되지 않도록 주의해주세요.) |
| personalseckey | 고객식별키 | String | N | 180 | [법인 필수] 제휴사 회원 관리를 위한 고객식별키 |
| tr_id | 거래ID | String | Y | 13 | FHPST04820000 |
| tr_cont | 연속 거래 여부 | String | N | 1 | 공백 : 초기 조회 N : 다음 데이터 조회 (output header의 tr_cont가 M일 경우) |
| custtype | 고객 타입 | String | Y | 1 | B : 법인 P : 개인 |
| seq_no | 일련번호 | String | N | 2 | [법인 필수] 001 |
| mac_address | 맥주소 | String | N | 12 | 법인고객 혹은 개인고객의 Mac address 값 |
| phone_number | 핸드폰번호 | String | N | 12 | [법인 필수] 제휴사APP을 사용하는 경우 사용자(회원) 핸드폰번호 ex) 01011112222 (하이픈 등 구분값 제거) |
| ip_addr | 접속 단말 공인 IP | String | N | 12 | [법인 필수] 사용자(회원)의 IP Address |
| hashkey | 해쉬키 | String | N | 256 | [POST API 대상] Client가 요청하는 Request Body를 hashkey api로 생성한 Hash값 * API문서 > hashkey 참조 |
| gt_uid | Global UID | String | N | 32 | [법인 필수] 거래고유번호로 사용하므로 거래별로 UNIQUE해야 함 |
Query Parameter
Element한글명TypeRequiredLengthDescription
| FID_APLY_RANG_VOL | FID 적용 범위 거래량 | String | Y | 18 | 공백 |
| FID_COND_MRKT_DIV_CODE | 조건 시장 분류 코드 | String | Y | 2 | 시장구분코드 (주식 J) |
| FID_COND_SCR_DIV_CODE | 조건 화면 분류 코드 | String | Y | 5 | Unique key(20482) |
| FID_INPUT_ISCD | 입력 종목코드 | String | Y | 12 | 0000:전체, 0001:코스피, 1001:코스닥, 2001:코스피200, 4001: KRX100, 3003: 코스닥150 |
| FID_PERIOD_DIV_CODE | 조회구분 (일/월) | String | Y | 32 | 조회구분 (일/월) D: 일, M:월 |
| FID_INPUT_CNT_1 | 조회가간(일수 | String | Y | 12 | '조회가간(일수): 조회구분(D) 0:1일, 1:2일, 2:3일, 3:4일, 4:1주일, 9:2주일, 14:3주일, 조회구분(M) 1:1개월, 2:2개월, 3:3개월' |
| FID_TRGT_EXLS_CLS_CODE | 대상 제외 구분 코드 | String | Y | 32 | 공백 |
| FID_TRGT_CLS_CODE | FID 대상 구분 코드 | String | Y | 32 | 공백 |
| FID_APLY_RANG_PRC_1 | FID 적용 범위 가격1 | String | Y | 18 | 가격 ~ |
| FID_APLY_RANG_PRC_2 | FID 적용 범위 가격2 | String | Y | 18 | ~ 가격 |
Response
Header
Element한글명TypeRequiredLengthDescription| content-type | 컨텐츠타입 | String | Y | 40 | application/json; charset=utf-8 |
| tr_id | 거래ID | String | Y | 13 | 요청한 tr_id |
| tr_cont | 연속 거래 여부 | String | N | 1 | 공백 : 초기 조회 N : 다음 데이터 조회 (output header의 tr_cont가 M일 경우) |
| gt_uid | Global UID | String | N | 32 | [법인 필수] 거래고유번호로 사용하므로 거래별로 UNIQUE해야 함 |
Body
Element한글명TypeRequiredLengthDescription| rt_cd | 성공 실패 여부 | String | Y | 1 | |
| msg_cd | 응답코드 | String | Y | 8 | |
| msg1 | 응답메세지 | String | Y | 80 | |
| output | 응답상세 | Object Array | Y | array | |
| -mksc_shrn_iscd | 유가증권 단축 종목코드 | String | Y | 9 | |
| -hts_kor_isnm | HTS 한글 종목명 | String | Y | 40 | |
| -stck_prpr | 주식 현재가 | String | Y | 10 | |
| -prdy_vrss | 전일 대비 | String | Y | 10 | |
| -prdy_vrss_sign | 전일 대비 부호 | String | Y | 1 | |
| -prdy_ctrt | 전일 대비율 | String | Y | 82 | |
| -acml_vol | 누적 거래량 | String | Y | 18 | |
| -acml_tr_pbmn | 누적 거래 대금 | String | Y | 18 | |
| -ssts_cntg_qty | 공매도 체결 수량 | String | Y | 12 | |
| -ssts_vol_rlim | 공매도 거래량 비중 | String | Y | 62 | |
| -ssts_tr_pbmn | 공매도 거래 대금 | String | Y | 18 | |
| -ssts_tr_pbmn_rlim | 공매도 거래대금 비중 | String | Y | 62 | |
| -stnd_date1 | 기준 일자1 | String | Y | 8 | |
| -stnd_date2 | 기준 일자2 | String | Y | 8 | |
| -avrg_prc | 평균가격 | String | Y | 11 |
import tkinter as tk
from tkinter import messagebox
import requests
import json
import pandas as pd
from datetime import datetime
import os
import time
# API 기본 설정 (초기값은 빈 문자열)
APP_KEY = ""
APP_SECRET = ""
ACCESS_TOKEN = ""
URL_BASE = "https://openapi.koreainvestment.com:9443"
class APISettingsApp:
def __init__(self, root):
self.root = root
self.root.title("KIS 공매도 크롤러")
self.root.geometry("250x150")
tk.Button(root, text="API 기본 설정", command=self.open_api_settings).pack(pady=10)
tk.Button(root, text="공매도 조회", command=self.fetch_short).pack(pady=10)
tk.Button(root, text="종료", command=root.quit).pack(pady=10)
def open_api_settings(self):
"""API 설정 팝업창 열기"""
popup = tk.Toplevel(self.root)
popup.title("API 기본 설정")
popup.geometry("400x150")
popup.transient(self.root)
popup.grab_set()
tk.Label(popup, text="APP Key:").grid(row=0, column=0, padx=5, pady=5, sticky="e")
app_key_entry = tk.Entry(popup, width=40)
app_key_entry.grid(row=0, column=1, padx=5, pady=5)
app_key_entry.insert(0, APP_KEY)
tk.Label(popup, text="APP Secret:").grid(row=1, column=0, padx=5, pady=5, sticky="e")
app_secret_entry = tk.Entry(popup, width=40)
app_secret_entry.grid(row=1, column=1, padx=5, pady=5)
app_secret_entry.insert(0, APP_SECRET)
tk.Label(popup, text='"(따옴표)는 제외"', fg="red").grid(row=2, column=1, pady=2)
tk.Button(popup, text="저장", command=lambda: self.save_api_settings(app_key_entry.get(), app_secret_entry.get(), popup)).grid(row=3, column=1, pady=5)
def save_api_settings(self, app_key, app_secret, popup):
"""입력된 API 설정 저장 및 토큰 발급"""
global APP_KEY, APP_SECRET, ACCESS_TOKEN
APP_KEY = app_key.strip()
APP_SECRET = app_secret.strip()
print(f"API 설정 저장됨 - APP Key: {APP_KEY}, APP Secret: {APP_SECRET}")
popup.destroy()
if not ACCESS_TOKEN:
try:
auth()
messagebox.showinfo("성공", "API 설정이 저장되고 토큰이 발급되었습니다!")
except Exception as e:
messagebox.showerror("오류", f"토큰 발급 실패: {e}")
else:
messagebox.showinfo("알림", "이미 토큰이 발급되어 있습니다.")
def fetch_short(self):
"""KIS API로 공매도 조회"""
if not ACCESS_TOKEN:
messagebox.showerror("오류", "먼저 API 설정을 완료하세요.")
return
try:
short_data = self.get_short("NAS", "Y") # 매개변수는 사용되지 않음, 제거 가능
print("가져온 공매도 데이터:", short_data)
if short_data:
self.save_to_excel(short_data, "short_balance")
messagebox.showinfo("성공", "공매도 조회가 완료되었습니다!")
else:
messagebox.showerror("오류", "공매도 데이터를 가져오지 못했습니다.")
except Exception as e:
print(f"fetch_short 예외 발생: {str(e)}")
messagebox.showerror("오류", f"공매도 조회 중 오류 발생: {str(e)}")
def get_short(self, exchange_code, sort_type):
"""KIS API로 국내주식 공매도 상위"""
path = "uapi/domestic-stock/v1/ranking/short-sale"
url = f"{URL_BASE}/{path}"
headers = {
"Content-Type": "application/json",
"authorization": f"Bearer {ACCESS_TOKEN}",
"appKey": APP_KEY,
"appSecret": APP_SECRET,
"tr_id": "FHPST04820000",
"custtype": "P"
}
params = {
"FID_APLY_RANG_VOL": "",
"FID_COND_MRKT_DIV_CODE": "J",
"FID_COND_SCR_DIV_CODE": "20482",
"FID_INPUT_ISCD": "0000",
"FID_PERIOD_DIV_CODE": "",
"FID_INPUT_CNT_1": "0",
"FID_TRGT_EXLS_CLS_CODE": "",
"FID_TRGT_CLS_CODE": "",
"FID_APLY_RANG_PRC_1": "100",
"FID_APLY_RANG_PRC_2": "9000000"
}
res = requests.get(url, headers=headers, params=params)
print(f"API 요청 URL: {res.url}")
print(f"응답 상태: {res.status_code}, 응답 본문: {res.text}")
if res.status_code == 200 and res.json().get("rt_cd") == "0":
return res.json()["output"] # "output2" 대신 "output"
else:
return None
def save_to_excel(self, data, filename_prefix):
"""데이터를 엑셀로 저장하고 폴더 열기"""
df = pd.DataFrame(data)
output_dir = "D:\\file"
if not os.path.exists(output_dir):
os.makedirs(output_dir)
today = datetime.today().strftime("%Y%m%d_%H%M%S")
output_file = os.path.join(output_dir, f"{filename_prefix}_{today}.xlsx")
df.to_excel(output_file, index=False)
print(f"엑셀 파일 저장 완료: {output_file}")
os.startfile(output_dir)
print(f"폴더 열기: {output_dir}")
def auth():
headers = {"content-type": "application/json; charset=utf-8"}
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))
print(f"토큰 요청 응답: {res.status_code} - {res.text}")
if res.status_code == 200:
print("토큰 발급 성공:", res.json())
global ACCESS_TOKEN
ACCESS_TOKEN = res.json()["access_token"]
else:
raise Exception(f"토큰 발급 실패: {res.status_code} - {res.text}")
if __name__ == "__main__":
root = tk.Tk()
app = APISettingsApp(root)
root.mainloop()