我有一个循环,循环数天和一个金融时间序列的离散时隙,我的策略的一些代码就在里面。我将这个内部代码提取到一个独立的函数中,以便能够在特定情况下使用它进行测试,直到稍后才发现这个操作大大增加了这个循环的总计算时间。这是合理的,长函数签名,调用,(堆栈内存?)所有这些都会带来一些开销。
# Fast, but cannot use my strategy for specific instances:
for d in data:
# my strategy code
# Flexible but really slow
def my_strategy(**params):
# my strategy code
f = partial(my_strategy, **params)
for d in data:
f(d)现在,我的问题是:是否有一种方法可以同时保留中的设置、独立函数和具有循环和实际代码的函数,而不需要显式冗余?我需要两者兼得。
我已经用inspect.getsource(my_function)做了实验,并包装了一份外部函数的副本,然后使用eval,但是5分钟后,我觉得自己像个白痴,感觉一点也不对。
做这件事的最佳做法是什么?如果我重复代码,我必须确保如果我修改一个版本,另一个版本总是同步的。我不喜欢这样。我想要有效地反映这一点。
正如您可能已经想到的那样,有一个选项可以是:将代码留在循环中,并在需要时将特定的情况包装在数组中,并将其提供给循环。嗯,我不能这样做,主要是因为我需要对内部代码执行有一个定时的控制(正在将经过回传测试的策略移植到实时)。在这里,我只是寻找一些快速的技巧,以反映优雅的代码,如果有任何。
编辑
好的,为了进一步测试这一点,我实现了我刚才提到的可怕的攻击,并让它与exec一起工作。由于这一点,我现在可以保证所使用的代码完全相同。不过,观测到的时差是一样的。包含代码的循环大约占用函数调用循环所需时间的一半。
# horrible hack, please forgive me
def extract_strategy_function():
# Start
source_code = inspect.getsource(fastest_compute_trades)
# Cutting the code snippet
source_code = source_code.split('[safe_start - 1:]:', 1)[1].rsplit("if s['pos_open']:", 1)[0]
# Needs re-indentation so the parser doesn't complain
source_code = reindent(source_code, -2)
function_name = 'single_apply_strategy' # internal, no-one will see this anyway
function_string = f"""
def {function_name}(
# INDICATORS (non mutable)
i_time, close_price, r2, ma1, ma2, rsi, upper, lower,
# STATE VARIABLES, TX HISTORY (mutable, 2 dictionaries and 1 list of dicts)
s, gs, transactions,
# CONSTANT PARAMETERS (non mutable)
min_r2, macd_threshold, rsi_bound, waiting_slots, stop_loss,
take_profit, trail_profit, double_down, dd_qty, commission_fee,
# Day ID, useful when back-testing/grid-searching, left at the end so it can default to 0
i_day=0
):
{source_code}
""".strip()
# Evaluate it
exec(function_string) # Now in locals
return locals()[function_name]
def slow(...):
...
apply_strategy = partial(extract_strategy_function(),
**params, commission_fee=commission_fee, rsi_bound=rsi_bound)
for i_day, day in enumerate(data):
for i_time, close_price, r2, ma1, ma2, rsi, upper, lower, in list(enumerate(day))[safe_start - 1:]:
apply_strategy(i_time, close_price, r2, ma1, ma2, rsi, upper, lower,
day_state, global_state, transactions, i_day=i_day)
if day_state['pos_open']:
...
...
def fast(...,
#some extra parameters are now unpacked here,
#since all code is here they are now needed here
):
...
for i_day, day in enumerate(data):
for i_time, close_price, r2, ma1, ma2, rsi, upper, lower, in list(enumerate(day))[safe_start - 1:]:
# actual code contained in single_apply_strategy written here, the whole strategy (one timestep)
if day_state['pos_open']:
...
...有什么建议吗?
附注:不用说,这两个设置的输出完全相同。
发布于 2021-02-08 14:07:14
函数调用引起的任何开销很可能可以忽略不计。使用您的功能,在需要的情况下,通过“手动插入”您的功能来衡量可能的改进。
如果您需要这样的性能,Python可能不是最好的选择,在这种情况下,我建议使用C++。您需要一个语言特性,它称为内联函数:https://www.geeksforgeeks.org/inline-functions-cpp/
有关Python和内联的更多信息:Python equivalence to inline functions or macros
无论如何,我认为你在这个方向上的努力不会有任何改善。
https://stackoverflow.com/questions/66101653
复制相似问题