Reputation: 1
Okay, right now I use df.iterrows()
in chunks to loop through each minute and compare with the previous Renko close. However, this takes a long time. I keep thinking is there a way to vectorize and backtest faster. Cause I will be backtesting on tick data which means millions of rows.
Below is the for loop I have been using. I am a newbie to this so please excuse any inefficiencies in the code I have written and help me in any way that is possible.
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
Upvotes: 0
Views: 1153
Reputation: 23
You can use vectorised Renko below. It is designed for FX markets, to make it work with stock you may want to remove pips conversion: brick_size = brick_size_pips / 10000.
Performance-wise, it takes approx. 250ms to calculate Renko for the whole year against 16,600ms for the iterative approach, around 70x quicker.
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
Upvotes: 1