ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [파이썬] 선물 분 차트 그리기(matplotlib)
    개발/파이썬 2018.02.18 19:13

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

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

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


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



    [전체 코드]

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    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, [0123458])  # 요청항목 - 날짜, 시간,시가,고가,저가,종가,거래량
            self.objFutureChart.SetInputValue(6, dwm)  # '차트 주기 - 분/틱
            self.objFutureChart.SetInputValue(71)  # 분틱차트 주기
            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(50501200600)
            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(0len(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_()
     
     
    cs


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

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

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

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
            ###############################################
            # 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('')
    cs



    댓글 2

    • 프로필사진

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

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

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

      2018.05.19 15:37
      • 프로필사진

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

        http://cafe.naver.com/pystock

        2018.05.19 21:40 신고
Designed by black7375.