Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Renko bars/chart [enhancement] #59

Open
iguy0 opened this issue Jul 8, 2018 · 7 comments
Open

Renko bars/chart [enhancement] #59

iguy0 opened this issue Jul 8, 2018 · 7 comments

Comments

@iguy0
Copy link

iguy0 commented Jul 8, 2018

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 ?

@ghost
Copy link

ghost commented Feb 28, 2019

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.

@xmatthias
Copy link
Member

@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...

@xmatthias xmatthias transferred this issue from freqtrade/freqtrade Dec 25, 2019
@xmatthias
Copy link
Member

Transferring this issue to freqtrade, since it's about a renko strategy more than anything else.

@hroff-1902
Copy link
Member

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)

@C0D3D3V
Copy link

C0D3D3V commented Jan 2, 2022

@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

@HeyMrRobot
Copy link

Very sad this account and blobs deleted

@HeyMrRobot
Copy link

@iguy0 do you have shared files in here #59 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants