使用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库,而该库有的数据不能频繁获取。若数据量增多,为提高系统效率与性能,需要采取适当的方式,将数据持久化保存到本地。
  • 构建成多页面应用,一个页面存放一个指标
相关推荐
笨鸟先飞,勤能补拙1 小时前
AI 赋能网络安全:技术全景、成熟度评估与实战案例
人工智能·python·安全·web安全·网络安全·sqlite·github
长和信泰光伏储能2 小时前
京津冀光伏发电:绿色能源的未来之路
python·能源
浦信仿真大讲堂2 小时前
从重复操作到自动化闭环:如何让 CST 与 Python 真正协同起来
python·自动化·cst·仿真软件·达索软件
Gu Gu Study3 小时前
ScoutLoop开放域深度研究引擎(agent的初步设计想法)
人工智能·python
卷无止境4 小时前
写代码这件事,到底该讲究点什么?
后端·python
卷无止境4 小时前
循环复杂度到底在算什么,Python 代码怎么才能写得让人一看就懂
后端·python
lpfasd1234 小时前
MediaCrawler 项目深度分析
chrome·python·chrome devtools
Dxy12393102164 小时前
Python项目打包成EXE完整教程(PyInstaller实战避坑)
开发语言·python
bamb005 小时前
一个项目带你入门AI应用开发01
python
0566465 小时前
Python康复训练——常用标准库
开发语言·python·学习