哪个策略最赚钱? 捞底 vs 定期定额 vs 一次性投入资金

无论你是投资老手还是刚开始学习投资的小白
你都会留意到网路上人们都在讨论投资策略,捞底,还是不管市场高低定期定额投资等

“投资者” 会教你要长期持有,如果资金不大就定期定额投资
“投机者” 会教你要根据市场的趋势,情绪来交易,以”趋势是你的朋友”的理念来投资

其实双方的论点都听起来行得通,这里我们只相信数据
数据怎么说呢?到底哪个策略最赚钱?

数据读取

我从Yahoo Finance上下载了各大指数的历史数据,

要分析数据得先读取这个csv文件。

import pandas as pd
import datetime
import matplotlib.pyplot as plt
pd.options.mode.chained_assignment = None  # default='warn'
plt.style.use('seaborn')

period_type = 'M'

def read_and_process_data(name):
    df = pd.read_csv(name,parse_dates=['Date'])
    df = df.replace(',','', regex=True)
    df['Open'] = df['Open'].astype(float)
    df['Close'] = df['Close'].astype(float)

    data = df.resample(period_type, on = 'Date').last()
    data = data[data['Date'] > '01-01-2011']

    data['Open'] = data['Open'].resample(period_type).first()
    data['High'] = data['High'].resample(period_type).max()
    data['Low'] = data['Low'].resample(period_type).min()
    data['Close'] = data['Close'].resample(period_type).last()

    data['Change'] = ((data['Close'].shift(-1) - data['Close'])/data['Close']) + 1
    data['All_Time_High'] = data['Close'].cummax()
    data['Previous_Peak'] = data['Close'].rolling(24, min_periods = 1).max()
    data['Previous_Peak'] = data['Previous_Peak'].ffill()
    data['Changes_From_Previous_Peak'] = (data['Previous_Peak'] - data['Close'])/data['Previous_Peak']

    data.dropna(axis=1, how='all', inplace = True)
    data = data.fillna(1)    
    return data

data = read_and_process_data('MSGX.csv')
data.head()
DateOpenHighLowCloseAdj CloseChangeAll_Time_HighPrevious_PeakChanges_From_Previous_Peak
Date
2011-01-312011-01-313190.043280.773177.783179.723179.720.9467853179.723179.720.000000
2011-02-282011-02-283179.723232.992965.243010.513010.511.0316693179.723179.720.053215
2011-03-312011-03-313010.513115.472919.983105.853105.851.0215343179.723179.720.023232
2011-04-302011-04-303105.853208.343103.223172.733172.730.9959663179.723179.720.002198
2011-05-312011-05-313179.863182.463078.263159.933159.930.9875033179.723179.720.006224

策略

这次我们回测的有4个策略

  1. 每月买入相同的数额
  2. 每个存下相同的数额,在市场从24个月高点跌了20%才买入
  3. 一次性投入,买入并持有
  4. 一次性投入,买入并持有,市场破历史新高时卖出,在市场从24个月高点跌了20%才买入

每个策略投入的总金额都是一样的

initial_capital = 100
monthly_topup = 100
lumpsum = initial_capital + len(data)*monthly_topup

data['Capital_DCA'] = 0 # Buy fixed amount every month
data['Capital_buythedip'] = 0 # Save up fixed amount every month, only buy when there is 20% dip from previous peak
data['Capital_lumpsum'] = 0 # Buy and hold from beginning
data['Capital_lumpsum_tactical'] = 0 # Buy and hold from beginning, sell when price breaks all time high, buy when 20% dip
data['Capital'] = 0
data['Cash'] = 0

for i in range(0,len(data)):
    if i == 0:
        data['Capital_buythedip'][i] = data['Change'][i] * initial_capital
    else:
        if data['Changes_From_Previous_Peak'][i-1] > 0.2:
            data['Capital_buythedip'][i] = (data['Change'][i])*(data['Capital_buythedip'][i-1]+ 
                                                                monthly_topup + data['Cash'][i-1])
            data['Cash'][i] = 0
        else:
            data['Capital_buythedip'][i] = ((data['Change'][i])*data['Capital_buythedip'][i-1]) + monthly_topup/2
            data['Cash'][i] = data['Cash'][i-1] + monthly_topup/2
        
        if data['Close'][i] >= data['All_Time_High'][i]:
            temp = data['Cash'][i]
            data['Cash'][i] = data['Capital_buythedip'][i] + temp
            data['Capital_buythedip'][i] = 0

data['Capital_buythedip'] = data['Capital_buythedip'] + data['Cash']
            
for i in range(0,len(data)):
    if i == 0:
        data['Capital_DCA'][i] = data['Change'][i] * initial_capital
        data['Capital_lumpsum'][i] = data['Change'][i] * lumpsum
        data['Capital'][i] = data['Change'][i] * initial_capital
    else:
        data['Capital_DCA'][i] = ((data['Change'][i])*data['Capital_DCA'][i-1]) + monthly_topup
        data['Capital_lumpsum'][i] = data['Change'][i]*data['Capital_lumpsum'][i-1]
        data['Capital'][i] = data['Capital'][i-1] + monthly_topup

data['Cash'] = 0
for i in range(0,len(data)):
    if i == 0:
        data['Capital_lumpsum_tactical'][i] = data['Change'][i] * lumpsum
    else:
        if data['Changes_From_Previous_Peak'][i-1] > 0.2:
            data['Capital_lumpsum_tactical'][i] = (data['Change'][i])*(data['Capital_lumpsum_tactical'][i-1] \
                                                                       + data['Cash'][i-1])
            data['Cash'][i] = 0
        else:
            data['Capital_lumpsum_tactical'][i] = ((data['Change'][i])*data['Capital_lumpsum_tactical'][i-1])
            data['Cash'][i] = data['Cash'][i-1]
        
        if data['Close'][i] >= data['All_Time_High'][i]:
            temp = data['Cash'][i]
            data['Cash'][i] = data['Capital_lumpsum_tactical'][i] + temp
            data['Capital_lumpsum_tactical'][i] = 0

data['Capital_lumpsum_tactical'] = data['Capital_lumpsum_tactical'] + data['Cash']

ax = data['Capital_DCA'].plot(color = 'g')
data['Capital_buythedip'].plot(ax = ax, color = 'c')
data['Capital_lumpsum'].plot(ax = ax, color = 'k')
data['Capital_lumpsum_tactical'].plot(ax = ax, color = 'darkblue')

ax.set_ylabel('Capital')
plt.legend(['DCA', 'Buy the dip (20%)', 'Lumpsum', 'Lumpsum tactical'], bbox_to_anchor=(1, 1))
plt.show()

再回测

回测结果看起来不错,先试试别的市场数据

def backtest_monthly_buythedip(data, initial_capital, monthly_topup):
    data['Capital'] = 0
    data['Cash'] = 0
    
    for i in range(0,len(data)):
        if i == 0:
            data['Capital'][i] = data['Change'][i] * initial_capital
        else:
            if data['Changes_From_Previous_Peak'][i-1] > 0.2:
                data['Capital'][i] = (data['Change'][i])*(data['Capital'][i-1]+ monthly_topup + data['Cash'][i-1])
                data['Cash'][i] = 0
            else:
                data['Capital'][i] = ((data['Change'][i])*data['Capital'][i-1])
                data['Cash'][i] = data['Cash'][i-1] + monthly_topup

            if data['Close'][i] >= data['Previous_Peak'][i]:
                temp = data['Cash'][i]
                data['Cash'][i] = data['Capital'][i] + temp
                data['Capital'][i] = 0
    #             sell if close >= previous_peak

    data['Capital'] = data['Capital'] + data['Cash']
    
    return data['Capital']

def backtest_lumpsum(data, lumpsum):
    data['Capital'] = 0
    data['Cash'] = 0

    for i in range(0,len(data)):
        if i == 0:
            data['Capital'][i] = data['Change'][i] * lumpsum
        else:
            data['Capital'][i] = data['Change'][i]*data['Capital'][i-1]
            
    return data['Capital']
            
def backtest_DCA(data, initial_capital, monthly_topup):
    data['Capital'] = 0
    data['Cash'] = 0

    for i in range(0,len(data)):
        if i == 0:
            data['Capital'][i] = data['Change'][i] * initial_capital
        else:
            data['Capital'][i] = ((data['Change'][i])*data['Capital'][i-1]) + monthly_topup
            
    return data['Capital']

def backtest_lumpsum_tactical(data, lumpsum):
    data['Capital'] = 0
    data['Cash'] = 0
    
    for i in range(0,len(data)):
        if i == 0:
            data['Capital'][i] = data['Change'][i] * lumpsum
        else:
            if data['Changes_From_Previous_Peak'][i-1] > 0.2:
                data['Capital'][i] = (data['Change'][i])*(data['Capital'][i-1] \
                                                                           + data['Cash'][i-1])
                data['Cash'][i] = 0
            else:
                data['Capital'][i] = ((data['Change'][i])*data['Capital'][i-1])
                data['Cash'][i] = data['Cash'][i-1]

            if data['Close'][i] >= data['Previous_Peak'][i]:
                temp = data['Cash'][i]
                data['Cash'][i] = data['Capital'][i] + temp
                data['Capital'][i] = 0

    data['Capital'] = data['Capital'] + data['Cash']
    
    return data['Capital']
KLSE = read_and_process_data('KLSE.csv')
SP500 = read_and_process_data('MSP500.csv')
CHN = read_and_process_data('MShangHai.csv')
SGX = read_and_process_data('MSGX.csv')

initial_capital = 100
monthly_topup = 100
lumpsum = initial_capital + (len(KLSE)*monthly_topup)
    
KLSE_DCA = backtest_DCA(KLSE.copy(), initial_capital, monthly_topup)
KLSE_20pct = backtest_monthly_buythedip(KLSE.copy(), initial_capital, monthly_topup)
KLSE_lumpsum = backtest_lumpsum(KLSE.copy(), lumpsum)
KLSE_lumpsum_tactical = backtest_lumpsum_tactical(KLSE.copy(), lumpsum)

SP500_DCA = backtest_DCA(SP500.copy(), initial_capital, monthly_topup)
SP500_20pct = backtest_monthly_buythedip(SP500.copy(), initial_capital, monthly_topup)
SP500_lumpsum = backtest_lumpsum(SP500.copy(), lumpsum)
SP500_lumpsum_tactical = backtest_lumpsum_tactical(SP500.copy(), lumpsum)

CHN_DCA = backtest_DCA(CHN.copy(), initial_capital, monthly_topup)
CHN_20pct = backtest_monthly_buythedip(CHN.copy(), initial_capital, monthly_topup)
CHN_lumpsum = backtest_lumpsum(CHN.copy(), lumpsum)
CHN_lumpsum_tactical = backtest_lumpsum_tactical(CHN.copy(), lumpsum)

SGX_DCA = backtest_DCA(SGX.copy(), initial_capital, monthly_topup)
SGX_20pct = backtest_monthly_buythedip(SGX.copy(), initial_capital, monthly_topup)
SGX_lumpsum = backtest_lumpsum(SGX.copy(), lumpsum)
SGX_lumpsum_tactical = backtest_lumpsum_tactical(SGX.copy(), lumpsum)

fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(15,10))

KLSE_DCA.plot(ax = axes[0,0], color = 'g')
KLSE_20pct.plot(ax = axes[0,0], color = 'c')
KLSE_lumpsum.plot(ax = axes[0,0], color = 'k')
KLSE_lumpsum_tactical.plot(ax = axes[0,0], color = 'darkblue')
axes[0,0].set_title("Kuala Lumpur Composite Index")

SP500_DCA.plot(ax = axes[0,1], color = 'g')
SP500_20pct.plot(ax = axes[0,1], color = 'c')
SP500_lumpsum.plot(ax = axes[0,1], color = 'k')
SP500_lumpsum_tactical.plot(ax = axes[0,1], color = 'darkblue')
axes[0,1].set_title("SP500")

CHN_DCA.plot(ax = axes[1,0], color = 'g')
CHN_20pct.plot(ax = axes[1,0], color = 'c')
CHN_lumpsum.plot(ax = axes[1,0], color = 'k')
CHN_lumpsum_tactical.plot(ax = axes[1,0], color = 'darkblue')
axes[1,0].set_title("Shang Hai Composite Index")

SGX_DCA.plot(ax = axes[1,1], color = 'g')
SGX_20pct.plot(ax = axes[1,1], color = 'c')
SGX_lumpsum.plot(ax = axes[1,1], color = 'k')
SGX_lumpsum_tactical.plot(ax = axes[1,1], color = 'darkblue')
axes[1,1].set_title("Straits Times Index (Singapore)")

plt.legend(['DCA', 'Buy the dip (20%)', 'Lumpsum', 'Lumpsum tactical'], bbox_to_anchor=(1, 1))
plt.show()
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])

import numpy as np
x = np.arange(4)
DCA = [KLSE_DCA[-1], SP500_DCA[-1], CHN_DCA[-1], SGX_DCA[-1]]
PCT20 = [KLSE_20pct[-1], SP500_20pct[-1], CHN_20pct[-1], SGX_20pct[-1]]
lumpsum = [KLSE_lumpsum[-1], SP500_lumpsum[-1], CHN_lumpsum[-1], SGX_lumpsum[-1]]
lumpsum_tactical = [KLSE_lumpsum_tactical[-1], SP500_lumpsum_tactical[-1], 
                    CHN_lumpsum_tactical[-1], SGX_lumpsum_tactical[-1]]

width = 0.2
# plot data in grouped manner of bar type
plt.bar(x-0.2, DCA, width, color='cyan')
plt.bar(x, PCT20, width, color='orange')
plt.bar(x+0.2, lumpsum, width, color='green')
plt.bar(x+0.4, lumpsum_tactical, width, color='blue')

plt.xticks(x, ['KLSE', 'SP500', 'CHN', 'SGX'])
plt.xlabel("Assets")
plt.ylabel("Ending Wealth")
plt.legend(['DCA', '20%', 'lumpsum', 'lumpsum tactical'])

plt.show()

结论

  • 没有一个策略可以总是跑赢其他策略,策略的有效性得看市场的”性格”
  • 一次性投入, 在超长牛市是最好的策略
  • 投机或捞底, 在高波动的市场的最好的策略
  • 要是你总是在等捞底,你可能错过牛市
  • 一次性投入需要大量资金,每月定期定额的策略对普通人比较适合

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

Verified by MonsterInsights