结合Futuapi和pyechart绘制带有MACD和均线的K线图,支持缩放和基础的图例展示隐藏

结合Futuapi和pyechart绘制带有MACD和均线的K线图,支持缩放和基础的图例展示隐藏。
首先需要先到富途牛牛上找futuapi然后安装搭建环境。
网络上很多代码都不全,要么就引导取收费,这…做一点小数据分析就这么困难么:)

实现效果如图:
在这里插入图片描述

# 绘制K线图 & MACD & MA

from futu import *
import datetime
from pyecharts import options as opts
from pyecharts.charts import Kline, Line, Bar, Grid
import numpy as np
from pyecharts.commons.utils import JsCode

FUTUOPEND_ADDRESS = '127.0.0.1'  # FutuOpenD 监听地址
FUTUOPEND_PORT = 11111  # FutuOpenD 监听端口

TRADING_PERIOD = KLType.K_DAY  # 信号 K 线周期
TRADING_CODE = 'HK.00700'  # 股票标的
START_DATE = '2018-09-11'  # 股票回溯开始日期
END_DATE = datetime.date.today().strftime('%Y-%m-%d')  # 股票回溯结束日期
FAST_MOVING_AVERAGE = 5  # 均线快线的周期
SLOW_MOVING_AVERAGE = 10  # 均线慢线的周期

# MACD相关设定
MACD_SHORT = 12
MACD_LONG = 26
MACD_SIGNAL = 9


# 根据收盘价,计算MA, 返回快线和慢线数组
def calculate_MA(closeArray, fast_param, slow_param):
    fast_values = []  # 快线数组
    slow_values = []  # 慢线数组

    for i in range(len(closeArray)):
        if i < FAST_MOVING_AVERAGE:
            fast_values.append(np.nan)
        else:
            fast_value = format(sum(closeArray[i - FAST_MOVING_AVERAGE:i]) / FAST_MOVING_AVERAGE,
                                '.2f')  # 四舍五入取2位小数
            fast_values.append(float(fast_value))

        if i < SLOW_MOVING_AVERAGE:
            slow_values.append(np.nan)
        else:
            slow_value = format(sum(closeArray[i - SLOW_MOVING_AVERAGE:i]) / SLOW_MOVING_AVERAGE, '.2f')
            slow_values.append(float(slow_value))

    return fast_values, slow_values


# 根据收盘价,计算MACD,返回macd,diff,dea
def calculateEMA(period, closeArray, emaArray=[]):
    """计算指数移动平均"""
    length = len(closeArray)
    nanCounter = np.count_nonzero(np.isnan(closeArray))
    if not emaArray:
        emaArray.extend(np.tile([np.nan], (nanCounter + period - 1)))
        firstema = np.mean(closeArray[nanCounter:nanCounter + period - 1])
        emaArray.append(firstema)
        for i in range(nanCounter + period, length):
            ema = (2 * closeArray[i] + (period - 1) * emaArray[-1]) / (period + 1)
            emaArray.append(ema)
    return np.array(emaArray)


def calculateMACD(closeArray, shortPeriod=12, longPeriod=26, signalPeriod=9):
    ema_short = calculateEMA(shortPeriod, closeArray, [])
    ema_long = calculateEMA(longPeriod, closeArray, [])
    diff = np.round((ema_short - ema_long), 2)
    dea = np.round(calculateEMA(signalPeriod, diff, []), 2)
    macd = 2 * (diff - dea)
    return macd, diff, dea


# 画k线图
def draw_klines(data, self=None):
    klineData = data[['open', 'close', 'low', 'high']].values.tolist()  # 将pd转换为数组 股价
    klineX = data['time_key'].values.tolist()
    klineXData = [dateStr[0:10] for dateStr in klineX]  # 横坐标 日期

    kline = Kline()
    kline.add_xaxis(xaxis_data=klineXData)
    kline.add_yaxis(
        series_name="股价/港币",
        y_axis=klineData,
        itemstyle_opts=opts.ItemStyleOpts(opacity=.8),
    )
    kline.set_global_opts(
        xaxis_opts=opts.AxisOpts(
            name='日期',
            is_scale=True,
        ),
        yaxis_opts=opts.AxisOpts(
            name='股价/港币',
            is_scale=True,
            splitarea_opts=opts.SplitAreaOpts(
                is_show=True,
                areastyle_opts=opts.AreaStyleOpts(opacity=0.7)
            ),
        ),
        datazoom_opts=[
            opts.DataZoomOpts(
                is_show=False,
                type_="inside",
                xaxis_index=[0, 1],
                range_start=80,
                range_end=100,
            ),
            # xaxis_index=[0, 0]设置第一幅图为内部缩放
            opts.DataZoomOpts(
                is_show=True,
                type_="slider",
                xaxis_index=[0, 1],
                pos_top="95%",
                range_start=80,
                range_end=100,
            ),
            # xaxis_index=[0, 1]连接第二幅图的axis
        ],
        title_opts=opts.TitleOpts(title="股价k线图"),
        tooltip_opts=opts.TooltipOpts(trigger='axis', axis_pointer_type='cross'),
    )

    maLine = Line()
    maLine.add_xaxis(klineXData)
    closeArray = data['close'].values.tolist()
    fast_MA, slow_MA = calculate_MA(closeArray, FAST_MOVING_AVERAGE, SLOW_MOVING_AVERAGE)  # 计算均线

    maLine.add_yaxis(
        series_name="MA" + str(FAST_MOVING_AVERAGE),
        y_axis=fast_MA,
        is_smooth=True,
        linestyle_opts=opts.LineStyleOpts(opacity=0.9, width=2),
        label_opts=opts.LabelOpts(is_show=False),  # 不显示具体数值
        z=3,  # ma在最顶层
    )

    maLine.add_yaxis(
        series_name="MA" + str(SLOW_MOVING_AVERAGE),
        y_axis=slow_MA,
        is_smooth=True,
        linestyle_opts=opts.LineStyleOpts(opacity=0.9, width=2),
        label_opts=opts.LabelOpts(is_show=False),
        z=4,
    )

    KlineWithMA = kline.overlap(maLine)  # MA画在k线上
    return KlineWithMA


# 画MACD
def drawMACD(data):
    closeArray = data['close'].values.tolist()
    DIF, DEA, MACD = calculateMACD(closeArray, MACD_SHORT, MACD_LONG, MACD_SIGNAL)

    klineX = data['time_key'].values.tolist()
    klineXData = [dateStr[0:10] for dateStr in klineX]

    bar_2 = Bar()
    bar_2.add_xaxis(klineXData)
    bar_2.add_yaxis(
        series_name="MACD",
        y_axis=MACD.tolist(),
        xaxis_index=1,
        yaxis_index=1,
        label_opts=opts.LabelOpts(is_show=False),
        z=0,
        itemstyle_opts=opts.ItemStyleOpts(
            opacity=0.7,
            color=JsCode(
                # 就是这么神奇,要写在注释中,但是的确是生效的,通过浏览器进行解析
                """
                                    function(params) {
                                        var colorList;
                                        if (params.data >= 0) {
                                          colorList = '#D3403B';
                                        } else {
                                          colorList = '#66A578';
                                        }
                                        return colorList;
                                    }
                                    """
            )
        )
    )

    bar_2.set_global_opts(
        xaxis_opts=opts.AxisOpts(
            type_="category",
            grid_index=1,
            axislabel_opts=opts.LabelOpts(is_show=True),
            is_scale=True,
        ),
        yaxis_opts=opts.AxisOpts(
            grid_index=1,
            split_number=4,
            axisline_opts=opts.AxisLineOpts(is_on_zero=False),
        ),

        legend_opts=opts.LegendOpts(is_show=True, pos_top='70%'),  # 图例
    )

    line_2 = Line()
    line_2.add_xaxis(klineXData)
    line_2.add_yaxis(
        series_name="DIF",
        y_axis=DIF,
        xaxis_index=1,
        yaxis_index=2,
        label_opts=opts.LabelOpts(is_show=False),
        linestyle_opts=opts.LineStyleOpts(opacity=1.0, width=2),
    )
    line_2.add_yaxis(
        series_name="DEA",
        y_axis=DEA,
        xaxis_index=1,
        yaxis_index=2,
        label_opts=opts.LabelOpts(is_show=False),
        linestyle_opts=opts.LineStyleOpts(opacity=1.0, width=2),
    )

    overlap_bar_line = bar_2.overlap(line_2)
    return overlap_bar_line


def drawAll(overlap_kline_ma, overlap_bar_line):
    grid_chart = Grid(init_opts=opts.InitOpts(width="2560px", height="800px"))
    # K线图和 MA 的折线图
    grid_chart.add(
        overlap_kline_ma,
        grid_opts=opts.GridOpts(pos_left="3%", pos_right="3%", height="60%"),
    )
    # MACD DIFS DEAS
    grid_chart.add(
        overlap_bar_line,
        grid_opts=opts.GridOpts( pos_left="3%", pos_right="3%", pos_top="72%", height="20%"),
    )
    return grid_chart


def getGrid(kdata):
    k = draw_klines(kdata)
    m = drawMACD(kdata)
    return drawAll(k, m)


# 主函数
if __name__ == '__main__':
    quote_ctx = OpenQuoteContext(host=FUTUOPEND_ADDRESS, port=FUTUOPEND_PORT)  # 获取行情对象
    # 获取历史k线数据
    ret, data, page_req_key = quote_ctx.request_history_kline(TRADING_CODE, start=START_DATE, end=END_DATE)
    if ret != RET_OK:
        print('获取K线失败:', data)

    grid_chart = getGrid(data)
    grid_chart.render("test007.01.html")

    quote_ctx.close()

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值