freqtrade-strategies
freqtrade-strategies copied to clipboard
Renko bars/chart [enhancement]
I believe it would be a great enhancement to have Renko chart/bars as a feature in freqtrade. The nature of Renko charts will bring great filter capabilities to the "choppiness" of the crypto market.
Code reference: https://github.com/ChillarAnand/stocktrends https://www.backtrader.com/blog/posts/2017-06-26-renko-bricks/renko-bricks.html https://machinelearningtrading.wordpress.com/2014/02/23/plotting-renko-bars-in-python/
Interesting article about optimization of renko charts: https://towardsdatascience.com/renko-brick-size-optimization-34d64400f60e
What do you guys think ?
Here is my implementation; https://github.com/canercak/freqtrade/blob/develop/scripts/plot_renko.py https://github.com/canercak/freqtrade/blob/develop/user_data/strategies/renko_strategy.py
well, I know a lot about renko charts and used it with multiple timeframes and with many indicators in my bot and could get decent profits in trending markets.
I can assure you that dumbly buying when green and selling when red works better than putting more bricks into account or combining it with indicators.
Using atr mean value as brick size or period based atr give varying results but in general period based works better. If you want to see fixed bar chart of mean atr comment out the "if not np.isnan(row['atr'].." part. Another working combination is solely calculating macd by renko values and buy when macd > signal and sell when signal < macd.
I used some genetic algorithms in my bot to find out that 5 - 15 min time frames work better but going to use this bot's hyperopt to check too.
I am willing to work with anyone who is interested in renko charts and experienced in machine learning/ai.
@canercak
for the strategy you posted above- if you're willing to share it with a "wider" community - consider doing a PR against the strategy-repo...
Transferring this issue to freqtrade, since it's about a renko strategy more than anything else.
I was experimenting with this a bit, there are some methods to make the Renko charts out of the timeframe-based dataframe, for me the most complicated problem here is the keeping the starting point for Renko bars during dry-run/real-trade. Our time based dataframe is of 500 (well, almost) bars, "shifting" with every new candle arrives and Renko will always be repainted, calculating using new starting bar... The correct implementation should keep previously calculated Renko bars and the price limits that were used as the boundaries of the previous bars (or at least the first price boundary, for the first candle in the first ohlcv dataframe when the bot has just started and began calculating Renko's, used as the baseline for all upcoming ones)
@xmatthias I did implement a version of Renko Strategy with ATR brick size. I stacked the bricks when several bricks were created for one candle. I also added the bricks to the data frame only in addition to the original chart. this way, there are fewer calculation errors during backtesting. The strategy continues to generate high profits in backtesting, this could mean that calculation errors continue to occur. In general, I would suggest that using Renko as a single tool for a strategy is not very convincing, simpler strategies are more profitable in many cases.
Note that I duplicate previous Bricks if there was no new Brick for a Candle, but this does not affect the defined signals.
I also did not implement the hints from @hroff-1902 which would be very valuable for dry-run/real-run.
I publish the strategy here under MIT licenses, feel free to modify it and add it to the repo.
RenkoAllCandles.py
import arrow
import numpy as np
import pandas as pd
import talib.abstract as ta
from pandas import DataFrame
from freqtrade.strategy.interface import IStrategy
from freqtrade.exchange import timeframe_to_seconds
from datetime import timedelta
pd.set_option("display.precision", 10)
class RenkoAllCandles(IStrategy):
# Wir wollen kein ROI bei dieser Strateie
# minimal_roi = {"0": 0.249, "112": 0.121, "290": 0.059, "642": 0}
stoploss = -0.125
minimal_roi = {"0": 100}
# stoploss = -0.104
disable_dataframe_checks = True
atr_period = 5
# Wir benötigen kein startup_candle_count da wir sowieso nur die Candles zurück liefern die einen Renko Brick haben
timeframe = '15m'
timeframe_seconds = timeframe_to_seconds(timeframe)
plot_config = {
'main_plot': {
'renko_close': {'color': 'blue'},
'renko_open': {'color': 'orange'},
}
}
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# Füge ATR in den normalen Dataframe hinzu
dataframe['ATR'] = ta.ATR(dataframe, timeperiod=self.atr_period)
# Spalten die wir erzeugen wollen:
# normal_columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'ATR']
renko_columns = [
'date',
'renko_open',
'renko_high',
'renko_low',
'renko_close',
'trend',
'prev_trend',
'prev2_trend',
'prev3_trend',
'prev_date',
'new_brick',
]
DATE_IDX = 0
CLOSE_IDX = 4
TREND_IDX = 5
PREV_TREND_IDX = 6
PREV2_TREND_IDX = 7
NEW_BRICK = True
COPIED_BRICK = False
brick_size = np.NaN
# Liste aller "gestapelten" Bricks
data = []
prev_brick = None
prev2_trend = False
prev3_trend = False
for row in dataframe.itertuples():
# Für jede Candle (+ neue Ticks) berechnen wir nun die Bricks
if np.isnan(row.ATR):
# die ersten candles für die es kein ATR gibt ignorieren wir
# (diese werden sowieso dank startup_candle_count entfernt)
continue
else:
# Wenn der Eintrag älter als timeperiod ist, übernehmen wir den ATR Wert als brick_size
brick_size = row.ATR
# Aktuelle Candle Werte
close = row.close
date = row.date
if prev_brick is None:
trend = True
prev_brick = [
date,
close - brick_size,
close,
close - brick_size,
close,
trend,
False,
False,
False,
date,
NEW_BRICK,
]
prev2_trend = prev_brick[PREV_TREND_IDX]
prev3_trend = prev_brick[PREV2_TREND_IDX]
data.append(prev_brick)
continue
prev_date = prev_brick[DATE_IDX]
prev_close = prev_brick[CLOSE_IDX]
prev_trend = prev_brick[TREND_IDX]
new_brick = None
trend = prev_trend
# Anzahl der neuen Bricks (wir stapeln alle Bricks auf eine Candle) (Negativ heißt down trend)
bricks = int(np.nan_to_num((close - prev_close) / brick_size))
# Wenn es ein Up-Trend war:
if trend and bricks >= 1:
# Ertelle Up-Trend Bricks für diese Candle
# Wir stabeln bricks auf einer Candle
new_brick = [
date,
prev_close,
prev_close + bricks * brick_size,
prev_close,
prev_close + bricks * brick_size,
trend,
prev_trend,
prev2_trend,
prev3_trend,
prev_date,
NEW_BRICK,
]
elif trend and bricks <= -2:
# Ertelle Down-Trend Bricks für diese Candle
# Es handelt sich um ein wechsel von up- zu down-trend
trend = not trend
# Wir stabeln bricks auf einer Candle
# wir erstellen ein Brick weniger, da bei einem Trendwechsel nur ein Balken die spitze markiert
new_brick = [
date,
prev_close - brick_size,
prev_close - brick_size,
prev_close - abs(bricks) * brick_size,
prev_close - abs(bricks) * brick_size,
trend,
prev_trend,
prev2_trend,
prev3_trend,
prev_date,
NEW_BRICK,
]
# Wenn es Down-Trend war
elif not trend and bricks <= -1:
# Ertelle Down-Trend Bricks für diese Candle
# Wir stabeln bricks auf einer Candle
new_brick = [
date,
prev_close,
prev_close,
prev_close - abs(bricks) * brick_size,
prev_close - abs(bricks) * brick_size,
trend,
prev_trend,
prev2_trend,
prev3_trend,
prev_date,
NEW_BRICK,
]
elif not trend and bricks >= 2:
# Ertelle Up-Trend Bricks für diese Candle
# Es handelt sich um ein wechsel von down- zu up-trend
trend = not trend
# Wir stabeln bricks auf einer Candle
# wir erstellen ein Brick weniger, da bei einem Trendwechsel nur ein Balken die spitze markiert
new_brick = [
date,
prev_close + brick_size,
prev_close + bricks * brick_size,
prev_close + brick_size,
prev_close + bricks * brick_size,
trend,
prev_trend,
prev2_trend,
prev3_trend,
prev_date,
NEW_BRICK,
]
else:
data.append(
[
date,
prev_brick[1],
prev_brick[2],
prev_brick[3],
prev_brick[4],
prev_brick[5],
prev_brick[6],
prev_brick[7],
prev_brick[8],
prev_brick[9],
COPIED_BRICK,
]
)
if new_brick is not None:
# Wir fügen den Brick zu unser Zeitlinie hinzu
data.append(new_brick)
# Speicher referenz zu Brick für die Berechnung des nächsten Bricks
prev2_trend = prev_brick[PREV_TREND_IDX]
prev3_trend = prev_brick[PREV2_TREND_IDX]
prev_brick = new_brick
renko_chart = pd.DataFrame(data=data, columns=renko_columns)
# Mit how='right' entfernen wir alle Candles für die wir keinen Renko-Brick haben
return dataframe.merge(renko_chart, how='right', on='date')
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Buy Signale erzeugen wir:
- Ab 3 Bricks nach oben
"""
dataframe.loc[
((dataframe['prev2_trend'] == True) & (dataframe['prev_trend'] == True) & (dataframe['trend'] == True)),
'buy',
] = 1
return dataframe
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Sell Signale erzeugen wir:
- Ab 3 Bricks nach unten
"""
dataframe.loc[
((dataframe['prev2_trend'] == False) & (dataframe['prev_trend'] == False) & (dataframe['trend'] == False)),
'sell',
] = 1
return dataframe
Very sad this account and blobs deleted
@iguy0 do you have shared files in here https://github.com/freqtrade/freqtrade-strategies/issues/59#issuecomment-468218971