[复现]比较6种股市趋势识别方

作者: Filippos Tzimopoulos

翻译整理:进击的小学生

文章名称: I compared 6 methods to identify the trend of the stock market. These are the results!

code 与 datas ,请转至获取!

背景说明

通过识别市场方向,交易者可以就进入和退出点做出明智的决策,改进整体策略并避免在波动条件下出现代价高昂的错误。

在本文中,您可以期待以下内容:

  • 描述最常用的技术分析方法,使用指标来识别趋势;主要指标有:MA(快速、慢速移动平均值)、MACD、RSI、布林带、ADX、Ichimoku Cloud。
  • 对于每种方法,使用趋势作为信号,并计算相关绩效指标,主要有:总收益、最大回撤、Sharp、权益曲线;
  • 比较不同方法的各种统计数据;
  • 根据回测数据绘制趋势图;

在原文基础上为更好理解,做以下调整:

  • 作者使用SPY作为标的,考虑更加贴近自身环境,便于理解和对比,标的该为ETF 516510.SH。
  • 向上趋势:green 改为 red;向下趋势:red 改为 green

运行环境

Author: lleasy

Python implementation: CPython
Python version       : 3.10.9
IPython version      : 8.10.0

numpy     : 1.26.4
pandas    : 2.2.1
matplotlib: 3.8.4
pandas_ta : 0.3.14b0

Compiler    : Clang 14.0.6 
OS          : Darwin
Release     : 22.6.0
Machine     : x86_64
Processor   : i386
CPU cores   : 16
Architecture: 64bit

指标介绍

权益曲线(Equity Curve)和净值曲线(Net Value Curve)

权益曲线(Equity Curve)和净值曲线(Net Value Curve)通常用于描述投资组合或基金的价值随时间的变化,但它们在计算和表示上有一些区别:

  1. 权益曲线(Equity Curve)

    • 权益曲线通常从100开始(或者从投资的初始资金开始)。
    • 它反映了投资组合的累积价值,考虑到了所有的收益和损失。
    • 权益曲线的每个点是通过将起始价值乘以(1加上累积的回报率)来计算的。
    • 它通常用于展示交易策略的表现,特别是在量化交易和对冲基金中。
  2. 净值曲线(Net Value Curve)

    • 净值曲线通常从1开始(或者从投资的初始资金除以初始资金额来归一化)。
    • 它也反映了投资组合的累积价值,但通常用于基金净值的计算。
    • 净值曲线的每个点是通过将起始价值乘以(1加上累积的回报率)并考虑了资金的流入和流出(如投资者的申购和赎回)来计算的。
    • 它常用于衡量共同基金或投资基金的表现。
计算方法的区别
  • 权益曲线的计算方法通常是:
    Equity t = Equity t − 1 × ( 1 + R t ) \text{Equity}_{t} = \text{Equity}_{t-1} \times (1 + R_t) Equityt=Equityt1×(1+Rt)
    其中, E q u i t y t {Equity}_{t} Equityt 是第 $ t $ 天的权益,$ R_t $ 是第 $ t $ 天的回报率。

  • 净值曲线的计算方法通常是:
    Net Value t = Net Value t − 1 × ( 1 + R t ) × ( 1 + Cash Flow t ) \text{Net Value}_{t} = \text{Net Value}_{t-1} \times (1 + R_t) \times (1 + \text{Cash Flow}_{t}) Net Valuet=Net Valuet1×(1+Rt)×(1+Cash Flowt)
    其中, Net Value t \text{Net Value}_{t} Net Valuet 是第 t t t 天的净值, Cash Flow t \text{Cash Flow}_{t} Cash Flowt 是第 t t t 天的资金流入流出比例。

应用场景的区别
  • 权益曲线更多地用于个人投资者或交易者,以及那些关注交易策略表现的场景。
  • 净值曲线则更多地用于基金经理和基金公司,以及那些需要向投资者报告基金表现的场景。

总的来说,两者都是衡量投资表现的工具,但权益曲线更侧重于策略的累积表现,而净值曲线则更侧重于基金的整体表现,包括了资金的流入流出。

一目均衡图(Ichimoku Cloud)

一目均衡图又称云图指标、日平均图(Ichimoku Cloud)是一种技术分析方法,它将多个指标组合在一张图表中。它在烛台图表上作为一种交易工具,为交易者提供有关潜在支撑位和阻力位区域的参考。它也被用作预测工具,帮助交易者确定未来趋势和市场动力。

Ichimoku Cloud系统根据高低价格指标显示数据,图表由五组参数组成:

  1. 转换线(Tenkan-sen):以9日为移动平均线。
  2. 基线(Kijun-sen):以26日为移动平均线。
  3. 先行带A(Senkou Span A):通过转换线和基线的移动平均值预计未来26日内趋势
  4. 先行带B(Senkou Span B):通过52日移动平均值预计未来26日内趋势。
  5. 迟行带(Chikou Span):今日收盘价与过去26日中线的差值。

Ichimoku Cloud

先行带A(3)和先行带B(4)之间的空间称为云带(Kumo),该参数是Ichimoku系统中最值得注意的元素。两条先行带能够预计26日内的市场趋势,因此被视为先行指标。另一方面,迟行带(5)是一个滞后指标,体现了过去26日的趋势。

默认情况下,为方便阅读云带以绿色或红色显示。当先行带A(绿色云线)高于先行带B(红色云线)时,会产生绿色云带。同理,完全相反的情况下会产生红色云带。

一目均衡图(Ichimoku Cloud)详解

# Download the stock data
ticker = '516510.SH' 
#df = yf.download(ticker, start='2023-01-01', end='2024-06-01')
df = pd.read_csv('./datas/516510.SH.csv',parse_dates=['datetime'])
df = df[['ts_code','datetime','open','high','low','close','vol']]
df = df.set_index('datetime')
df = df.query('datetime<=20230101').copy()
def calculate_returns(df_for_returns, col_for_returns = 'close', col_for_signal = 'Trend'):

    stats = {}

    # Calculate daily returns
    df_for_returns['Daily_Returns'] = df[col_for_returns].pct_change()
    df_for_returns['Returns'] = df_for_returns['Daily_Returns'] * df_for_returns[col_for_signal].shift(1)
    df_for_returns['Returns'] = df_for_returns['Returns'].fillna(0)
    # 股票或投资组合的权益曲线(Equity Curve)是投资领域中用来展示投资组合价值随时间变化的图表,通常用来评估投资策略的表现。
    df_for_returns['Equity Curve'] = 100 * (1 + df_for_returns['Returns']).cumprod()

    equity_curve = df_for_returns['Equity Curve']
    # Calculate the running maximum of the equity curve
    cumulative_max = equity_curve.cummax()
    drawdown = (equity_curve - cumulative_max) / cumulative_max
    stats['max_drawdown'] = drawdown.min()

    # calculate the sharpe ratio
    stats['sharpe_ratio'] = (df_for_returns['Returns'].mean() / df_for_returns['Returns'].std()) * np.sqrt(252)

    # calculate the total return
    stats['total_return'] = (equity_curve.iloc[-1] / equity_curve.iloc[0]) - 1

    # calculate the number of long signals
    stats['number_of_long_signals'] = len(df_for_returns[df_for_returns[col_for_signal] == 1])

    # calculate the number of short signals
    stats['number_of_short_signals'] = len(df_for_returns[df_for_returns[col_for_signal] == -1])

    return df_for_returns['Equity Curve'], stats

策略回测

快速 + 慢速 移动平均线MA

2 个移动平均线。当快速值高于慢值时,表明呈上升趋势。

def calculate_trend_2_ma(df_ohlc, period_slow=21, period_fast=9):
    # Calculate Moving Averages (fast and slow) using pandas_ta
    df_ohlc['MA_Fast'] = df_ohlc.ta.sma(close='Close', length=period_fast)
    df_ohlc['MA_Slow'] = df_ohlc.ta.sma(close='Close', length=period_slow)

    # Determine the trend based on Moving Averages
    def identify_trend(row):
        if row['MA_Fast'] > row['MA_Slow']:
            return 1
        elif row['MA_Fast'] < row['MA_Slow']:
            return -1
        else:
            return 0
        
    df_ohlc = df_ohlc.assign(Trend=df_ohlc.apply(identify_trend, axis=1))
    df_ohlc['Trend'] =  df_ohlc['Trend'].fillna('0')

    return df_ohlc['Trend']

df['Trend'] = calculate_trend_2_ma(df, period_slow=21, period_fast=9)
df['Equity Curve'], stats = calculate_returns(df, col_for_returns = 'close', col_for_signal = 'Trend')

# Plotting with adjusted subplot heights
fig, ax1 = plt.subplots(1, 1, figsize=(14, 7), sharex=True)

# Plotting the close price with the color corresponding to the trend
# 当快速值高于慢值时,表明呈上升趋势。
for i in range(1, len(df)):
    ax1.plot(df.index[i-1:i+1]
            , df['close'].iloc[i-1:i+1]
            #, color='red' if df['Trend'].iloc[i] == 1 else ('green' if df['Trend'].iloc[i] == -1 else 'darkgrey')
            , color='green' if df['Trend'].iloc[i] == 1 else ('red' if df['Trend'].iloc[i] == -1 else 'darkgrey')
            , linewidth=2)

# Plot the Moving Averages
ax1.plot(df['MA_Fast'], label='9-day MA (Fast)', color='blue')
ax1.plot(df['MA_Slow'], label='21-day MA (Slow)', color='orange')
ax1.set_title(f'{ticker} - Price and Moving Averages')
ax1.text(0.5, 0.9, f"Total Return: {stats['total_return']:.2%}", transform=ax1.transAxes, ha='center', va='top', fontsize=14)
ax1.legend(loc='best')

plt.show()

png

MA + MACD

上升趋势,收盘价应高于移动平均线,MACD 线应高于 MACD 信号。

def calculate_trend_macd_ma(df_ohlc, ma_period=50, macd_fast=12, macd_slow=26, macd_signal=9):
    # Calculate MACD using pandas_ta
    df_ohlc.ta.macd(close='close', fast=macd_fast, slow=macd_slow, signal=macd_signal, append=True)

    # Calculate Moving Average
    df_ohlc['MA'] = df_ohlc.ta.sma(close='close', length=ma_period)

    # Determine the trend based on MA and MACD
    def identify_trend(row):
        macd_name = f'{macd_fast}_{macd_slow}_{macd_signal}'
        # 收盘价应高于移动平均线,MACD 线应高于 MACD 信号。
        if row['close'] > row['MA'] and row[f'MACD_{macd_name}'] > row[f'MACDs_{macd_name}']:
            return 1
        elif row['close'] < row['MA'] and row[f'MACD_{macd_name}'] < row[f'MACDs_{macd_name}']:
            return -1
        else:
            return 0

    df_ohlc['Trend'] = df_ohlc.apply(identify_trend, axis=1)
    return df_ohlc['Trend']

df['Trend'] = calculate_trend_macd_ma(df, ma_period=50, macd_fast=12, macd_slow=26, macd_signal=9)
df['Equity Curve'], stats = calculate_returns(df, col_for_returns = 'close', col_for_signal = 'Trend')

# Plotting with adjusted subplot heights
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), sharex=True, 
                            gridspec_kw={'height_ratios': [3, 1]})

# Plotting the close price with the color corresponding to the trend
for i in range(1, len(df)):
    ax1.plot(df.index[i-1:i+1]
            , df['close'].iloc[i-1:i+1]
            , color='red' if df['Trend'].iloc[i] == 1 else ('green' if df['Trend'].iloc[i] == -1 else 'darkgrey')
            , linewidth=2)

# Plot the Moving Average
ax1.plot(df['MA'], label=f'50-day MA', color='orange')
ax1.set_title(f'{ticker} - Price and Moving Average')
ax1.text(0.5, 0.9, f"Total Return: {stats['total_return']:.2%}", transform=ax1.transAxes, ha='center', va='top', fontsize=14)
ax1.legend(loc='best')

# Plot MACD and Signal Line on the second subplot (smaller height)
ax2.plot(df.index, df['MACD_12_26_9'], label='MACD', color='blue')
ax2.plot(df.index, df['MACDs_12_26_9'], label='Signal Line', color='red')
ax2.set_title(f'{ticker} - MACD')
ax2.legend(loc='best')

plt.show()

png

RSI + 快速和慢速移动平均线

快速 MA 应高于慢速 MA,RSI > 50 以确定上升趋势和下降趋势的相反趋势。

def calculate_trend_rsi_ma(df_ohlc, rsi_period=14, ma_fast=9, ma_slow=21):

    # Calculate RSI using pandas_ta
    df_ohlc['RSI'] = df.ta.rsi(close='close', length=rsi_period)

    # Calculate Moving Averages (14-day and 50-day) using pandas_ta
    df_ohlc[f'MA_{ma_fast}'] = df_ohlc.ta.sma(close='close', length=14)
    df_ohlc[f'MA_{ma_slow}'] = df_ohlc.ta.sma(close='close', length=50)

    # Determine the trend based on RSI and Moving Averages
    # 快速 MA 应高于慢速 MA,RSI > 50 以确定上升趋势
    def identify_trend(row):
        if row['RSI'] > 50 and row[f'MA_{ma_fast}'] > row[f'MA_{ma_slow}']:
            return 1
        elif row['RSI'] < 50 and row[f'MA_{ma_fast}'] < row[f'MA_{ma_slow}']:
            return -1
        else:
            return 0

    df_ohlc['Trend'] = df_ohlc.apply(identify_trend, axis=1)
    return df_ohlc['Trend']

df['Trend'] = calculate_trend_rsi_ma(df, rsi_period=14, ma_fast=14, ma_slow=50)
df['Equity Curve'], stats = calculate_returns(df, col_for_returns = 'close', col_for_signal = 'Trend')

# Plotting with adjusted subplot heights
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), sharex=True, 
                            gridspec_kw={'height_ratios': [3, 1]})

# Plotting the close price with the color corresponding to the trend
for i in range(1, len(df)):
    ax1.plot(df.index[i-1:i+1]
            , df['close'].iloc[i-1:i+1]
            , color='red' if df['Trend'].iloc[i] == 1 else ('green' if df['Trend'].iloc[i] == -1 else 'darkgrey')
            , linewidth=2)

# Plot the Moving Averages
ax1.plot(df['MA_14'], label='14-day MA', color='blue')
ax1.plot(df['MA_50'], label='50-day MA', color='orange')
ax1.text(0.5, 0.9, f"Total Return: {stats['total_return']:.2%}", transform=ax1.transAxes, ha='center', va='top', fontsize=14)
ax1.set_title(f'{ticker} - Price, RSI and Fast and Slow Moving Average')
ax1.legend(loc='best')

# Plot RSI on the second subplot (smaller height)
ax2.plot(df.index, df['RSI'], label='RSI', color='purple')
ax2.axhline(50, color='black', linestyle='--', linewidth=1)  # Add a horizontal line at RSI=50
ax2.set_title(f'{ticker} - RSI')
ax2.legend(loc='best')

plt.show()

png

RSI + 布林带

当价格高于中间布林带且 RSI 高于 50 时,我们有一个上升趋势。

def calculate_trend_bbands_rsi(df_ohlc, bbands_period=5, bbands_std=2, rsi_period=14):

    # Calculate RSI using pandas_ta
    df_ohlc['RSI'] = df_ohlc.ta.rsi(close='close', length=rsi_period)

    # Calculate Bollinger Bands using pandas_ta
    bbands = df.ta.bbands(close='close', length=bbands_period, std=bbands_std)
    df_ohlc['BB_upper'] = bbands[f'BBU_{bbands_period}_{bbands_std}.0']
    df_ohlc['BB_middle'] = bbands[f'BBM_{bbands_period}_{bbands_std}.0']
    df_ohlc['BB_lower'] = bbands[f'BBL_{bbands_period}_{bbands_std}.0']

    # Determine the trend based on Bollinger Bands and RSI
    # 当价格高于中间布林带且 RSI 高于 50 时,上升趋势。
    def identify_trend(row):
        if row['close'] > row['BB_middle'] and row['RSI'] > 50:
            return 1
        elif row['close'] < row['BB_middle'] and row['RSI'] < 50:
            return -1
        else:
            return 0

    df_ohlc['Trend'] = df_ohlc.apply(identify_trend, axis=1)
    return df_ohlc['Trend']

df['Trend'] = calculate_trend_bbands_rsi(df, bbands_period=20, bbands_std=2, rsi_period=14)
df['Equity Curve'], stats = calculate_returns(df, col_for_returns = 'close', col_for_signal = 'Trend')

# Plotting with adjusted subplot heights
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), sharex=True, 
                            gridspec_kw={'height_ratios': [3, 1]})

# Plotting the close price with the color corresponding to the trend
for i in range(1, len(df)):
    ax1.plot(df.index[i-1:i+1]
            , df['close'].iloc[i-1:i+1]
            , color='red' if df['Trend'].iloc[i] == 1 else ('green' if df['Trend'].iloc[i] == -1 else 'darkgrey')
            , linewidth=2)

# Plot Bollinger Bands
ax1.plot(df['BB_upper'], label='Upper Band', color='blue', linestyle='--')
ax1.plot(df['BB_middle'], label='Middle Band', color='orange')
ax1.plot(df['BB_lower'], label='Lower Band', color='blue', linestyle='--')
ax1.text(0.5, 0.9, f"Total Return: {stats['total_return']:.2%}", transform=ax1.transAxes, ha='center', va='top', fontsize=14)
ax1.set_title(f'{ticker} - Price, RSI and Bollinger Bands')
ax1.legend(loc='best')

# Plot RSI on the second subplot (smaller height)
ax2.plot(df.index, df['RSI'], label='RSI', color='purple')
ax2.axhline(50, color='black', linestyle='--', linewidth=1)  # Add a horizontal line at RSI=50
ax2.set_title(f'{ticker} - RSI')
ax2.legend(loc='best')

plt.show()


png

ADX + 慢速和快速移动平均线

通过结合 ADX 和移动平均线,当 ADX 高于 25(表明强劲趋势)且快速 MA 高于慢速 MA 时,我们将确定上升趋势。

def calculate_trend_adx_ma(df_ohlc, adx_period=14, fast_ma_period=14, slow_ma_period=50):
    # Calculate ADX using pandas_ta
    df_ohlc['ADX'] = df_ohlc.ta.adx(length=adx_period)[f'ADX_{adx_period}']

    # Calculate Moving Averages (14-day and 50-day) using pandas_ta
    df_ohlc['MA_fast'] = df_ohlc.ta.sma(close='close', length=fast_ma_period)
    df_ohlc['MA_slow'] = df_ohlc.ta.sma(close='close', length=slow_ma_period)

    # Determine the trend based on ADX and Moving Averages
    # 当 ADX 高于 25(表明强劲趋势)且快速 MA 高于慢速 MA 时,上升趋势
    def identify_trend(row):
        if row['ADX'] > 25 and row['MA_fast'] > row['MA_slow']:
            return 1
        elif row['ADX'] > 25 and row['MA_fast'] < row['MA_slow']:
            return -1
        else:
            return 0

    df_ohlc['Trend'] = df_ohlc.apply(identify_trend, axis=1)
    return df_ohlc['Trend']

df['Trend'] = calculate_trend_adx_ma(df, adx_period=14, fast_ma_period=14, slow_ma_period=50)
df['Equity Curve'], stats = calculate_returns(df, col_for_returns = 'close', col_for_signal = 'Trend')

# Plotting with adjusted subplot heights
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), sharex=True, 
                            gridspec_kw={'height_ratios': [3, 1]})

# Plotting the close price with the color corresponding to the trend
for i in range(1, len(df)):
    ax1.plot(df.index[i-1:i+1]
            , df['close'].iloc[i-1:i+1]
            , color='red' if df['Trend'].iloc[i] == 1 else ('green' if df['Trend'].iloc[i] == -1 else 'darkgrey')
            , linewidth=2)

# Plot the Moving Averages
ax1.plot(df['MA_fast'], label='Fast MA', color='blue')
ax1.plot(df['MA_slow'], label='Slow MA', color='orange')
ax1.text(0.5, 0.9, f"Total Return: {stats['total_return']:.2%}", transform=ax1.transAxes, ha='center', va='top', fontsize=14)
ax1.set_title(f'{ticker} - Price, ADX and Moving Averages')
ax1.legend(loc='best')

# Plot ADX on the second subplot (smaller height)
ax2.plot(df.index, df['ADX'], label='ADX', color='purple')
ax2.axhline(25, color='black', linestyle='--', linewidth=1)  # Add a horizontal line at ADX=25
ax2.set_title(f'{ticker} - ADX')
ax2.legend(loc='best')

plt.show()


png

Ichimoku Cloud + MACD

当价格高于 Ichimoku Cloud 且 MACD 高于信号线时,我们确定上升趋势。

def calculate_trend_ichimoku_macd(df_ohlc, macd_fast=12, macd_slow=26, macd_signal=9, tenkan=9, kijun=26, senkou=52):

    # Calculate Ichimoku Cloud components using pandas_ta
    df_ichimoku = df_ohlc.ta.ichimoku(tenkan, kijun, senkou)[0]

    # Extract Ichimoku Cloud components
    df_ohlc['Ichimoku_Conversion'] = df_ichimoku[f'ITS_{tenkan}']  # Tenkan-sen (Conversion Line)
    df_ohlc['Ichimoku_Base'] = df_ichimoku[f'IKS_{kijun}']       # Kijun-sen (Base Line)
    df_ohlc['Ichimoku_Span_A'] = df_ichimoku[f'ITS_{tenkan}']         # Senkou Span A
    df_ohlc['Ichimoku_Span_B'] = df_ichimoku[f'ISB_{kijun}']        # Senkou Span B

    # Calculate MACD using pandas_ta
    df_ohlc.ta.macd(close='close', fast=macd_fast, slow=macd_slow, signal=macd_signal, append=True)

    # Determine the trend based on Ichimoku Cloud and MACD
    # 当价格高于 Ichimoku Cloud 且 MACD 高于信号线时,我们确定上升趋势。
    def identify_trend(row):
        if row['close'] > max(row['Ichimoku_Span_A'], row['Ichimoku_Span_B']) and row['MACD_12_26_9'] > row['MACDs_12_26_9']:
            return 1
        elif row['close'] < min(row['Ichimoku_Span_A'], row['Ichimoku_Span_B']) and row['MACD_12_26_9'] < row['MACDs_12_26_9']:
            return -1
        else:
            return 0

    df_ohlc['Trend'] = df_ohlc.apply(identify_trend, axis=1)
    return df_ohlc['Trend']

df['Trend'] = calculate_trend_ichimoku_macd(df, macd_fast=12, macd_slow=26, macd_signal=9)
df['Equity Curve'], stats = calculate_returns(df, col_for_returns = 'close', col_for_signal = 'Trend')

# Plotting with adjusted subplot heights
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), sharex=True, 
                            gridspec_kw={'height_ratios': [3, 1]})

# Plotting the close price with the color corresponding to the trend
for i in range(1, len(df)):
    ax1.plot(df.index[i-1:i+1]
            , df['close'].iloc[i-1:i+1]
            , color='red' if df['Trend'].iloc[i] == 1 else ('green' if df['Trend'].iloc[i] == -1 else 'darkgrey')
            , linewidth=2)

# Plot Ichimoku Cloud
ax1.fill_between(df.index, df['Ichimoku_Span_A'], df['Ichimoku_Span_B'], 
                where=(df['Ichimoku_Span_A'] >= df['Ichimoku_Span_B']), color='lightgreen', alpha=0.5)
ax1.fill_between(df.index, df['Ichimoku_Span_A'], df['Ichimoku_Span_B'], 
                where=(df['Ichimoku_Span_A'] < df['Ichimoku_Span_B']), color='lightcoral', alpha=0.5)

ax1.plot(df['Ichimoku_Conversion'], label='Conversion Line (Tenkan-sen)', color='blue')
ax1.plot(df['Ichimoku_Base'], label='Base Line (Kijun-sen)', color='orange')
ax1.text(0.5, 0.9, f"Total Return: {stats['total_return']:.2%}", transform=ax1.transAxes, ha='center', va='top', fontsize=14)
ax1.set_title(f'{ticker} - Price and Ichimoku Cloud')
ax1.legend(loc='best')

# Plot MACD and Signal Line on the second subplot (smaller height)
ax2.plot(df.index, df['MACD_12_26_9'], label='MACD', color='blue')
ax2.plot(df.index, df['MACDs_12_26_9'], label='Signal Line', color='red')
ax2.set_title(f'{ticker} - MACD')
ax2.legend(loc='best')

plt.show()

png

结果总结

最后,对以上两两组合指标回测结果进行汇总比较:

# Download the stock data
#ticker = 'SPY'  # You can replace 'AAPL' with any other stock ticker or currency pair
#df = yf.download(ticker, start='2023-01-01', end='2024-06-01')

trend_identification_methods = ['2_ma', 'macd_ma', 'rsi_ma', 'bbands_rsi', 'adx_ma',  'ichimoku_macd']
trend_identification_results = []

def calculate_trend(df, method):
    if method == '2_ma':
        return calculate_trend_2_ma(df, period_slow=21, period_fast=9)
    elif method == 'macd_ma':
        return calculate_trend_macd_ma(df, ma_period=50, macd_fast=12, macd_slow=26, macd_signal=9)
    elif method == 'rsi_ma':
        return calculate_trend_rsi_ma(df, rsi_period=14, ma_fast=9, ma_slow=21)
    elif method == 'bbands_rsi':
        return calculate_trend_bbands_rsi(df, bbands_period=5, bbands_std=2, rsi_period=14)
    elif method == 'adx_ma':
        return calculate_trend_adx_ma(df, adx_period=14, fast_ma_period=14, slow_ma_period=50)
    elif method == 'ichimoku_macd':
        return calculate_trend_ichimoku_macd(df, macd_fast=12, macd_slow=26, macd_signal=9, tenkan=9, kijun=26, senkou=52)

for method in trend_identification_methods:

    # Calculate results of returns for each method and append to the list
    df_copy = df.copy()
    d = {}
    d['Method'] = method
    df_copy['Trend'] = calculate_trend(df_copy, method)
    df_copy['Equity Curve'], stats = calculate_returns(df_copy, col_for_returns = 'close', col_for_signal = 'Trend')
    d.update(stats)
    trend_identification_results.append(d)

    # Add trend line and equity curve to the df
    df[f'Trend_{method}'] = df_copy['Trend']
    df[f'Equity Curve_{method}'] = df_copy['Equity Curve']


trend_identification_results_df = pd.DataFrame(trend_identification_results)

trend_identification_results_df
Methodmax_drawdownsharpe_ratiototal_returnnumber_of_long_signalsnumber_of_short_signals
02_ma-0.2205140.2791250.067435217187
1macd_ma-0.1977660.2585730.054929100121
2rsi_ma-0.2656110.049457-0.023366141129
3bbands_rsi-0.3021480.2803530.065393169128
4adx_ma-0.1302350.5310430.1284816738
5ichimoku_macd-0.2307520.1365600.01265510382

在该标的下,ADX快速和慢速移动平均线的组合下 sharpe 最大 、 max_drawdown 最小,收益率也高于其他组合。

多股票回测

tickers = ['AAPL', 'AMZN', 'GOOG', 'TSLA', 'SPY']
results = []

for ticker in tickers:
    df = yf.download(ticker, start='2023-01-01', end='2024-06-01')

    trend_identification_methods = ['2_ma', 'macd_ma', 'rsi_ma', 'bbands_rsi', 'adx_ma',  'ichimoku_macd']
    trend_identification_results = []

    def calculate_trend(df, method):
        if method == '2_ma':
            return calculate_trend_2_ma(df, period_slow=21, period_fast=9)
        elif method == 'macd_ma':
            return calculate_trend_macd_ma(df, ma_period=50, macd_fast=12, macd_slow=26, macd_signal=9)
        elif method == 'rsi_ma':
            return calculate_trend_rsi_ma(df, rsi_period=14, ma_fast=9, ma_slow=21)
        elif method == 'bbands_rsi':
            return calculate_trend_bbands_rsi(df, bbands_period=5, bbands_std=2, rsi_period=14)
        elif method == 'adx_ma':
            return calculate_trend_adx_ma(df, adx_period=14, fast_ma_period=14, slow_ma_period=50)
        elif method == 'ichimoku_macd':
            return calculate_trend_ichimoku_macd(df, macd_fast=12, macd_slow=26, macd_signal=9, tenkan=9, kijun=26, senkou=52)


    for method in trend_identification_methods:

        # Calculate results of returns for each method and append to the list
        df_copy = df.copy()
        d = {}
        d['Method'] = method
        df_copy['Trend'] = calculate_trend(df_copy, method)
        df_copy['Equity Curve'], stats = calculate_returns(df_copy, col_for_returns = 'Close', col_for_signal = 'Trend')
        results.append({'ticker':ticker, 'method':method, 'total_return':stats['total_return']})

test_df = pd.DataFrame(results)

# Pivot the DataFrame to prepare for plotting
pivot_df = test_df.pivot(index='ticker', columns='method', values='total_return')

# Plotting
pivot_df.plot(kind='bar', figsize=(12, 8))

plt.title('Comparison of total returns per trend method')
plt.ylabel('Value')
plt.xlabel('Stock')
plt.legend(title='Indicators', bbox_to_anchor=(1.05, 1), loc='upper left')
plt.xticks(rotation=45)

plt.tight_layout()
plt.show()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/896226.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

webAPI中的排他思想、自定义属性操作、节点操作(配大量案例练习)

一、排他操作 1.排他思想 如果有同一组元素&#xff0c;我们想要某一个元素实现某种样式&#xff0c;需要用到循环的排他思想算法&#xff1a; 1.所有的元素全部清除样式 2.给当前的元素设置样式 注意顺序能不能颠倒&#xff0c;首先清除全部样式&#xff0c;再设置自己当前的…

dyna批处理代码,无需蹲守,自行连续计算

用此代码即可 上面一行是模型位置 下面一行是DYNA模拟器位置 第三个框框是K文件名称 上述三者改成自己的 然后复制修改即可 复制几个就几个进行批处理

深入理解伪元素与伪类元素

在“探秘盒子浮动&#xff0c;破解高度塌陷与文字环绕难题&#xff0c;清除浮动成关键&#xff01;”中&#xff0c;我们讲到如果父盒由于各种原因未设置高度&#xff0c; 子盒的浮动会导致父盒的高度塌陷。为了解决高度塌陷的问题&#xff0c;我们可以添加伪元素。 一、伪元素…

Android 内存优化——常见内存泄露及优化方案

看到了一篇关于内存泄漏的文章后&#xff0c;就想着分享给大家&#xff0c;最后一起学习&#xff0c;一起进步&#xff1a; 如果一个无用对象&#xff08;不需要再使用的对象&#xff09;仍然被其他对象持有引用&#xff0c;造成该对象无法被系统回收&#xff0c;以致该对象在…

【element-tiptap】如何修改选中内容时的背景颜色?

前言&#xff1a;element-tiptap 用鼠标选中内容的时候&#xff0c;背景颜色跟系统设置的主题有关&#xff0c;比如的我的就是卡哇伊的pink&#xff0c;默认是淡蓝色 但是我们观察一下语雀&#xff0c;背景颜色是它规定好的颜色 这篇文章来探索一下&#xff0c;怎么自己规定选…

教学平台的智能化升级:Spring Boot应用

4系统概要设计 4.1概述 本系统采用B/S结构(Browser/Server,浏览器/服务器结构)和基于Web服务两种模式&#xff0c;是一个适用于Internet环境下的模型结构。只要用户能连上Internet,便可以在任何时间、任何地点使用。系统工作原理图如图4-1所示&#xff1a; 图4-1系统工作原理…

除GOF23种设计模式之简单工厂模式

文章目录 1. 简介2. 代码2.1 抽象类&#xff1a;Course.java2.2 产品A:JavaCourse.java2.3 产品B:PythonCourse.java2.4 工厂:CourseFactory.java2.5 测试&#xff1a;Test.java 3. 心得参考链接&#xff08;无&#xff09; 1. 简介 简单工厂模式(Simple Factory Patern):又称…

数据结构4——栈

1. 栈的概念及结构 栈的概念&#xff1a; 栈是一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last In First Out&#xff09;的原则…

【SuperHotSwap】IDEA零配置热更新插件升级

往期往期插件v1.0.0发布的时候我发表了一篇文章&#xff0c;如下&#xff1a; 支持功能 如今插件迭代了数个版本&#xff0c;现在迎来了v1.9.0版本的重大升级。如下是支持功能。 支持功能是否支持说明MybatisXML热更新√Class热更新√增强功能需安装dcevm补丁。支持动态新增类…

git rebase的常用场景: 交互式变基, 变基和本地分支基于远端分支的变基

文章目录 作用应用场景场景一&#xff1a;交互式变基(合并同一条线上的提交记录) —— git rebase -i HEAD~2场景二&#xff1a;变基(合并分支) —— git rebase [其他分支名称]场景三&#xff1a;本地分支与远端分支的变基 作用 使git的提交记录变得更加简洁 应用场景 场景…

Java爬虫:获取数据的入门详解

在数字化时代&#xff0c;数据已成为最宝贵的资产之一。无论是市场研究、客户洞察还是产品开发&#xff0c;获取大量数据并从中提取有价值的信息变得至关重要。Java&#xff0c;作为一种成熟且功能强大的编程语言&#xff0c;为编写爬虫提供了强大的支持。Java爬虫可以帮助我们…

如何提高外贸网站在谷歌的收录速度?

外贸企业在进行网络推广时&#xff0c;经常遇到网站页面无法被谷歌快速收录的问题。即使你的网站内容优质、设计精美&#xff0c;如果没有被谷歌收录&#xff0c;就等于失去了被客户发现的机会&#xff0c;GSI谷歌快速收录服务就是为了解决这一问题而诞生的。它不仅能够帮助网站…

5G智慧医疗的实践先锋:SR830-E工业路由器的理性应用

在医疗科技日新月异的今天&#xff0c;5G技术无疑为智慧医疗注入了新的活力。然而&#xff0c;技术的进步不应仅停留在理论层面&#xff0c;更应该在实践中发挥其真正价值。今天&#xff0c;我们就来探讨SR830-E工业路由器如何在实际医疗场景中扮演关键角色&#xff0c;推动5G智…

vscode 远程linux服务器 连接git

vscode 远程linux服务器 连接git 1. git 下载2. git 配置1&#xff09;github 设置2&#xff09;与github建立连接linux端&#xff1a;创建密钥github端&#xff1a;创建ssh key 3. 使用1&#xff09;初始化repository2&#xff09;commit 输入本次提交信息&#xff0c;提交到本…

UE5 圆周运动、贝塞尔曲线运动、贝塞尔曲线点

圆周运动 贝塞尔曲线路径运动 蓝图函数库创建贝塞尔曲线点 // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include "Kismet/BlueprintFunctionLibrary.h" #include "MyBlu…

从MySQL到OceanBase离线数据迁移的实践

本文作者&#xff1a;玉璁&#xff0c;OceanBase 生态产品技术专家。工作十余年&#xff0c;一直在基础架构与中间件领域从事研发工作。现负责OceanBase离线导数产品工具的研发工作&#xff0c;致力于为 OceanBase 建设一套完善的生态工具体系。 背景介绍 在互联网与云数据库技…

【码农必备】CasaOS香橙派安装Code server+cpolar让远程开发更轻松

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

基于Spring Boot、Vue和MyBatis的前后端分离座位管理系统:增删改查功能入门指南

在项目开发和工作实践中&#xff0c;创作灵感往往来自于对日常经验的总结与反思。通过记录技术难点和解决方案&#xff0c;不仅可以加深对问题的理解&#xff0c;还能为后续项目的优化提供参考。与此同时&#xff0c;撰写技术笔记、分享职场心得&#xff0c;不仅是对自己成长的…

一款基于 Vue 3 的现代化数据可视化组件库,功能强大,颜值爆表,开发者必备!(带私活源码)

Vue Data UI 是一款基于 Vue 3 的现代化数据可视化组件库&#xff0c;专为开发者提供强大的数据展示功能&#xff0c;旨在帮助用户通过图形化手段生动地讲述数据故事。该库由开源开发者 Graphieros 创建和维护&#xff0c;专注于提升图形渲染性能与交互体验&#xff0c;并致力于…

基于SSM汽车零部件加工系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;员工管理&#xff0c;经理管理&#xff0c;零件材料管理&#xff0c;产品类型管理&#xff0c;产品信息管理&#xff0c;产品出库管理&#xff0c;产品入库管理 员工账号功能包括&#xff1a;系统首页…