【问题标题】:Avoid looping to calculate simple moving average crossing-derived signals避免循环计算简单的移动平均交叉衍生信号
【发布时间】:2018-05-11 15:43:28
【问题描述】:

我想根据简单移动平均线 (SMA) 交叉计算股票的买入和卖出信号。只要 SMA_short 高于 SMA_long(即 SMA_difference > 0),就应该发出买入信号。为了避免仓位被过快卖出,我希望只有在 SMA_short 大幅移动超过交叉点时才发出卖出信号(即 SMA_difference

我通过这个help 来实现它(见下文):

  • 买入和卖出信号由 inout 指示。
  • Position首先考虑buy_limit
  • Position_extended 中,然后为 SMA_short 刚刚穿过 SMA_long 的所有情况设置一个 in (SMA_short SMA_long) 但 SMA_short > -1。为此,它考虑了 i-1 的位置扩展,以防超过一天前交叉但 SMA_short 仍然存在:0 > SMA_short > -1

Python 代码

import pandas as pd
import numpy as np

index = pd.date_range('20180101', periods=6)
df = pd.DataFrame(index=index)
df["SMA_short"] = [9,10,11,10,10,9]
df["SMA_long"] = 10
df["SMA_difference"] = df["SMA_short"] - df["SMA_long"]
buy_limit = 0
sell_limit = -1

df["Position"] = np.where((df["SMA_difference"] > buy_limit),"in","out")
df["Position_extended"] = df["Position"]
for i in range(1,len(df)):
    df.loc[index[i],"Position_extended"] =  \
    np.where((df.loc[index[i], "SMA_difference"] > sell_limit) \
              & (df.loc[index[i-1],"Position_extended"] == "in") \
              ,"in",df.loc[index[i],'Position'])
print df 

结果是:

              SMA_short  SMA_long  SMA_difference Position Position_extended
2018-01-01          9        10              -1      out               out
2018-01-02         10        10               0      out               out
2018-01-03         11        10               1       in                in
2018-01-04         10        10               0      out                in
2018-01-05         10        10               0      out                in
2018-01-06          9        10              -1      out               out

该代码有效,但是,它使用了for 循环,这大大减慢了脚本的速度,并且在此分析的更大范围内变得不适用。由于 SMA 交叉是一种使用率很高的工具,我想知道是否有人可以为此找到更优雅、更快的解决方案。

【问题讨论】:

    标签: python pandas dataframe moving-average


    【解决方案1】:

    本质上,您是在尝试通过传播最后一个非零值来消除矛盾的零条目。类似于零阶保持。您可以这样做,我首先用 NaN 替换零值,然后使用 ffill 对后者进行插值。

    import pandas as pd
    import numpy as np
    
    index = pd.date_range('20180101', periods=6)
    df = pd.DataFrame(index=index)
    df["SMA_short"] = [9,10,11,10,10,9]
    df["SMA_long"] = 10
    df["SMA_difference"] = df["SMA_short"] - df["SMA_long"]
    buy_limit = 0
    sell_limit = -1
    
    df["ZOH"] = df["SMA_difference"].replace(0,np.nan).ffill()
    df["Position"] = np.where((df["ZOH"] > buy_limit),"in","out")
    print df 
    

    结果:

                SMA_short  SMA_long  SMA_difference  ZOH Position
    2018-01-01          9        10              -1 -1.0      out
    2018-01-02         10        10               0 -1.0      out
    2018-01-03         11        10               1  1.0       in
    2018-01-04         10        10               0  1.0       in
    2018-01-05         10        10               0  1.0       in
    2018-01-06          9        10              -1 -1.0      out
    

    【讨论】:

    • 这是完美的;正是我想要的。非常抱歉,我有限的声誉不允许我对你的答案投赞成票,但我为你感到骄傲。
    【解决方案2】:

    如果第 T 行需要在第 T-1 行中计算的值作为输入,那么您可能需要进行迭代计算。通常,回测是通过依次迭代价格数据来完成的。您可以仅根据市场状态计算一些信号,但您不会知道投资组合价值、pnl 或投资组合头寸,除非您从头开始并及时推进。这就是为什么如果您查看像 Quantopian 这样的网站,回测总是从开始日期运行到结束日期。

    【讨论】:

    • DataFrame.shift() 允许您将每个元素与其他所有移位元素进行比较。
    • 对于投反对票,我深表歉意。可以通过两个rolling 计算来确定一个 SMA 是否与另一个 SMA 交叉。可以通过diffcumsum 之类的操作来找到它在每个周期中发生的第一次
    • 我知道您可以计算 SMA 交叉点,但我没有关注您如何跟踪位置以确定他是否希望进入或退出该位置,@AmiTavory。你的解决方案是什么?
    • 完全反驳您的答案需要回答 OP 的问题,我不想这样做(至少现在)。另一方面,否定的回答是不可能的,这会让你而不是我有责任去证实它。如果反对票让您感到困扰,LMK 和我会撤销它(即使我不同意您的观点)。
    • 所以我推翻了反对意见,但我不同意你的观点,FWIW。无论如何,一切顺利。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-23
    • 2016-03-20
    • 2012-10-04
    • 1970-01-01
    • 2011-03-07
    • 1970-01-01
    • 2021-07-17
    相关资源
    最近更新 更多