Introduction

我正在使用Python pandas对本地存储的市场数据进行回测自己的策略.由于我想快速回测这些策略,并且数据很大(7+ 00万行),因此我正在try 将所有操作进行载体化.对于入口信号判断,情况已经是如此,而且效果相当好.作 for each 参赛作品的退出标准,使用take profitstop loss价格阈值.即为以下DataFrame提供datetime index:

import pandas as pd
from pandas import Timestamp
import numpy as np

df = pd.DataFrame({
    'open': {Timestamp('2021-01-03 22:11:00'): 1.22319, Timestamp('2021-01-03 22:12:00'): 1.22315, Timestamp('2021-01-03 22:15:00'): 1.22324, Timestamp('2021-01-03 22:16:00'): 1.22355, Timestamp('2021-01-03 22:17:00'): 1.22357}, 
    'high': {Timestamp('2021-01-03 22:11:00'): 1.22319, Timestamp('2021-01-03 22:12:00'): 1.22318, Timestamp('2021-01-03 22:15:00'): 1.22358, Timestamp('2021-01-03 22:16:00'): 1.2236, Timestamp('2021-01-03 22:17:00'): 1.22361}, 
    'low': {Timestamp('2021-01-03 22:11:00'): 1.22317, Timestamp('2021-01-03 22:12:00'): 1.22315, Timestamp('2021-01-03 22:15:00'): 1.22324, Timestamp('2021-01-03 22:16:00'): 1.22352, Timestamp('2021-01-03 22:17:00'): 1.22355}, 
    'close': {Timestamp('2021-01-03 22:11:00'): 1.22317, Timestamp('2021-01-03 22:12:00'): 1.22315, Timestamp('2021-01-03 22:15:00'): 1.22358, Timestamp('2021-01-03 22:16:00'): 1.22352, Timestamp('2021-01-03 22:17:00'): 1.22356}, 
    'longEntrySignal': {Timestamp('2021-01-03 22:11:00'): False, Timestamp('2021-01-03 22:12:00'): False, Timestamp('2021-01-03 22:15:00'): True, Timestamp('2021-01-03 22:16:00'): False, Timestamp('2021-01-03 22:17:00'): False}, 
    'longEntry': {Timestamp('2021-01-03 22:11:00'): False, Timestamp('2021-01-03 22:12:00'): False, Timestamp('2021-01-03 22:15:00'): False, Timestamp('2021-01-03 22:16:00'): True, Timestamp('2021-01-03 22:17:00'): False}, 
    'longEntryPrice': {Timestamp('2021-01-03 22:11:00'): np.nan, Timestamp('2021-01-03 22:12:00'): np.nan, Timestamp('2021-01-03 22:15:00'): np.nan, Timestamp('2021-01-03 22:16:00'): 1.22355, Timestamp('2021-01-03 22:17:00'): np.nan}, 
    'longTpPrice': {Timestamp('2021-01-03 22:11:00'): np.nan, Timestamp('2021-01-03 22:12:00'): np.nan, Timestamp('2021-01-03 22:15:00'): np.nan, Timestamp('2021-01-03 22:16:00'): 1.2243451663854852, Timestamp('2021-01-03 22:17:00'): np.nan}, 
    'longSlPrice': {Timestamp('2021-01-03 22:11:00'): np.nan, Timestamp('2021-01-03 22:12:00'): np.nan, Timestamp('2021-01-03 22:15:00'): np.nan, Timestamp('2021-01-03 22:16:00'): 1.2227548336145146, Timestamp('2021-01-03 22:17:00'): np.nan}})

print(df)
                    open    high    low     close   longEntrySignal longEntry   longEntryPrice  longTpPrice longSlPrice
2021-01-03 22:11:00 1.22319 1.22319 1.22317 1.22317 False           False       NaN             NaN         NaN
2021-01-03 22:12:00 1.22315 1.22318 1.22315 1.22315 False           False       NaN             NaN         NaN
2021-01-03 22:15:00 1.22324 1.22358 1.22324 1.22358 True            False       NaN             NaN         NaN
2021-01-03 22:16:00 1.22355 1.22360 1.22352 1.22352 False           True        1.22355         1.224345    1.222755
2021-01-03 22:17:00 1.22357 1.22361 1.22355 1.22356 False           False       NaN             NaN         NaN

longEntrySignal通过TrueFalse表示在下一个蜡烛内打开多头头寸的给定信号.longEntry代表开盘位置,TruelongEntryPrice以该蜡烛的开盘位置作为入场价格.longTpPricelongSlPrice是根据达到的止盈或停止损失标准应关闭开仓头寸的相应价格.

Desired output

根据为take profitstop loss Select 的阈值,很可能存在具有不同入口点(time)take profit以及stop loss阈值的多个持有位置.

不管怎样,我现在的问题是如何计算入场后平仓的情况.这意味着之后验证策略性能的最低信息为exitPriceexitTime列.

                    open    high    low     close   longEntrySignal longEntry   longEntryPrice  longTpPrice longSlPrice exitPrice exitTime
2021-01-03 22:11:00 1.22319 1.22319 1.22317 1.22317 False           False       NaN             NaN         NaN         NaN       NaN
2021-01-03 22:12:00 1.22315 1.22318 1.22315 1.22315 False           False       NaN             NaN         NaN         NaN       NaN
2021-01-03 22:15:00 1.22324 1.22358 1.22324 1.22358 True            False       NaN             NaN         NaN         NaN       NaN
2021-01-03 22:16:00 1.22355 1.22360 1.22352 1.22352 False           True        1.22355         1.224345    1.222755    1.224345  2021-01-03 22:29:00
2021-01-03 22:17:00 1.22357 1.22361 1.22355 1.22356 False           False       NaN             NaN         NaN         NaN       NaN

exitPrice将是相应的take profit(longTpPrice)或stop loss(longSlPrice)阈值,而如果在同一row (candle)内达到两个阈值,则应考虑longSlPrice.那么exitTime将是相应的时间.

Current approach

目前,我将df减少到给定long entries的行后,使用apply()函数进行退出计算:

entryDf = df[df['longEntry']].copy()
entryDf[['exitPrice', 'exitTime']] = entryDf.apply(lambda x: getLongExit(exitDf=df[['high', 'low']], entryPrice=x['longEntryPrice'], entryTime=x.index, takeProfit=x['longTpPrice'], stopLoss=x['longSlPrice']), axis=1, result_type='expand')

然后,通过基本上使用.loc.idxmax().idxmin()来确定getLongExit是否达到阈值,以及如果何时达到阈值take profit或/和stop loss并比较结果以判断哪个发生得更早.它返回相应的exitPrice和相应的exitTime.

基于确定的exitPrice,例如overall gainwin-rate等信息可以很容易地计算.

我希望我能够让您正确了解我的问题是什么.可以肯定的是,使用.ffill()或类似功能可能会更快,但我无法让它工作.我期待您的建议-谢谢!

Edit/Update according to @Andrej's solution

我按照建议实施了您的解决方案,但它会导致以下错误:

Traceback (most recent call last):
  File "/Users/maxwitt/PycharmProjects/ForexStrategies/strategy1.py", line 288, in <module>
    get_long_exit(
  File "/Users/maxwitt/PycharmProjects/ForexStrategies/venv/lib/python3.10/site-packages/numba/core/dispatcher.py", line 468, in _compile_for_args
    error_rewrite(e, 'typing')
  File "/Users/maxwitt/PycharmProjects/ForexStrategies/venv/lib/python3.10/site-packages/numba/core/dispatcher.py", line 409, in error_rewrite
    raise e.with_traceback(None)
numba.core.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
No implementation of function Function(<built-in function setitem>) found for signature:
 
 >>> setitem(array(float64, 1d, C), int64, datetime64[ns])
 
There are 16 candidate implementations:
   - Of which 16 did not match due to:
   Overload of function 'setitem': File: <numerous>: Line N/A.
     With argument(s): '(array(float64, 1d, C), int64, datetime64[ns])':
    No match.

During: typing of setitem at /Users/maxwitt/PycharmProjects/ForexStrategies/strategy1.py (167)

File "strategy1.py", line 167:
def get_long_exit(index, high_vals, low_vals, tp_prices, sl_prices, out_exit_price, out_indices):
    <source elided>
                out_exit_price[idx1] = sl_entry
                out_indices[idx1] = index[idx2]
                ^

推荐答案

IIUC, for this task I'd use :

import numba

@numba.njit
def get_long_exit(
    index, high_vals, low_vals, tp_prices, sl_prices, out_exit_price, out_indices
):
    for idx1 in range(len(index) - 1):
        if np.isnan(tp_prices[idx1]):
            continue

        tp_entry, sl_entry = tp_prices[idx1], sl_prices[idx1]

        for idx2 in range(idx1 + 1, len(index)):
            h, l = high_vals[idx2], low_vals[idx2]

            # i'm not sure about these if's, but you can adjust it to your needs:
            if sl_entry > l:
                out_exit_price[idx1] = sl_entry
                out_indices[idx1] = index[idx2]
                break
            elif tp_entry < h:
                out_exit_price[idx1] = tp_entry
                out_indices[idx1] = index[idx2]
                break

df["exitPrice"] = np.nan
df["exitIndex"] = np.nan

# then fill the exitPrice/exitIndex values by:
get_long_exit(
    df.index.values.astype("float64"),  # <-- convert datetime64 to float64
    df.high.values,
    df.low.values,
    df.longTpPrice.values,
    df.longSlPrice.values,
    df.exitPrice.values,
    df.exitIndex.values,
)

# convert back float64 to datetime64
df["exitIndex"] = df["exitIndex"].astype("datetime64[ns]")

print(df)

Python相关问答推荐

如何检测背景有噪的图像中的正方形

将数据框架与导入的Excel文件一起使用

按列分区,按另一列排序

Vectorize多个头寸的止盈/止盈回溯测试pythonpandas

如何在类和classy-fastapi -fastapi- followup中使用FastAPI创建路由

如何在WSL2中更新Python到最新版本(3.12.2)?

如何使用Python以编程方式判断和检索Angular网站的动态内容?

对象的`__call__`方法的setattr在Python中不起作用'

连接一个rabrame和另一个1d rabrame不是问题,但当使用[...]'运算符会产生不同的结果

当递归函数的返回值未绑定到变量时,非局部变量不更新:

Python中的变量每次增加超过1

Python Tkinter为特定样式调整所有ttkbootstrap或ttk Button填充的大小,适用于所有主题

手动设置seborn/matplotlib散点图连续变量图例中显示的值

在方法中设置属性值时,如何处理语句不可达[Unreacable]";的问题?

Python日志(log)模块如何在将消息发送到父日志(log)记录器之前向消息添加类实例变量

统计numpy. ndarray中的项目列表出现次数的最快方法

从嵌套极轴列的列表中删除元素

应用指定的规则构建数组

删除Dataframe中的第一个空白行并重新索引列

利用广播使减法更有效率