如果你想找的是求最大回撤的算法,请跳转:[Python] 使用动态规划求解最大回撤详解
【Python】基金/股票 最大回撤率计算与绘图详解(附源码和数据)
0. 起因
前一段时间投了一家金融公司的Python开发工程师,让我做以下事情:
- 以月为窗口,挑选一只基金or股票,查看它近2年最大回撤率;
- 找到从08年-至今的金融危机时间段,并呈现在这些危机时间段内,该基金or股票的历次回撤率。
给了3天时间,代码我1天不到就写完了,写完之后我就去做自己的项目了,沉迷技术无法自拔~~~
3天过后,没想到打电话过来的是产品经理,完全不问代码的事情,问我平时是怎么学习的;问我有没有考虑为什么要分析这些东西;问我以后的方向;问我学习中遇到过的最大的问题是什么,我说了我最近做的一个网站遇到的问题;
我觉得自己答的都还不错,也许是没答到人家内心里去吧,问了几个问题之后,我感觉人家貌似情绪有点不对了。隔了几天才收到技术面,问的都是NumPy和Pandas,因为几个月前学的还不错,加上问的比较基础(没有涉及到时间序列和透视表那些),所以大部分问题都答上来了。不过还是没有收到Offer,缘分未到~
言归正传,我选了一家老牌的基金 github 源码和数据
1. 大成沪深300指数A 519300 最大回撤率分析
数据来源:http://fund.eastmoney.com/519300.html?spm=search
天天基金官网:大成沪深300指数A 519300
0. 基金走势图 2007 - 2020
官网上分析数据接口,清洗数据之后,我绘制出来的图 2006 - 2020
1. 以月为窗口,挑选一只基金or股票,查看它近2年最大回撤率;
2.找到从08年-至今的金融危机时间段,并呈现在这些危机时间段内,该基金or股票的历次回撤率。
很多基金成立年限都不高,没有经历过金融危机,这只基金是我找了些资料才找出来的,成立了14个年头。
2. 绘制走势图和最大回撤的函数封装
封装了两个函数,一个用于绘制走势图,一个用于绘制走势图和回撤率。效果如下:
import matplotlib.pyplot as plt
import pylab as pl
def draw_trend_chart(xs, ys, title):
""" 根据数据绘制折线走势图 """
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.plot(xs, ys) # 根据数据绘制折线走势图
plt.title(title)
plt.show()
def draw_trend_and_withdraw(xs, ys, title, max_x, max_y, show_max_str, min_x, min_y, show_min_str,
withdraw, withdraw_x=None, withdraw_y=None, x_ticks_rotation=None):
""" 根据数据绘制折线走势图和最大回撤信息 """
"""
xs: x轴数组
ys: y轴数组
title: 走势图标题
max_x, max_y: 最大回撤最高点的x值和y值
min_x, min_y: 最大回撤最低点的x值和y值
show_max_str: 最大回撤最高点的标记提示
show_min_str: 最大回撤最低点的标记提示
withdraw: 最大回撤值
withdraw_x: 最大回撤值提示点的x值
withdraw_y: 最大回撤值提示点的y值
"""
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.plot(xs, ys) # 根据数据绘制折线走势图
plt.title(title)
plt.scatter(min_x, min_y, color='r') # 标记最低点
plt.scatter(max_x, max_y, color='r') # 标记最高点
plt.annotate(show_min_str, xytext=(min_x, min_y), xy=(min_x, min_y)) # 标记提示
plt.annotate(show_max_str, xytext=(max_x, max_y), xy=(max_x, max_y)) # 标记提示
plt.plot([min_x, max_x], [min_y, max_y], color='b', linestyle='--') # 连接最低净值点和最高净值点
if withdraw_x is None or withdraw_y is None:
plt.annotate(withdraw, xytext=((max_x + min_x) / 2, (max_y + min_y) / 2), xy=((max_x + min_x) / 2, (max_y + min_y) / 2)) # 标记提示
else:
plt.annotate(withdraw, xytext=(withdraw_x, withdraw_y), xy=(withdraw_x, withdraw_y)) # 标记提示
if x_ticks_rotation is not None: # 旋转 x 轴的标记
pl.xticks(rotation=60)
plt.show()
if __name__ == '__main__':
xs = range(0, 9)
arr = [3, 7, 2, 6, 4, 1, 9, 8, 5]
_max, _min = 1, 5 # 最高点索引和最低点索引
max_rate = 6 # 最大回撤
draw_trend_chart(xs, arr, title=f'{arr}')
draw_trend_and_withdraw(list(range(0, 9)), arr, f'{arr}', _max, arr[_max], f'最高点索引:{_max}',
_min, arr[_min], f'最低点索引:{_min}', max_rate)
3. 从0到1分析最大回撤率
0. 求取最大回撤的函数
除了两个绘图函数,还有求最大的回撤的函数:
def _withdraw_with_high_low(arr):
""" 传入一个数组,返回最大回撤和对应的最高点索引、最低点索引 """
_dp = 0 # 使用 _dp 表示 i 点的最大回撤
i_high = 0 # 遍历时,0 ~ i - 1 中最高的点的索引,注意是索引
# 全局最大回撤和对应的最高点和最低点的索引,注意是索引
g_withdraw, g_high, g_low = float('-inf'), -1, -1
for i in range(1, len(arr)):
# 注意:此处求的是
if arr[i_high] < arr[i-1]: # 若 0 ~ i - 1 中最高的点小于当前点
i_high = i-1 # 0 ~ i - 1 中最高的点的索引
_dp = arr[i_high