본문 바로가기

카테고리 없음

국내주식 공매도 상위종목[국내주식-133]

국내주식 공매도 상위종목[국내주식-133]

기본정보

개요

공매도 상위종목 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()