pandas_计算最大回撤

目录

概念 

一、数据为收盘价,计算最大回撤

二、数据为净值,计算最大回撤


概念 

计算方法

1. 将收益率做成时间序列
2. 计算财富指数(也就是净值)【PS:初始净值为1】
3. 计算上一个最高点
4. 计算回撤率
5. 找出最大回撤

日期收益率财富指数(净值)上一个高点回撤率
02-190.014483

1*(1+0.014483)

=1.014483

1.0144830
02-20-0.010259

1.014483*(1-0.010259)

=1.004075

1.014483-0.010259
02-21-0.022635

1.004075*(1-0.022635)

=0.981348

1.014483-0.032662
02-24-0.047500

0.981348*(1-0.047500)

=0.934734

1.014483-0.078611
02-25-0.033872

0.934734*(1-0.033872)

=0.903072

1.014483-0.109820
02-260.015864

0.903072*(1+0.015864)

=0.917398

1.014483-0.095699

一、数据为收盘价,计算最大回撤

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
df_aapl = pd.read_csv('AAPL.csv',encoding='utf-8')
df_aapl['ret'] = df_aapl['Close'].pct_change()
df_aapl['Date'] = pd.to_datetime(df_aapl['Date'])
df_aapl.set_index('Date',inplace=True)
df = df_aapl.iloc[-504:]
df.head()

 

# 构建财富指数
wealth = 1*(1+df['ret']).cumprod()
wealth.head()

 

wealth.plot()

 

# 计算上一个最高点
previos_max = wealth.cummax()
previos_max.plot()

 

# 计算回撤率
drawdowns = (wealth-previos_max)/previos_max
drawdowns.plot()

 

# 找出最大回撤
drawdowns.min()
# out: -0.30668685958066383

# 最大回撤对应的日期
drawdowns.idxmin()
# out: Timestamp('2020-03-23 00:00:00')

 将上面的计算过程组合成一个函数

def drawdown(return_series:pd.Series):
    '''
    把一个时间序列做成最大回撤的表格
    表格字段为:
    财富指数
    上一个最大值
    回撤率
    '''
    wealth = 1*(1+return_series).cumprod()
    previos_max = wealth.cummax()
    drawdowns = (wealth-previos_max)/previos_max
    return pd.DataFrame({'wealth':wealth,
                        'previos_max':previos_max,
                        'drawdowns':drawdowns})
res_df = drawdown(df['ret'])
res_df.head()

 

res_df.plot(y=['wealth','previos_max'],figsize=(8,4))

 

res_df.plot(y='drawdowns',figsize=(8,4),color='k')

 

 

二、数据为净值,计算最大回撤

前置: 

文章中用到的数据

链接:https://pan.baidu.com/s/1rKLM45dq_xIKxcI54Nq0qg 
提取码:c298

最终效果图:

计算过程(jupyter notebook):

import matplotlib.pyplot as plt
import pylab as pl
import pandas as pd
import math
df = pd.read_csv('./temptemp.csv',encoding='utf-8')
df.head()

# 绘制折线图,标记回撤区域
def draw_trend_and_withdraw(xs,ys,title,res_points):
    plt.figure(figsize=(20,10))
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.rcParams['axes.unicode_minus'] = False
    xs00 = range(len(xs))
    plt.plot(xs00,ys)
    
    # 只显示10个x轴刻度
    xs00_=[]
    xs_ =[]
    for i in range(0,len(xs),math.floor(len(xs)/10)):
        xs00_.append(i)
        xs_.append(xs[i])
    
    plt.xticks(xs00_,xs_,rotation=30)
    plt.title(title)
    for item in res_points:
        min_x = item['min_x']
        min_y = item['min_y']
        max_x = item['max_x']
        max_y = item['max_y']
        show_min_str = item['show_min_str']
        show_max_str = item['show_max_str']
        withdraw = item['withdraw']
        
        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='--')  # 连接最低净值点和最高净值点
        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))  # 标记提示
        pass
    plt.show()
df['o_date'] = df['date']
df['o_date'] = pd.to_datetime(df['o_date'])
df.dropna(inplace=True)
df.sort_values(by='o_date',ascending=True,inplace=True)
df['count'] = range(len(df)) # count用于标识折线图x轴的位置
res_list = []
temp_hv = None # 记录当前最大值
temp_hv_date = None # 记录当前最大值对应的日期
temp_hv_loc = None # 记录当前最大值所在的位置
temp_lv = None # 记录当前最小值
temp_lv_date = None # 记录当前最小值对应的日期
temp_lv_loc = None # 记录当前最小值所在的位置
temp_duration = None # 最大值与最小值之间的差
for i,row in df.iterrows():
    if temp_hv is None:
        temp_hv = row['value']
        temp_hv_date = row['date']
        temp_hv_loc = row['count']
        temp_lv = row['value']
        temp_lv_date = row['date']
        temp_lv_loc = row['count']
        temp_duration = 0
    else:
        if row['value'] > temp_hv:
            # 如果值大于此前的最大值,说明要进行新一轮的回撤,记录当前这一轮回撤的信息
            # 计算回撤百分比
            temp_pct = (temp_duration/temp_hv)*100
            res_list.append([temp_hv_loc,temp_hv_date,temp_hv,temp_lv_loc,temp_lv_date,temp_lv,temp_pct,temp_duration])
            temp_hv = row['value']
            temp_hv_date = row['date']
            temp_hv_loc = row['count']
            temp_lv = row['value']
            temp_lv_date = row['date']
            temp_lv_loc = row['count']
            temp_duration = 0
            pass
        else:
            if row['value'] <= temp_lv:
                # 当前值小于此前的最小值,说明数值还在往下走
                temp_lv = row['value']
                temp_lv_date = row['date']
                temp_lv_loc= row['count']
                temp_duration = temp_hv-temp_lv
# 最后一次的回撤:最后的价格一直没有超过此前一次的最高价,最后一次的回撤会无法触发记录的条件
temp_pct = (temp_duration/temp_hv)*100
res_list.append([temp_hv_loc,temp_hv_date,temp_hv,temp_lv_loc,temp_lv_date,temp_lv,temp_pct,temp_duration])
# 抽取出回撤大于等于10%的区间,并在图中标出
final_list = []
for item in res_list:
    if item[-2]>=10:
        pct_ = round(item[-2],2)
        final_list.append({
            "min_x":item[0],
            "min_y":item[2],
            "max_x":item[3],
            "max_y":item[5],
            "show_min_str":item[1],
            "show_max_str":item[4],
            "withdraw":str(pct_)+'%'
        })
xs = df['date'].values.tolist()
ys = df['value'].values.tolist()
title_str = '回撤大于等于10%'
draw_trend_and_withdraw(xs,ys,title_str,final_list)

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值