[파이썬] 선물 분 차트 그리기(matplotlib)

2018. 2. 18. 19:13개발/파이썬

CYBOS PLUS/CREON PLUS API 를 이용하여 간단한 분차트를 그리는 예제 코드입니다 

차트는 matplotlib 라이브러리를 이용했습니다 

matplotlib 사용방법이 은근히 까다롭네요 

 

화면 좌 상단에는 파생 종목 코드 리스트를 콤보에 넣어 종목을 선택할 수 있게 했습니다. 

 

 

[전체 코드]

import datetime
import sys
import ctypes
import time
 
import numpy as np
from PyQt5.QtWidgets import *
import win32com.client
import pandas as pd
import os
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
import matplotlib.finance as matfin
import matplotlib.ticker as ticker
 
C_DT = 0    # 일자
C_TM = 1    # 시간
C_OP = 2    # 시가
C_HP = 3    # 고가
C_LP = 4    # 저가
C_CP = 5    # 종가
C_VL = 6    # 거래량
C_MA5 = 7    # 5일 이동평균
C_MA10 = 8    # 10일 이동평균
C_MA20 = 9    # 20일 이동평균
 
 
 
g_objCodeMgr = win32com.client.Dispatch('CpUtil.CpCodeMgr')
g_objCpStatus = win32com.client.Dispatch('CpUtil.CpCybos')
g_objCpTrade = win32com.client.Dispatch('CpTrade.CpTdUtil')
g_objFutureMgr = win32com.client.Dispatch("CpUtil.CpFutureCode")
def InitPlusCheck():
    # 프로세스가 관리자 권한으로 실행 여부
    if ctypes.windll.shell32.IsUserAnAdmin():
        print('정상: 관리자권한으로 실행된 프로세스입니다.')
    else:
        print('오류: 일반권한으로 실행됨. 관리자 권한으로 실행해 주세요')
        return False
 
    # 연결 여부 체크
    if (g_objCpStatus.IsConnect == 0):
        print("PLUS가 정상적으로 연결되지 않음. ")
        return False
 
    # 주문 관련 초기화
    if (g_objCpTrade.TradeInit(0) != 0):
        print("주문 초기화 실패")
        return False
 
    return True
 
 
 
 
class CpFutureChart:
    def __init__(self):
        self.objFutureChart = win32com.client.Dispatch("CpSysDib.FutOptChart")
 
 
    # 차트 요청 - 분간, 틱 차트
    def RequestMT(self, code, dwm, count, caller):
        # 연결 여부 체크
        bConnect = g_objCpStatus.IsConnect
        if (bConnect == 0):
            print("PLUS가 정상적으로 연결되지 않음. ")
            return False
 
        self.objFutureChart.SetInputValue(0, code)  # 종목코드
        self.objFutureChart.SetInputValue(1, ord('2'))  # 개수로 받기
        self.objFutureChart.SetInputValue(4, count)  # 조회 개수
        self.objFutureChart.SetInputValue(5, [0, 1, 2, 3, 4, 5, 8])  # 요청항목 - 날짜, 시간,시가,고가,저가,종가,거래량
        self.objFutureChart.SetInputValue(6, dwm)  # '차트 주기 - 분/틱
        self.objFutureChart.SetInputValue(7, 1)  # 분틱차트 주기
        self.objFutureChart.SetInputValue(8, ord('1'))  # 갭보정
        self.objFutureChart.SetInputValue(9, ord('1'))  # 수정주가 사용
        self.objFutureChart.BlockRequest()
 
        rqStatus = self.objFutureChart.GetDibStatus()
        rqRet = self.objFutureChart.GetDibMsg1()
        print("통신상태", rqStatus, rqRet)
        if rqStatus != 0:
            exit()
 
        len = self.objFutureChart.GetHeaderValue(3)
 
        caller.chartData = {}
        caller.chartData[C_DT] = []
        caller.chartData[C_TM] = []
        caller.chartData[C_OP] = []
        caller.chartData[C_HP] = []
        caller.chartData[C_LP] = []
        caller.chartData[C_CP] = []
        caller.chartData[C_VL] = []
        caller.chartData[C_MA5] = []
        caller.chartData[C_MA10] = []
        caller.chartData[C_MA20] = []
 
        for i in range(len):
            caller.chartData[C_DT].insert(0, self.objFutureChart.GetDataValue(0, i))
            caller.chartData[C_TM].insert(0, self.objFutureChart.GetDataValue(1, i))
            caller.chartData[C_OP].insert(0, self.objFutureChart.GetDataValue(2, i))
            caller.chartData[C_HP].insert(0, self.objFutureChart.GetDataValue(3, i))
            caller.chartData[C_LP].insert(0, self.objFutureChart.GetDataValue(4, i))
            caller.chartData[C_CP].insert(0, self.objFutureChart.GetDataValue(5, i))
            caller.chartData[C_VL].insert(0, self.objFutureChart.GetDataValue(6, i))
 
        #print(self.objFutureChart.Continue)
 
 
        return
 
 
 
class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
 
        # plus 상태 체크
        if InitPlusCheck() == False:
            exit()
 
 
        # 기본 변수들
        self.chartData = {}
        self.code = ''
        self.isRq = False
 
        self.objChart = CpFutureChart()
 
        self.sizeControl()
        # 선물 종목 코드 추가
        for i in range(g_objFutureMgr.GetCount()):
            code = g_objFutureMgr.GetData(0, i)
            self.comboStg.addItem(code)
        self.comboStg.setCurrentIndex(0)
 
 
 
    def sizeControl(self) :
        # 윈도우 버튼 배치
        self.setWindowTitle("PLUS API TEST")
 
        self.setGeometry(50, 50, 1200, 600)
        self.comboStg = QComboBox()
        #self.comboStg.move(20, nH)
        self.comboStg.currentIndexChanged.connect(self.comboChanged)
        #self.comboStg.resize(100, 30)
        self.label = QLabel('종목코드')
        #self.label.move(140, nH)
 
        # Figure 를 먼저 만들고 레이아웃에 들어 갈 sub axes 를 생성 한다.
        self.fig = plt.Figure()
        self.canvas = FigureCanvas(self.fig)
 
 
        # top layout
        topLayout = QHBoxLayout()
        topLayout.addWidget(self.comboStg)
        topLayout.addWidget(self.label)
        topLayout.addStretch(1)
        # topLayout.addSpacing(20)
 
        chartlayout = QVBoxLayout()
        chartlayout.addWidget(self.canvas)
 
 
        layout = QVBoxLayout()
        layout.addLayout(topLayout)
        layout.addLayout(chartlayout)
        layout.setStretchFactor(topLayout, 0)
        layout.setStretchFactor(chartlayout, 1)
 
        self.setLayout(layout)
 
    # 분차트 받기
    def RequestMinChart(self):
        if self.objChart.RequestMT(self.code, ord('m'), 100, self) == False:
            exit()
 
    def makeMovingAverage(self, maData, interval):
        #maData = []
        for i in range(0, len(self.chartData[C_DT])) :
            if (i < interval) :
                maData.append(float('nan'))
                continue
            sum = 0
            for j in range (0, interval) :
                sum += self.chartData[C_CP][i - j]
            ma = sum / interval
            maData.append(ma)
        # print(maData)
 
 
    def drawMinChart(self):
        # 기존 거 지운다.
        self.fig.clf()
 
        # 211 - 2(행) * 1(열) 배치 1번째
        self.ax1 = self.fig.add_subplot(2,1,1)
        # 212 - 2(행) * 1(열) 배치 2번째
        self.ax2 = self.fig.add_subplot(2,1,2)
 
        ###############################################
        # 봉차트 그리기
        # self.ax1.xaxis.set_major_formatter(ticker.FixedFormatter(schartData[C_TM]))
        matfin.candlestick2_ohlc(self.ax1, self.chartData[C_OP], self.chartData[C_HP], self.chartData[C_LP], self.chartData[C_CP],
                                  width=0.8, colorup='r', colordown='b')
 
 
        ###############################################
        # x 축 인덱스 만들기 - 기본 순차 배열 추가
        x_tick_raw =  [i for i in range(len(self.chartData[C_DT]))]
        # x 축 인덱스 만들기 - 실제 화면에 표시될 텍스트 만들기
        x_tick_labels = []
 
        startDate = 0
        dateChanged = True
        for i in range(len(self.chartData[C_DT])) :
            # 날짜 변경 된 경우 날짜 정보 저장
            date = self.chartData[C_DT][i]
            if (date != startDate) :
                yy, mm = divmod(date, 10000)
                mm,dd = divmod(mm, 100)
                sDate = '%2d/%d '%(mm,dd)
                print(sDate)
                startDate = date
                dateChanged = True
 
            # 0 분 또는 30분 단위로 시간 표시
            hhh, mmm = divmod(self.chartData[C_TM][i], 100)
            stime = '%02d:%02d' % (hhh, mmm)
            if (mmm == 0 or mmm == 30):
                if dateChanged == True:
                    sDate += stime
                    x_tick_labels.append(sDate)
                    dateChanged = False
                else:
                    x_tick_labels.append(stime)
            else:
                x_tick_labels.append('')
 
 
        ###############################################
        # 이동 평균 그리기
        self.ax1.plot(x_tick_raw, self.chartData[C_MA5], label='ma5')
        self.ax1.plot(x_tick_raw, self.chartData[C_MA10], label='ma10')
        self.ax1.plot(x_tick_raw, self.chartData[C_MA20], label='ma20')
 
 
        ###############################################
        # 거래량 그리기
        self.ax2.bar(x_tick_raw, self.chartData[C_VL])
 
        ###############################################
        # x 축 가로 인덱스 지정
        self.ax1.set(xticks=x_tick_raw, xticklabels=x_tick_labels)
        self.ax2.set(xticks=x_tick_raw, xticklabels=x_tick_labels)
 
        self.ax1.grid()
        self.ax2.grid()
        plt.tight_layout()
        self.ax1.legend(loc='upper left')
 
        self.canvas.draw()
 
 
    def comboChanged(self):
        if self.isRq == True:
            return
        self.isRq = True
        self.code = self.comboStg.currentText()
        self.name = g_objFutureMgr.CodetoName(self.code)
        self.label.setText(self.name)
        self.RequestMinChart()
        self.makeMovingAverage(self.chartData[C_MA5], 5)
        self.makeMovingAverage(self.chartData[C_MA10], 10)
        self.makeMovingAverage(self.chartData[C_MA20], 20)
        self.drawMinChart()
        self.isRq = False
 
 
        #self.requestStgID(cur)
 
 
 
 
 
if __name__ == "__main__":
    app = QApplication(sys.argv)
    myWindow = MyWindow()
    myWindow.show()
    app.exec_()
 

 

가장 까다로웠던 차트 인덱스 그리기 부분

matplotlib 는 X 축을 항상 연속으로 그리게 되어 있어 있습니다 

15:10분 이후에 거래가 없어서 15:15 분 데이터가 있으면 matplotlib 에서는 자동으로 X 축에 15:11~15:14 분을 채워넣네요 

이 부분을 방지하기 위해 가로축 기본 데이터는 그냥 0 ~ 데이터 개수 만큼 순차적으로 세팅하고 

라벨 부분만 원하는 문자열을 채워 넣었습니다.

관련 코드는 아래 부분 입니다.

       ###############################################
        # x 축 인덱스 만들기 - 기본 순차 배열 추가
        x_tick_raw =  [i for i in range(len(self.chartData[C_DT]))]
        # x 축 인덱스 만들기 - 실제 화면에 표시될 텍스트 만들기
        x_tick_labels = []
 
        startDate = 0
        dateChanged = True
        for i in range(len(self.chartData[C_DT])) :
            # 날짜 변경 된 경우 날짜 정보 저장
            date = self.chartData[C_DT][i]
            if (date != startDate) :
                yy, mm = divmod(date, 10000)
                mm,dd = divmod(mm, 100)
                sDate = '%2d/%d '%(mm,dd)
                print(sDate)
                startDate = date
                dateChanged = True
 
            # 0 분 또는 30분 단위로 시간 표시
            hhh, mmm = divmod(self.chartData[C_TM][i], 100)
            stime = '%02d:%02d' % (hhh, mmm)
            if (mmm == 0 or mmm == 30):
                if dateChanged == True:
                    sDate += stime
                    x_tick_labels.append(sDate)
                    dateChanged = False
                else:
                    x_tick_labels.append(stime)
            else:
                x_tick_labels.append('')

 

 

  • 프로필사진
    Psalms232018.05.19 15:37

    감사합니다
    Python을 공부하고있는 초보생입니다
    주식에 관련하여서 자료를 찾다보니 귀한 소스 코드를 올려주셔서 열공중입니다.

    또한 방문한 곳의 사진을 소소하게 올려주셔서 글도 잘읽고 있습니다
    저도 언제 저런곳을 여행해볼수있을지 동경의 대상이 되고있습니다~~^^ 부럽스니당~~ㅠ

    윗글을 보면서 궁금사항들이있어서 메일로 궁금사항을 보내드리고 싶은데.....
    못찾고있습니다.
    자세한 내용을 메일로 요청드리고 싶습니다.

    • 프로필사진
      BlogIcon esstory2018.05.19 21:40 신고

      블로그 댓글로 힘드신 내용인가요 ?
      우선 비밀댓글로라도 간단히 올려주시면 추가 답변 드리겠습니다
      그리고 파이썬 주식 트레이딩 카페에 좋은 코드와 고수들이 많으니 참고해 봐 주세요

      http://cafe.naver.com/pystock