首页 > 编程语言 >python+ mplfinance实现全功能动态交互式K线图

python+ mplfinance实现全功能动态交互式K线图

时间:2022-09-19 20:34:15浏览次数:58  
标签:idx python self 全功能 0.1 fig mplfinance font data

在网上找的资料,但没有数据,于是根据代码自己造了一些,发现跑起来太卡了,放弃

# coding=utf-8
# inter_candle.py

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import mplfinance as mpf

data = pd.read_csv('test_data2.csv', index_col=0)
data.index = pd.to_datetime(data.index)

my_color = mpf.make_marketcolors(up='r',
                                 down='g',
                                 edge='inherit',
                                 wick='inherit',
                                 volume='inherit')
my_style = mpf.make_mpf_style(marketcolors=my_color,
                                  figcolor='(0.82, 0.83, 0.85)',
                                  gridcolor='(0.82, 0.83, 0.85)')

title_font = {'fontname': 'SimHei',
              'size':     '16',
              'color':    'black',
              'weight':   'bold',
              'va':       'bottom',
              'ha':       'center'}
large_red_font = {'fontname': 'SimHei',
                  'size':     '24',
                  'color':    'red',
                  'weight':   'bold',
                  'va':       'bottom'}
large_green_font = {'fontname': 'SimHei',
                    'size':     '24',
                    'color':    'green',
                    'weight':   'bold',
                    'va':       'bottom'}
small_red_font = {'fontname': 'SimHei',
                  'size':     '12',
                  'color':    'red',
                  'weight':   'bold',
                  'va':       'bottom'}
small_green_font = {'fontname': 'SimHei',
                    'size':     '12',
                    'color':    'green',
                    'weight':   'bold',
                    'va':       'bottom'}
normal_label_font = {'fontname': 'SimHei',
                     'size':     '12',
                     'color':    'black',
                     'weight':   'normal',
                     'va':       'bottom',
                     'ha':       'right'}
normal_font = {'fontname': 'SimHei',
               'size':     '12',
               'color':    'black',
               'weight':   'normal',
               'va':       'bottom',
               'ha':       'left'}

class InterCandle:
    def __init__(self, data, my_style):
        self.pressed = False
        self.xpress = None

        # 初始化交互式K线图对象,历史数据作为唯一的参数用于初始化对象
        self.data = data
        self.style = my_style
        # 设置初始化的K线图显示区间起点为0,即显示第0到第99个交易日的数据(前100个数据)
        self.idx_start = 0
        self.idx_range = 100
        # 设置ax1图表中显示的均线类型
        self.avg_type = 'ma'
        self.indicator = 'macd'
        
        # 初始化figure对象,在figure上建立三个Axes对象并分别设置好它们的位置和基本属性
        self.fig = mpf.figure(style=my_style, figsize=(12, 8), facecolor=(0.82, 0.83, 0.85))
        fig = self.fig
        self.ax1 = fig.add_axes([0.08, 0.25, 0.88, 0.60])
        self.ax2 = fig.add_axes([0.08, 0.15, 0.88, 0.10], sharex=self.ax1)
        self.ax2.set_ylabel('volume')
        self.ax3 = fig.add_axes([0.08, 0.05, 0.88, 0.10], sharex=self.ax1)
        self.ax3.set_ylabel('macd')
        # 初始化figure对象,在figure上预先放置文本并设置格式,文本内容根据需要显示的数据实时更新
        self.t1 = fig.text(0.50, 0.94, '513100.SH - 纳斯达克指数ETF基金', **title_font)
        self.t2 = fig.text(0.12, 0.90, '开/收: ', **normal_label_font)
        self.t3 = fig.text(0.14, 0.89, f'', **large_red_font)
        self.t4 = fig.text(0.14, 0.86, f'', **small_red_font)
        self.t5 = fig.text(0.22, 0.86, f'', **small_red_font)
        self.t6 = fig.text(0.12, 0.86, f'', **normal_label_font)
        self.t7 = fig.text(0.40, 0.90, '高: ', **normal_label_font)
        self.t8 = fig.text(0.40, 0.90, f'', **small_red_font)
        self.t9 = fig.text(0.40, 0.86, '低: ', **normal_label_font)
        self.t10 = fig.text(0.40, 0.86, f'', **small_green_font)
        self.t11 = fig.text(0.55, 0.90, '量(万手): ', **normal_label_font)
        self.t12 = fig.text(0.55, 0.90, f'', **normal_font)
        self.t13 = fig.text(0.55, 0.86, '额(亿元): ', **normal_label_font)
        self.t14 = fig.text(0.55, 0.86, f'', **normal_font)
        self.t15 = fig.text(0.70, 0.90, '涨停: ', **normal_label_font)
        self.t16 = fig.text(0.70, 0.90, f'', **small_red_font)
        self.t17 = fig.text(0.70, 0.86, '跌停: ', **normal_label_font)
        self.t18 = fig.text(0.70, 0.86, f'', **small_green_font)
        self.t19 = fig.text(0.85, 0.90, '均价: ', **normal_label_font)
        self.t20 = fig.text(0.85, 0.90, f'', **normal_font)
        self.t21 = fig.text(0.85, 0.86, '昨收: ', **normal_label_font)
        self.t22 = fig.text(0.85, 0.86, f'', **normal_font)

        #fig.canvas.mpl_connect('button_press_event', self.on_press)
        #fig.canvas.mpl_connect('button_release_event', self.on_release)
        #fig.canvas.mpl_connect('motion_notify_event', self.on_motion)
        #fig.canvas.mpl_connect('scroll_event', self.on_scroll)
        #fig.canvas.mpl_connect('key_press_event', self.on_key_press)

    def refresh_plot(self, idx_start, idx_range):
        """ 根据最新的参数,重新绘制整个图表
        """
        all_data = self.data
        plot_data = all_data.iloc[idx_start: idx_start + idx_range]

        ap = []
        # 添加K线图重叠均线,根据均线类型添加移动均线或布林带线
        if self.avg_type == 'ma':
            ap.append(mpf.make_addplot(plot_data[['MA5', 'MA10', 'MA20', 'MA60']], ax=self.ax1))
        elif self.avg_type == 'bb':
            ap.append(mpf.make_addplot(plot_data[['bb-u', 'bb-m', 'bb-l']], ax=self.ax1))
        # 添加指标,根据指标类型添加MACD或RSI或DEMA
        if self.indicator == 'macd':
            ap.append(mpf.make_addplot(plot_data[['macd-m', 'macd-s']], ylabel='macd', ax=self.ax3))
            bar_r = np.where(plot_data['macd-h'] > 0, plot_data['macd-h'], 0)
            bar_g = np.where(plot_data['macd-h'] <= 0, plot_data['macd-h'], 0)
            ap.append(mpf.make_addplot(bar_r, type='bar', color='red', ax=self.ax3))
            ap.append(mpf.make_addplot(bar_g, type='bar', color='green', ax=self.ax3))
        elif self.indicator == 'rsi':
            ap.append(mpf.make_addplot([75] * len(plot_data), color=(0.75, 0.6, 0.6), ax=self.ax3))
            ap.append(mpf.make_addplot([30] * len(plot_data), color=(0.6, 0.75, 0.6), ax=self.ax3))
            ap.append(mpf.make_addplot(plot_data['rsi'], ylabel='rsi', ax=self.ax3))
        else:  # indicator == 'dema'
            ap.append(mpf.make_addplot(plot_data['dema'], ylabel='dema', ax=self.ax3))
        # 绘制图表
        mpf.plot(plot_data,
                 ax=self.ax1,
                 volume=self.ax2,
                 addplot=ap,
                 type='candle',
                 style=self.style,
                 datetime_format='%Y-%m',
                 xrotation=0)
        self.fig.show()

    def refresh_texts(self, display_data):
        return
        """ 更新K线图上的价格文本
        """
        # display_data是一个交易日内的所有数据,将这些数据分别填入figure对象上的文本中
        self.t3.set_text(f'{np.round(display_data["open"], 3)} / {np.round(display_data["close"], 3)}')
        self.t4.set_text(f'{np.round(display_data["change"], 3)}')
        self.t5.set_text(f'[{np.round(display_data["pct_change"], 3)}%]')
        self.t6.set_text(f'{display_data.name.date()}')
        self.t8.set_text(f'{np.round(display_data["high"], 3)}')
        self.t10.set_text(f'{np.round(display_data["low"], 3)}')
        self.t12.set_text(f'{np.round(display_data["volume"] / 10000, 3)}')
        self.t14.set_text(f'{display_data["value"]}')
        self.t16.set_text(f'{np.round(display_data["upper_lim"], 3)}')
        self.t18.set_text(f'{np.round(display_data["lower_lim"], 3)}')
        self.t20.set_text(f'{np.round(display_data["average"], 3)}')
        self.t22.set_text(f'{np.round(display_data["last_close"], 3)}')
        # 根据本交易日的价格变动值确定开盘价、收盘价的显示颜色
        if display_data['change'] > 0:  # 如果今日变动额大于0,即今天价格高于昨天,今天价格显示为红色
            close_number_color = 'red'
        elif display_data['change'] < 0:  # 如果今日变动额小于0,即今天价格低于昨天,今天价格显示为绿色
            close_number_color = 'green'
        else:
            close_number_color = 'black'
        self.t3.set_color(close_number_color)
        self.t4.set_color(close_number_color)
        self.t5.set_color(close_number_color)

    def on_press(self, event):
        if not event.inaxes == self.ax1:
            return
        if event.button != 1:
            return
        self.pressed = True
        self.xpress = event.xdata

        # 切换当前ma类型, 在ma、bb、none之间循环
        if event.inaxes == self.ax1 and event.dblclick == 1:
            if self.avg_type == 'ma':
                self.avg_type = 'bb'
            elif self.avg_type == 'bb':
                self.avg_type = 'none'
            else:
                self.avg_type = 'ma'
        # 切换当前indicator类型,在macd/dma/rsi/kdj之间循环
        if event.inaxes == self.ax3 and event.dblclick == 1:
            if self.indicator == 'macd':
                self.indicator = 'dma'
            elif self.indicator == 'dma':
                self.indicator = 'rsi'
            elif self.indicator == 'rsi':
                self.indicator = 'kdj'
            else:
                self.indicator = 'macd'

        self.ax1.clear()
        self.ax2.clear()
        self.ax3.clear()
        self.refresh_plot(self.idx_start, self.idx_range)

    def on_release(self, event):
        self.pressed = False
        dx = int(event.xdata - self.xpress)
        self.idx_start -= dx
        if self.idx_start <= 0:
            self.idx_start = 0
        if self.idx_start >= len(self.data) - 100:
            self.idx_start = len(self.data) - 100

    def on_motion(self, event):
        if not self.pressed:
            return
        if not event.inaxes == self.ax1:
            return
        dx = int(event.xdata - self.xpress)
        new_start = self.idx_start - dx
        # 设定平移的左右界限,如果平移后超出界限,则不再平移
        if new_start <= 0:
            new_start = 0
        if new_start >= len(self.data) - 100:
            new_start = len(self.data) - 100
        self.ax1.clear()
        self.ax2.clear()
        self.ax3.clear()

        self.refresh_texts(self.data.iloc[new_start])
        self.refresh_plot(new_start, self.idx_range)

    def on_scroll(self, event):
        # 仅当鼠标滚轮在axes1范围内滚动时起作用
        if event.inaxes != self.ax1:
            return
        if event.button == 'down':
            # 缩小20%显示范围
            scale_factor = 0.8
        if event.button == 'up':
            # 放大20%显示范围
            scale_factor = 1.2
        # 设置K线的显示范围大小
        self.idx_range = int(self.idx_range * scale_factor)
        # 限定可以显示的K线图的范围,最少不能少于30个交易日,最大不能超过当前位置与
        # K线数据总长度的差
        data_length = len(self.data)
        if self.idx_range >= data_length - self.idx_start:
            self.idx_range = data_length - self.idx_start
        if self.idx_range <= 30:
            self.idx_range = 30 
        # 更新图表(注意因为多了一个参数idx_range,refresh_plot函数也有所改动)
        self.ax1.clear()
        self.ax2.clear()
        self.ax3.clear()
        self.refresh_texts(self.data.iloc[self.idx_start])
        self.refresh_plot(self.idx_start, self.idx_range)
        
    # 键盘按下处理
    def on_key_press(self, event):
        data_length = len(self.data)
        if event.key == 'a':  # avg_type, 在ma,bb,none之间循环
            if self.avg_type == 'ma':
                self.avg_type = 'bb'
            elif self.avg_type == 'bb':
                self.avg_type = 'none'
            elif self.avg_type == 'none':
                self.avg_type = 'ma'
        elif event.key == 'up':  # 向上,看仔细1倍
            if self.idx_range > 60:
                self.idx_range = int(self.idx_range / 2)
        elif event.key == 'down':  # 向下,看多1倍标的
            if self.idx_range <= 480:
                self.idx_range = self.idx_range * 2
        elif event.key == 'left':  
            if self.idx_start > self.idx_range:
                self.idx_start = self.idx_start - self.idx_range
        elif event.key == 'right':
            if self.idx_start < data_length - self.idx_range:
                self.idx_start = self.idx_start + self.idx_range
        self.ax1.clear()
        self.ax2.clear()
        self.ax3.clear()
        self.refresh_texts(self.data.iloc[self.idx_start])
        self.refresh_plot(self.idx_start, self.idx_range)

if __name__ == '__main__':
    candle = InterCandle(data, my_style)
    candle.idx_start = 1
    candle.idx_range = 200
    candle.refresh_texts(data.iloc[249])
    candle.refresh_plot(1, 200)
    input()

 

数据,请存为test_data2.csv:

,ts_code,trade_date,open,high,low,close,pre_close,change,pct_chg,volume,amount,MA5,MA10,MA20,MA60,macd-m,macd-s,macd-h
0,000001.SZ,20201231,19.21,19.58,19.02,19.34,19.2,0.14,0.7292,924503.43,1781736.285,19.275,19.275,19.275,19.275,0.1,0.12,0.1
1,000001.SZ,20201230,19,19.2,18.72,19.2,19.17,0.03,0.1565,978497.78,1854082.812,19.1,19.1,19.1,19.1,0.1,0.12,0.1
2,000001.SZ,20201229,18.87,19.3,18.7,19.17,18.85,0.32,1.6976,963092.23,1837947.238,19.02,19.02,19.02,19.02,0.1,0.12,0.1
3,000001.SZ,20201228,18.02,18.86,17.96,18.85,18.04,0.81,4.49,1270337.06,2352947.321,18.435,18.435,18.435,18.435,0.1,0.12,0.1
4,000001.SZ,20201225,18.26,18.26,17.8,18.04,18.26,-0.22,-1.2048,577077.33,1038128.197,18.15,18.15,18.15,18.15,0.1,0.12,0.1
5,000001.SZ,20201224,18.15,18.49,18.05,18.26,18,0.26,1.4444,632950.12,1155767.283,18.205,18.205,18.205,18.205,0.1,0.12,0.1
6,000001.SZ,20201223,17.96,18.07,17.79,18,17.9,0.1,0.5587,750082.73,1345650.736,17.98,17.98,17.98,17.98,0.1,0.12,0.1
7,000001.SZ,20201222,18.36,18.65,17.88,17.9,18.28,-0.38,-2.0788,1028737.51,1873638.734,18.13,18.13,18.13,18.13,0.1,0.12,0.1
8,000001.SZ,20201221,18.3,18.64,18.03,18.28,18.36,-0.08,-0.4357,906878.11,1662649.81,18.29,18.29,18.29,18.29,0.1,0.12,0.1
9,000001.SZ,20201218,19,19.1,18.23,18.36,18.95,-0.59,-3.1135,1058756.36,1959764.074,18.68,18.68,18.68,18.68,0.1,0.12,0.1
10,000001.SZ,20201217,18.91,19.07,18.6,18.95,19.01,-0.06,-0.3156,686140.69,1294658.299,18.93,18.93,18.93,18.93,0.1,0.12,0.1
11,000001.SZ,20201216,18.8,19.1,18.58,19.01,18.74,0.27,1.4408,689070.42,1302133.815,18.905,18.905,18.905,18.905,0.1,0.12,0.1
12,000001.SZ,20201215,18.8,18.85,18.15,18.74,18.88,-0.14,-0.7415,788857.94,1458268.62,18.77,18.77,18.77,18.77,0.1,0.12,0.1
13,000001.SZ,20201214,18.6,19.06,18.5,18.88,18.52,0.36,1.9438,835991.74,1575396.008,18.74,18.74,18.74,18.74,0.1,0.12,0.1

 吐槽一下博客园,到现在传个文件,还不能在这个界面完成,唉。

标签:idx,python,self,全功能,0.1,fig,mplfinance,font,data
From: https://www.cnblogs.com/szyicol/p/16708958.html

相关文章

  • python 字符串倒序
    #面试题:给你一个字符串,请将这个字符串翻转。name="生活不是电影,生活比电影苦"-->答案在底部,看答案前先思考哦              value=......
  • python 数据类型之整型,布尔,字符串
    python数据类型包含以下几种-int,整数类型(整形)-bool,布尔类型-str,字符串类型-list,列表类型-tuple,元组类型-dict,字典类型-set,集合类型-float,浮点类型(浮点型)1.整型--......
  • python格式化输出输出数据到json文件
    input_python={'n_layer':n_layer,'L':L,'Emm':Emm,'mu':mu,'h':h,'P':P,'Q':......
  • python利用logging模块实现根据日志级别打印不同颜色日志
    logger:日志器对象,可通过logging.getLogger()方法获取handler:处理器对象,将日志信息输出到指定位置,可通过logger.addHandler()方法进行添加formatter:格式器对象,输出格式化......
  • Python获取UTC时间
    fromdatetimeimportdatetime,timedeltanow_time=datetime.now()utc_time=now_time-timedelta(hours=8)#减去8小时utc_time=utc_time.strftime("%Y-%m-%......
  • Python在字典中通过键名查找键值
    deffind(target,dict_data):""":paramtarget:需要查找的键名:paramdict_data:需要查找的列表:return:如果找到就返回对应键名的键值,否则提示没......
  • Python简单操作!!
    一文肝完Python文件操作知识!点击关注......
  • python读写文件模板记录
    目录读写模式读文件read(可选:size)一次性读全部内容readline()读取一行内容readlines()读取所有内容,返回列表从file中读取每行等同于readlines()的功能写......
  • Python多线程编程——threading模块
    本文参考:https://blog.csdn.net/youngwyj/article/details/124720041https://blog.csdn.net/youngwyj/article/details/124833126目录前言threading模块1.简介2.创建线......
  • python-程序控制-for
    1.for循环的一般形式fortmpVarinIterable:blocktmpVar是临时变量Iterable是可迭代对象第一行是循环条件,当对可迭代对象的迭代遍历结束时,for循......