使用Python构建量化小程序

本文介绍基于Python构建查询历史股票行情的应用。

1 数据获取

量化数据接口有很多,有tushare、alltick、xtquant等等。本文使用的是akshare库,它是开源的财经数据库,不仅包含股票、期货、期权、外汇、基金等常见的金融数据,还包括能源、事件、舆情和艺人指数这类不常见但可能影响市场行为的其他数据,可以说是目前市面上免费接口里最全的一个。

以获取深市A股历史日线数据为例,首先获取深市A股的股票信息:

python 复制代码
import akshare as ak
stock_info = ak.stock_info_sz_name_code()
stock_info.to_excel('stock_code.xlsx')

2 应用架构

本应用基于dash框架、开源组件库feffery_antd_components与akshare金融数据接口开发的轻量级应用。app.py中进行应用对象的实例化,并构建应用的初始化页面内容;setting.css进行前端界面样式的调整;data储存的是深交所A股信息。

python 复制代码
- demo
  - data
    - stock_code.xlsx
  - assets
    - setting.css
  - app.py

3 应用启动

安装好当前项目依赖库,然后直接在终端执行python app.py即可启动应用,按照控制台提示的信息,浏览器访问本地http://127.0.0.1:8050地址即可访问应用。

4 代码

python 复制代码
# app.py
import pandas as pd
import dash
from pyecharts import options as opts
import akshare as ak
from dash import Dash, html, dcc, callback, Output, Input, State
import dash_bootstrap_components as dbc
from pyecharts.charts import Kline
import feffery_antd_components as fac


# 应用实例化
app = Dash(
    __name__,
    title='量化小应用',
    update_title='加载中...',
    assets_url_path='assets/setting.css'
)

app.layout = fac.AntdSpace([
    dcc.Store(storage_type='local', id='stock-code-store', data=pd.read_excel('data\stock_code.xlsx', dtype={'A股代码': 'str'}).to_dict('records')),
    fac.AntdFlex([
        fac.AntdIcon(icon='antd-right', style={'color': '#0069d9'}),
        fac.AntdText('股票历史日线数据查询', className='header')
    ]),
    fac.AntdFlex([
        fac.AntdFlex([
            '股票代码:',
            dcc.Dropdown(
                id='stock-dropdown',
                multi=False,
                searchable=True,
                value='000001',
                placeholder='股票代码',
                style={'width': '100px'}
            ),
        ], align='center', gap='small'),
        fac.AntdFlex([
            '日期:',
            fac.AntdDateRangePicker(
                placeholder=['选择开始日期', '选择结束日期'],
                id='stock-dropdown datepicker',
                size='middle',
                prefix=fac.AntdIcon(icon='antd-calendar')
            ),
        ], gap='small'),
        fac.AntdButton(
            '搜索',
            type='primary',
            id='stock-dropdown btn',
            loadingChildren="查询中",
        ),
    ], align='center', gap='large'),
    fac.AntdCard(
        title='收益概述',
        headStyle={'background': 'rgba(0, 0, 0, 0.3)', 'text-align': 'left'},
        id='card-content',
        className='card'
    ),
], className='container')


@app.callback(
    Output('stock-dropdown', 'options'),
    Input('stock-code-store', 'data')
)
def code_info(data):
    data = pd.DataFrame(data)
    data['A股代码'] = data['A股代码'].astype('str')
    options = [
        {'label': x, 'value': 'sz' + y}
        for x, y in zip(data['A股代码'], data['A股代码'])
    ]
    return options


@app.callback(
    Output('card-content', 'children'),
     Input('stock-dropdown btn', 'nClicks'),
    [State('stock-code-store', 'data'),
     State('stock-dropdown', 'value'),
     State('stock-dropdown datepicker', 'value')],
    running=[(Output('stock-dropdown btn', "loading"), True, False)],
)
def update(nClicks, data, value, date):
    stock_info = pd.DataFrame(data)

    stock_info = stock_info[stock_info['A股代码'] == value[2:]].to_dict('records')
    title = stock_info[0]['A股简称'] + '(SZ:' + stock_info[0]['A股代码'] + ')'

    result = ak.stock_zh_a_daily(symbol=value, start_date=date[0], end_date=date[1])
    result.rename(columns={'open': '开盘价', 'close': '收盘价', 'low': '最低', 'high': '最高'}, inplace=True)
    fig = (
        Kline(init_opts=opts.InitOpts(width='1200px', height='300px'))
        .add_xaxis(list(result['date']))
        .add_yaxis(series_name='k线', y_axis=result[['开盘价', '收盘价', '最低', '最高']].values.tolist(),
                   itemstyle_opts=opts.ItemStyleOpts(color='rgb(192, 51, 47)', color0='green'))
        .set_global_opts(
            title_opts=opts.TitleOpts(title=f'{date[0]}-{date[1]}日K线'),
            yaxis_opts=opts.AxisOpts(name='单位净值(元)', min_=result[['开盘价', '收盘价', '最低', '最高']].min().min(), max_=result[['开盘价', '收盘价', '最低', '最高']].max().max()),
            tooltip_opts=opts.TooltipOpts(trigger='axis', axis_pointer_type='cross', formatter='{b}: {c}', border_color='#ccc', background_color='rgba(245, 245, 245, 0.8)'),
            datazoom_opts=opts.DataZoomOpts(is_show=True, range_start=result['date'].min(), range_end=result['date'].max()),
        )
        .set_series_opts(
            markpoint_opts=opts.MarkPointOpts(data=[opts.MarkPointItem(type_='max', name='最高价', value=result['最高'].max()), opts.MarkPointItem(type_='min', name='最低价', value=result['最低'].min())], symbol='pin'),
            splitline_opts=opts.SplitAreaOpts(is_show=True),
            linestyle_opts=opts.LineStyleOpts(type_='dashed', color='lightgrey'),
        )
    )
    fig.render('kline.html')

    card_layout = [
        fac.AntdRow(title, className='card-content title', wrap=True),
        fac.AntdRow([
            fac.AntdCol([
                fac.AntdText('最高:', className='card-content index'),
                fac.AntdText(result['最高'].max(), className='card-content value', style={'color': 'red'})
            ]),
            fac.AntdCol([
                fac.AntdText('最低:', className='card-content index'),
                fac.AntdText(result['最低'].max(), className='card-content value', style={'color': 'green'})
            ]),
            fac.AntdCol([
                fac.AntdText(f'成交量:{str(result['volume'].sum())}手', className='card-content index'),]),
            fac.AntdCol([
                fac.AntdText(f'成交额:{str(result['amount'].sum())}元', className='card-content index'),]),
        ], gutter=20),
        html.Iframe(
            srcDoc=open('kline.html', 'r').read(),
            style={
               'height': 300,
               'width': '100%',
               'align': 'center'
            }
        )
    ]
    return card_layout


if __name__ == '__main__':
    app.run(debug=False)
css 复制代码
/* setting.css*/
html,body {
    background-color: #ffffff;
    color: #000000;
    text-align: center !important;
    margin: 1%;
    padding: 1%;
    font-family: 'Sans ser-if';
}

.container {
    display: grid;
    width: 1200px;
}

/* 标题样式 */
.header {
    font-size: 18px;
    font-weight: bold;
}

/* 卡片样式 */
.card {
    width: 1400px;
}

.ant-card-body {
    display: flex;
    flex-direction: column;
}

.card-content {

}
.title {
    font-size: 24px;
    font-weight: bold;
}
.index {
    font-size: 12px;
    font-weight: bold;
}
.value {
    font-weight: bold;
}

5 优化方向

  • 由于本文涉及的数据交互与计算较少,历史数据获取的方式是直接调用akshare库,而该库有的数据不能频繁获取。若数据量增多,为提高系统效率与性能,需要采取适当的方式,将数据持久化保存到本地。
  • 构建成多页面应用,一个页面存放一个指标
相关推荐
烛阴40 分钟前
一文搞懂 Python 闭包:让你的代码瞬间“高级”起来!
前端·python
JosieBook1 小时前
【Java编程动手学】Java中的数组与集合
java·开发语言·python
Gyoku Mint2 小时前
深度学习×第4卷:Pytorch实战——她第一次用张量去拟合你的轨迹
人工智能·pytorch·python·深度学习·神经网络·算法·聚类
郭庆汝7 小时前
pytorch、torchvision与python版本对应关系
人工智能·pytorch·python
思则变11 小时前
[Pytest] [Part 2]增加 log功能
开发语言·python·pytest
漫谈网络11 小时前
WebSocket 在前后端的完整使用流程
javascript·python·websocket
try2find12 小时前
安装llama-cpp-python踩坑记
开发语言·python·llama
博观而约取13 小时前
Django ORM 1. 创建模型(Model)
数据库·python·django
精灵vector15 小时前
构建专家级SQL Agent交互
python·aigc·ai编程
Zonda要好好学习15 小时前
Python入门Day2
开发语言·python