好吧,现在我在块中使用
df.iterrows()
来循环每分钟并与之前的砖形收盘进行比较。然而,这需要很长时间。我一直在想是否有一种方法可以更快地进行矢量化和回测。因为我将对刻度数据进行回测,这意味着数百万行。
下面是我一直在使用的for循环。我是这方面的新手,所以请原谅我编写的代码中的任何低效之处,并以任何可能的方式帮助我。
def trend(box, df, lasttrend, lastrenko):
for rows in df.itertuples():
index = rows.Index
print(index)
df.loc[index, 'renko close'] = lastrenko
if index == 0:
df.loc[index, 'renko close'] = df.loc[index, 'close']
df.loc[index, 'trend'] = 'S'
lasttrend = 'S'
lastrenko = df.loc[index, 'renko close']
print('Trend start: S', lastrenko, '\n-------------------------------------')
continue
if df.loc[index, 'close'] - lastrenko >= box:
if lasttrend == 'R':
if df.loc[index, 'close'] - lastrenko >= 2*box:
df.loc[index, 'renko close'] = lastrenko + (2*box)
df.loc[index, 'deviation'] = abs(df.loc[index, 'close'] - df.loc[index, 'renko close'])
df.loc[index, 'deviation boxes'] = df.loc[index, 'deviation']/box
df.loc[index, 'trend'] = 'G'
if df.loc[index,'deviation boxes'] >= 1:
df.loc[index, 'renko close'] = df.loc[index, 'renko close'] + (math.floor(df.loc[index,'deviation boxes']))*(box)
lasttrend = 'G'
lastrenko = df.loc[index, 'renko close']
print('Trend change: R - G', lastrenko, '\n-------------------------------------')
elif lasttrend == 'G' or lasttrend == 'S':
df.loc[index, 'renko close'] = lastrenko + (box)
df.loc[index, 'deviation'] = abs(df.loc[index, 'close'] - df.loc[index, 'renko close'])
df.loc[index, 'deviation boxes'] = df.loc[index, 'deviation']/box
df.loc[index, 'trend'] = 'G'
if df.loc[index, 'deviation boxes'] >= 1:
df.loc[index, 'renko close'] = df.loc[index, 'renko close'] + (math.floor(df.loc[index, 'deviation boxes']))*(box)
lasttrend = 'G'
lastrenko = df.loc[index, 'renko close']
print('Trend change: G - G', lastrenko, '\n-------------------------------------')
else:
df.loc[index, 'deviation'] = abs(df.loc[index, 'close'] - df.loc[index, 'renko close'])
df.loc[index, 'deviation boxes'] = df.loc[index, 'deviation']/box
df.loc[index, 'trend'] = 'N'
print('Trend change: R - N', lastrenko, '\n-------------------------------------')
elif df.loc[index, 'close'] - lastrenko <= -box:
if lasttrend == 'G':
if df.loc[index, 'close'] - lastrenko <= -2*box:
df.loc[index, 'renko close'] = lastrenko - (2*box)
df.loc[index, 'deviation'] = abs(df.loc[index, 'close'] - df.loc[index, 'renko close'])
df.loc[index, 'deviation boxes'] = df.loc[index, 'deviation']/box
df.loc[index, 'trend'] = 'R'
if df.loc[index,'deviation boxes'] >= 1:
df.loc[index, 'renko close'] = df.loc[index, 'renko close'] - (math.floor(df.loc[index,'deviation boxes']))*(box)
lasttrend = 'R'
lastrenko = df.loc[index, 'renko close']
print('Trend change: G - R', lastrenko, '\n-------------------------------------')
elif lasttrend == 'R' or lasttrend == 'S':
df.loc[index, 'renko close'] = lastrenko - (box)
df.loc[index, 'deviation'] = abs(df.loc[index, 'close'] - df.loc[index, 'renko close'])
df.loc[index, 'deviation boxes'] = df.loc[index, 'deviation']/box
df.loc[index, 'trend'] = 'R'
if df.loc[index,'deviation boxes'] >= 1:
df.loc[index, 'renko close'] = df.loc[index, 'renko close'] - (math.floor(df.loc[index,'deviation boxes']))*(box)
lasttrend = 'R'
lastrenko = df.loc[index, 'renko close']
print('Trend change: R - R', lastrenko, '\n-------------------------------------')
else:
df.loc[index, 'deviation'] = abs(df.loc[index, 'close'] - df.loc[index, 'renko close'])
df.loc[index, 'deviation boxes'] = df.loc[index, 'deviation']/box
df.loc[index, 'trend'] = 'N'
print('Trend change: G - N', lastrenko, '\n-------------------------------------')
else:
df.loc[index, 'renko close'] = lastrenko
df.loc[index, 'deviation'] = abs(df.loc[index, 'close'] - df.loc[index, 'renko close'])
df.loc[index, 'deviation boxes'] = df.loc[index, 'deviation']/box
df.loc[index, 'trend'] = 'N'
print('Trend change: N', lastrenko, '\n-------------------------------------')
return df, lasttrend, lastrenko
您可以使用下面的矢量化砖形。它是专为外汇市场设计的,为了使其与股票配合使用,您可能需要删除点转换:
brick_size = brick_size_pips / 10000.
就性能而言,大约需要。计算全年砖形图需要 250 毫秒,而迭代方法则需要 16,600 毫秒,快了大约 70 倍。
def create_renko(df_prices, brick_size_pips):
t1 = time.perf_counter()
# Renko charts generate new brick when price moves by "brick_size" from old one
# Algorithm below splits price into "brick_size" bins, starting from firs close price (ref_price)
# It forms new "down" brick when price left last bin in "down" direction and reaches new bin
# The same for "up" brick but price needs to leave in "up" direction and reach new bin over existing
# During high volatility (large candles) price may leave current bin and immediately move to new one
# This can be detemined by "2 bins move" and is handled as edge case scenario
# This approach has known flaw - when price gap (ie over weekend) is of 2 or more Renko brick size, it will generate 1 brick only
df = df_prices[['high_bid', 'low_bid', 'close_bid']].dropna().copy()
ref_price = df.iloc[0]['close_bid']
ref_index = df.iloc[0].name
brick_size = brick_size_pips / 10000.
df['high_bin_float'] = ((df['high_bid'] - ref_price) / brick_size).fillna(0).round(5) # distance from ref_price
df['high_bin'] = df['high_bin_float'].apply(np.floor) # bin number
df['high_bin_diff'] = df['high_bin'].diff().fillna(0) # change in bin number
df['low_bin_float'] = ((df['low_bid'] - ref_price) / brick_size).fillna(0).round(5)
df['low_bin'] = df['low_bin_float'].apply(np.floor)
df['low_bin_diff'] = df['low_bin'].diff().fillna(0)
# previous bin numbers will be needed for edge case scenario
df['high_bin_prev'] = df['high_bin'].shift(1)
df['low_bin_prev'] = df['low_bin'].shift(1)
# remember direction of price movement when crossing bin boundary
df.loc[df.index == df.index[0], 'flip_direction'] = 'up'
df.loc[(df['high_bin_diff'] > 0) | (df['low_bin_diff'] > 0), 'flip_direction'] = 'up'
df.loc[(df['high_bin_diff'] < 0) | (df['low_bin_diff'] < 0), 'flip_direction'] = 'down'
df['flip_direction'] = df['flip_direction'].fillna(method='pad')
df['flip_direction_prev'] = df['flip_direction'].shift(1)
# new "up" brick is formed when (price moves to higher bin) and ((price left last bin in "up" direction) or (high volatility, candles crossed two bins))
df['new_brick_up'] = (df['high_bin_diff'] > 0) & ((df['flip_direction_prev'] == 'up') | (abs(df['low_bin_prev'] - df['high_bin']) > 1))
# new "down" brick is formed when (price moves to lower bin) and ((price left last bin in "down" direction) or (high volatility, candles crossed two bins))
df['new_brick_down'] = (df['low_bin_diff'] < 0) & ((df['flip_direction_prev'] == 'down') | (abs(df['high_bin_prev'] - df['low_bin']) > 1))
# populate helper data, start / end datetime and start / end price for each brick
df['end_dt'] = df.index
df.loc[df['new_brick_up'] | df['new_brick_down'], 'start_dt'] = df['end_dt']
df['start_dt'] = df['start_dt'].fillna(method='pad')
df['start_dt'] = df['start_dt'].shift(1).fillna(ref_index)
df.loc[df['new_brick_down'], 'end_price'] = ref_price + brick_size * df['low_bin_float'].round(0)
df.loc[df['new_brick_up'], 'end_price'] = ref_price + brick_size * df['high_bin_float'].round(0)
df['start_price'] = df['end_price'].fillna(method='pad').shift(1)
df['start_price'] = df['start_price'].fillna(ref_price)
# helpers to plot using OHLC charts
df['new_brick_up_prev'] = df['new_brick_up'].shift(1)
df['new_brick_down_prev'] = df['new_brick_down'].shift(1)
df.loc[df['new_brick_up'], 'change'] = 10
df.loc[df['new_brick_down'], 'change'] = -10
df['change'] = df['change'].fillna(0)
df['change_cumsum'] = df['change'].cumsum()
df['change_cumsum_prev'] = df['change_cumsum'].shift(1)
df.loc[df['new_brick_up'], 'open_bid'] = df['change_cumsum'] - 10
df.loc[df['new_brick_down'], 'open_bid'] = df['change_cumsum']
df.loc[df['new_brick_up'], 'close_bid'] = df['change_cumsum']
df.loc[df['new_brick_down'], 'close_bid'] = df['change_cumsum'] - 10
df['high_bid'] = df[['open_bid', 'close_bid']].max(axis=1)
df['low_bid'] = df[['open_bid', 'close_bid']].min(axis=1)
# return only relevant columns
df = df[df['new_brick_up'] | df['new_brick_down']][['new_brick_up', 'new_brick_down', 'start_dt', 'end_dt', 'start_price', 'end_price', 'open_bid', 'high_bid', 'low_bid', 'close_bid']].copy()
print(f'Renko built in: {((time.perf_counter()-t1)*1000.):,.2f}ms')
return df
存在一些错误,如红色箭头所示。