【问题标题】:Converting OHLC stock data into a different timeframe with python and pandas使用 python 和 pandas 将 OHLC 股票数据转换为不同的时间范围
【发布时间】:2023-04-07 21:08:01
【问题描述】:

关于使用Pandas 进行 OHLC 数据时间帧转换,有人可以为我指明正确的方向吗?我正在尝试做的是构建一个包含更高时间范围数据的数据框,给定具有更低时间范围的数据。

例如,假设我有以下一分钟 (M1) 数据:

                       Open    High     Low   Close  Volume
Date                                                       
1999-01-04 10:22:00  1.1801  1.1819  1.1801  1.1817       4
1999-01-04 10:23:00  1.1817  1.1818  1.1804  1.1814      18
1999-01-04 10:24:00  1.1817  1.1817  1.1802  1.1806      12
1999-01-04 10:25:00  1.1807  1.1815  1.1795  1.1808      26
1999-01-04 10:26:00  1.1803  1.1806  1.1790  1.1806       4
1999-01-04 10:27:00  1.1801  1.1801  1.1779  1.1786      23
1999-01-04 10:28:00  1.1795  1.1801  1.1776  1.1788      28
1999-01-04 10:29:00  1.1793  1.1795  1.1782  1.1789      10
1999-01-04 10:31:00  1.1780  1.1792  1.1776  1.1792      12
1999-01-04 10:32:00  1.1788  1.1792  1.1788  1.1791       4

每分钟都有开盘价、最高价、最低价、收盘价 (OHLC) 和成交量值我想构建一组 5 分钟读数 (M5),如下所示:

                       Open    High     Low   Close  Volume
Date                                                       
1999-01-04 10:25:00  1.1807  1.1815  1.1776  1.1789      91
1999-01-04 10:30:00  1.1780  1.1792  1.1776  1.1791      16

所以工作流程是这样的:

  • Open 是时间窗口中第一行的 Open
  • High 是时间窗口中的最高 High
  • Lo​​w是最低的Low
  • Close 是最后一个 Close
  • Volume 只是 Volumes 的总和

不过有几个问题:

  • 数据有间隙(注意没有 10:30:00 行)
  • 5 分钟的间隔必须从循环时间开始,例如M5 开始于 10:25:00 而不是 10:22:00
  • 首先,不完整的集合可以像本例一样被省略,也可以包括在内(所以我们可以有 10:20:00 5 分钟的条目)

Pandas documentation on up-down sampling 给出了一个例子,但他们使用平均值作为上采样行的值,这在此处不起作用。我曾尝试使用groupbyagg,但无济于事。对于一个人来说,获得最高的最高价和最低的最低价可能并不难,但我不知道如何获得第一个开盘和最后一个收盘。

我尝试的内容大致如下:

grouped = slice.groupby( dr5minute.asof ).agg( 
    { 'Low': lambda x : x.min()[ 'Low' ], 'High': lambda x : x.max()[ 'High' ] } 
)

但它会导致以下错误,我不明白:

In [27]: grouped = slice.groupby( dr5minute.asof ).agg( { 'Low' : lambda x : x.min()[ 'Low' ], 'High' : lambda x : x.max()[ 'High' ] } )
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
/work/python/fxcruncher/<ipython-input-27-df50f9522a2f> in <module>()
----> 1 grouped = slice.groupby( dr5minute.asof ).agg( { 'Low' : lambda x : x.min()[ 'Low' ], 'High' : lambda x : x.max()[ 'High' ] } )

/usr/lib/python2.7/site-packages/pandas/core/groupby.pyc in agg(self, func, *args, **kwargs)
    242         See docstring for aggregate
    243         """
--> 244         return self.aggregate(func, *args, **kwargs)
    245 
    246     def _iterate_slices(self):

/usr/lib/python2.7/site-packages/pandas/core/groupby.pyc in aggregate(self, arg, *args, **kwargs)
   1153                     colg = SeriesGroupBy(obj[col], column=col,
   1154                                          grouper=self.grouper)
-> 1155                     result[col] = colg.aggregate(func)
   1156 
   1157             result = DataFrame(result)

/usr/lib/python2.7/site-packages/pandas/core/groupby.pyc in aggregate(self, func_or_funcs, *args, **kwargs)
    906                 return self._python_agg_general(func_or_funcs, *args, **kwargs)
    907             except Exception:
--> 908                 result = self._aggregate_named(func_or_funcs, *args, **kwargs)
    909 
    910             index = Index(sorted(result), name=self.grouper.names[0])

/usr/lib/python2.7/site-packages/pandas/core/groupby.pyc in _aggregate_named(self, func, *args, **kwargs)
    976             grp = self.get_group(name)
    977             grp.name = name
--> 978             output = func(grp, *args, **kwargs)
    979             if isinstance(output, np.ndarray):
    980                 raise Exception('Must produce aggregated value')

/work/python/fxcruncher/<ipython-input-27-df50f9522a2f> in <lambda>(x)
----> 1 grouped = slice.groupby( dr5minute.asof ).agg( { 'Low' : lambda x : x.min()[ 'Low' ], 'High' : lambda x : x.max()[ 'High' ] } )

IndexError: invalid index to scalar variable.

因此,我们将不胜感激任何帮助。如果我选择的路径不起作用,请建议其他相对有效的方法(我有数百万行)。一些关于使用 Pandas 进行财务处理的资源也很好。

【问题讨论】:

  • 你用的是什么版本的熊猫?我们正在努力改进时间序列功能,这将大大简化这个过程,但它不太可能在 4 月底左右发布。不过,这里可能也有需要修复的错误
  • 嗨,韦斯,我使用的是 0.7.2。我想等待新版本发布是一个可行的选择,因为我没有这个转换的最后期限(我需要私人研究的数据)。让我借此机会感谢您为开发 Pandas 付出的努力! :)
  • 至于潜在的错误,请注意我没有为 Dataframe 中的所有列指定值(仅 5 列中的 2 列),如果这就是您的意思的话。

标签: python stock pandas


【解决方案1】:

对于更新版本的 Pandas,有一个 resample 方法。它非常快并且对于完成相同的任务很有用:

ohlc_dict = {                                                                                                             
    'Open': 'first',                                                                                                    
    'High': 'max',                                                                                                       
    'Low': 'min',                                                                                                        
    'Close': 'last',                                                                                                    
    'Volume': 'sum',
}

df.resample('5T', closed='left', label='left').apply(ohlc_dict)

【讨论】:

  • 好像还有一个警告FutureWarning: using a dict with renaming is deprecated and will be removed in a future version prices1 = prices.resample('D', closed='left', label='left').apply(ohlc_dict)
【解决方案2】:

您的方法是合理的,但失败了,因为 dict-of-functions 中的每个函数都应用于 agg() 接收反映与键值匹配的列的 Series 对象。因此,没有必要 再次过滤列标签。有了这个,并假设 groupby 保持顺序, 您可以对系列进行切片以提取打开/关闭的第一个/最后一个元素 列(注意:groupby 文档并未声称保留原始数据的顺序 系列,但似乎在实践中。)

In [50]: df.groupby(dr5minute.asof).agg({'Low': lambda s: s.min(), 
                                         'High': lambda s: s.max(),
                                         'Open': lambda s: s[0],
                                         'Close': lambda s: s[-1],
                                         'Volume': lambda s: s.sum()})
Out[50]: 
                      Close    High     Low    Open  Volume
key_0                                                      
1999-01-04 10:20:00  1.1806  1.1819  1.1801  1.1801      34
1999-01-04 10:25:00  1.1789  1.1815  1.1776  1.1807      91
1999-01-04 10:30:00  1.1791  1.1792  1.1776  1.1780      16

作为参考,这里有一个总结预期的表格 基于 groupby 对象类型的聚合函数的输入和输出类型以及聚合函数如何传递给 agg()。

                  agg() method     agg func    agg func          agg()
                  input type       accepts     returns           result
GroupBy Object
SeriesGroupBy     function         Series      value             Series
                  dict-of-funcs    Series      value             DataFrame, columns match dict keys
                  list-of-funcs    Series      value             DataFrame, columns match func names
DataFrameGroupBy  function         DataFrame   Series/dict/ary   DataFrame, columns match original DataFrame
                  dict-of-funcs    Series      value             DataFrame, columns match dict keys, where dict keys must be columns in original DataFrame
                  list-of-funcs    Series      value             DataFrame, MultiIndex columns (original cols x func names)

从上表来看,如果聚合需要访问多个 列,唯一的选择是将单个函数传递给 DataFrameGroupBy 对象。因此,完成原始任务的另一种方法是定义 如下函数:

def ohlcsum(df):
    df = df.sort()
    return {
       'Open': df['Open'][0],
       'High': df['High'].max(),
       'Low': df['Low'].min(),
       'Close': df['Close'][-1],
       'Volume': df['Volume'].sum()
      }

并应用 agg() :

In [30]: df.groupby(dr5minute.asof).agg(ohlcsum)
Out[30]: 
                       Open    High     Low   Close  Volume
key_0                                                      
1999-01-04 10:20:00  1.1801  1.1819  1.1801  1.1806      34
1999-01-04 10:25:00  1.1807  1.1815  1.1776  1.1789      91
1999-01-04 10:30:00  1.1780  1.1792  1.1776  1.1791      16

虽然 pandas 将来可能会提供一些更简洁的内置魔法,但希望这能解释如何使用当今的 agg() 功能。

【讨论】:

  • 首先感谢您提供的信息丰富的回答 :) 您能否写下您使用的是哪个版本的 Pandas 以及您是如何创建 dr5minute 的? groupby( dr5minute.asof ) 似乎有问题,它只返回一个组。
  • 我猜问题可能出在不正确的索引上。我认为 CSV 中的日期没有正确解析为日期......但这是另一回事,因此无需在 cmets 中讨论。再次感谢@crewburm!
  • 不客气,@kgr。我正在使用 0.7.2。要解释 csv 中的日期,请查看 read_csv()converters 参数。
【解决方案3】:

在我的 ma​​in() 函数中,我正在接收流式买卖数据。然后我执行以下操作:

df = pd.DataFrame([])

for msg_type, msg in response.parts():
    if msg_type == "pricing.Price":
        sd = StreamingData(datetime.now(),instrument_string(msg),
                           mid_string(msg),account_api,account_id,
                           's','5min',balance)
        df = df.append(sd.df())
        sd.resample(df)

我创建了一个类 StreamingData(),它接受所提供的输入(还创建了一些函数来将买/卖数据分解为单独的组件(买价、卖价、中间价、工具等) .

您所要做的就是将 's''5min' 更改为您想要的任何时间范围。将其设置为“m”和“D”以按分钟获取每日价格。

这就是我的 StreamingData() 的样子:

class StreamingData(object):
def __init__(self, time, instrument, mid, api, _id, xsec, xmin, balance):
    self.time = time
    self.instrument = instrument
    self.mid = mid
    self.api = api
    self._id = _id
    self.xsec = xsec
    self.xmin = xmin
    self.balance = balance
    self.data = self.resample(self.df())

def df(self):
    df1 = pd.DataFrame({'Time':[self.time]})
    df2 = pd.DataFrame({'Mid':[float(self.mid)]})
    df3 = pd.concat([df1,df2],axis=1,join='inner')
    df = df3.set_index(['Time'])
    df.index = pd.to_datetime(df.index,unit='s')
    return df

def resample(self, df):
    xx = df.to_period(freq=self.xsec)
    openCol = xx.resample(self.xmin).first()
    highCol = xx.resample(self.xmin).max()
    lowCol = xx.resample(self.xmin).min()
    closeCol = xx.resample(self.xmin).last()
    self.data = pd.concat([openCol,highCol,lowCol,closeCol],
                           axis=1,join='inner')
    self.data['Open'] = openCol.round(5)
    self.data['High'] = highCol.round(5)
    self.data['Low'] = lowCol.round(5)
    self.data['Close'] = closeCol.round(5)
    return self.data

所以它从 StreamingData() 中获取数据,在 df() 中创建一个时间索引数据帧,附加它,然后发送到 resample( )。我计算的价格基于:mid = (bid+ask)/2

【讨论】:

    【解决方案4】:
    df = df.resample('4h').agg({
        'open': lambda s: s[0],
        'high': lambda df: df.max(),
        'low': lambda df: df.min(),
        'close': lambda df: df[-1],
        'volume': lambda df: df.sum()
    })
    

    【讨论】:

    • 您好!为了改进这个答案,您可能希望添加一些关于为什么/如何工作的描述。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-07
    • 2019-11-20
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多