【问题标题】:How to create a simple flag in Python如何在 Python 中创建一个简单的标志
【发布时间】:2019-07-25 12:54:42
【问题描述】:

从雅虎获得 SPY 数据后,我创建了一个收盘价通道,如下所示,最大和最小滚动窗口。列是 HC 和 HL。

我需要创建一个列(我称之为标志),当收盘价等于 HC 时显示 1,并且该值一直持续到收盘价等于 HL。此时 Flag 的值为 -1。如您所见,它非常简单,Flag 只能有两个值; 1 或 -1。

简单的公式是这样的:

  1. 如果关闭 == HC 则标志为 1
  2. 如果关闭 == HL,则标志为 -1
  3. 如果 Close != HC 和 Close !=HL then flag 等于变量 flag 上保存的最后一个值。

我尝试了几件事,包括下面的代码,但都没有成功。此代码的问题是显示了 0 值。而且我不知道如何通过条件使其消失:

import pandas as pd
import pandas_datareader as dr
import numpy as np
from datetime import date

df = dr.data.get_data_yahoo('SPY',start='01-01-2019',end=date.today())

df['HC'] = df['Close'].rolling(20).max() 
df['LC'] = df['Close'].rolling(20).min() 

df['Flag'] = [1 if (df.loc[ei, 'Close'] == df.loc[ei, 'HC']) else 
              -1 if (df.loc[ei, 'Close'] == df.loc[ei, 'LC']) else   
              0 for ei in df.index]

您可以在下面看到我的代码的蓝色结果和我需要的红色结果。

有没有简单的方法来做到这一点?如果有人可以帮助我,我将不胜感激。谢谢!

【问题讨论】:

    标签: python pandas trading algorithmic-trading


    【解决方案1】:

    尽管已经回答了这个问题,但是计算这种结果的最快方法通常是使用np.where,如下所示:

    import pandas as pd
    import pandas_datareader as dr
    import numpy as np
    from datetime import date
    
    df = dr.data.get_data_yahoo('SPY',start='01-01-2019',end=date.today())
    
    df['HC'] = df['Close'].rolling(20).max() 
    df['LC'] = df['Close'].rolling(20).min() 
    

    下面有嵌套逻辑:

    1. 创建一个空数组
    2. 在条件下将值替换为 -1
    3. 在条件下将值替换为 1
    df['Flag'] = np.where((df.Close == df.HC), 1, 
             np.where(df.Close == df.LC, -1, np.full(df.Close.count(), np.nan)))
    df.Flag.fillna(method='ffill', inplace=True)
    

    在性能方面:

    %%timeit
    df['Flag'] = np.where((df.Close == df.HC), 1, 
             np.where(df.Close == df.LC, -1, np.full(df.Close.count(), np.nan)))
    df.Flag.fillna(method='ffill', inplace=True)
    
    912 µs ± 49.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    

    这肯定比循环或嵌套 if 条件更好。

    例如@Tim Mironov 回答:

    %%timeit
    pos_indexes = (df.Close == df.HC)
    neg_indexes = (df.Close == df.LC)
    
    df.loc[pos_indexes, 'Good_Flag'] = 1
    df.loc[neg_indexes, 'Good_Flag'] = -1
    
    df.fillna(method='ffill', inplace=True)
    
    4.43 ms ± 92 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    【讨论】:

    • 这是最好的答案,也是在 Pandas 中执行此操作的方法,尽管我认为您可以只使用 df['Flag'] = np.where(df['Close'] == df['HC'], 1, np.where(df['Close'] == df['LC'], -1, np.nan)),而不是使用 np.full
    • 正是我所需要的。谢谢!
    • 不错的答案,感谢您提供额外的、性能更好的方法!
    【解决方案2】:

    您可以使用更多 Pandas 的内置功能,特别是 fillna 方法和逻辑索引的使用。 我在你原来的代码中添加了一部分代码来创建一个额外的Good_Flag

    import pandas_datareader as dr
    from datetime import date
    
    df = dr.data.get_data_yahoo('SPY',start='01-01-2019',end=date.today())
    
    df['HC'] = df['Close'].rolling(20).max() 
    df['LC'] = df['Close'].rolling(20).min() 
    
    df['Flag'] = [1 if (df.loc[ei, 'Close'] == df.loc[ei, 'HC']) else 
                  -1 if (df.loc[ei, 'Close'] == df.loc[ei, 'LC']) else   
                  0 for ei in df.index]
    
    pos_indexes = df.Close == df.HC
    neg_indexes = df.Close == df.LC
    df.loc[pos_indexes, 'Good_Flag'] = 1
    df.loc[neg_indexes, 'Good_Flag'] = -1
    df = df.fillna(method='ffill')
    

    请注意,我使用了带有ffill 属性的fillna 方法来指定“Forward Pass”。

    编辑: 为了清楚起见,原来的Flag 列故意留在这里,新的Good_Flag 列的计算不依赖于Flag

    为了展示理想的行为:

    plt.plot(df.Flag, color='blue')
    plt.plot(df.Good_Flag, color='red')
    plt.legend(['Flag', 'Good_Flag'])
    plt.show()
    

    【讨论】:

    • 当我运行这段代码并绘制它时,我得到的结果与示例图中 OP 的蓝线相同。我认为您想将第 6 行中的 0 更改为 np.nan,然后它会按您的预期工作。
    • @HS-nebula,实际上我只是留下了@Martingale 提供的代码,因为它正在计算Flag 列。然后我计算额外的Good_Flag 列作为替代,以展示两者之间的差异。确保绘制两列。
    【解决方案3】:

    一种简单的方法是使用循环,但这在时间方面效率低下。不过,如果你不介意,你可以循环遍历数组

    flag01 = 0
    for ei in df.index:
        if (df.loc[ei, 'Close'] == df.loc[ei, 'HC']):
            flag01 = 1
        if (df.loc[ei, 'Close'] == df.loc[ei, 'LC'])
            flag01 = -1
        df.loc[ei, 'Flag'] = flag01
    

    基本上你设置零,每当你发现条件为真时,你设置为 1 并保持一,直到满足条件变为 -1,依此类推。这比您使用的方法慢,但它是“保留最后一个值”的最简单方法,因为您正在增加值并且您知道之前有哪个值。

    【讨论】:

    • 每次运行此循环时,您都会将整个“标志”列更改为 flag01 的任何值,而不是每一行的值。
    • 你说得对,应该是df.loc[ei, 'Flag'] ?谢谢!无论如何,下面的答案要好得多,它以更优雅,更有效的方式解决了问题。
    猜你喜欢
    • 2015-05-13
    • 2011-02-27
    • 2012-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-21
    相关资源
    最近更新 更多