I Built the Best Renko Builder in Python 3 Years Ago

Algo Forex Lab By · · 8 min read
renko-builder-python

Three years ago, when I started working on a Renko candles trading strategy, I tested almost every Renko builder available in Python.

And honestly?
None of them were efficient enough.

They were either too slow, inaccurate in live markets, or unreliable for serious backtesting. That’s when I decided to build something better — something that could actually work in real market conditions and deliver accurate backtests.

But there was a problem.

Python wasn’t fast enough for what I needed. And at the time, the only solid integration available was the MetaTrader 5 (MT5) library for Python.

So I made a bold decision.

I turned Python into a bridge — and used JavaScript to build the Renko engine itself.

And the result?

It was amazing.

Fast. Accurate. Reliable.
Exactly what I had been looking for.

Why Renko Candles?

Because Renko candles are based on price — not time.

That changes everything.

They filter out market noise and focus purely on meaningful price movement. And with the custom options I added to my Renko builder, you can even further denoise the bricks, making them significantly more efficient for strategy development and live execution.

Below, you’ll find two versions of the Renko builder:

  • âś… A Live version
  • âś… An Offline/backtesting version

Both can be used as standalone tools or connected directly to MetaTrader 5.

for disable noise detection set value of “noisecount” to 0

Python Code

Offline version :

def renko(datep1, closep1, openp1, highp1, lowp1, noisecount, renkosize):
    candlec = noisecount
    cc1, oo1, ll1, hh1, dd1 = [round(closep1[0], 5)], [round(openp1[0], 5)], [round(lowp1[0], 5)], [
        round(highp1[0], 5)], [
                                  datep1[0]]
    for l in range(1, len(closep1)):
        if closep1[l] > openp1[l] and cc1[-1] < oo1[-1]:
            if closep1[l] - ((candlec + 1) * renkosize) >= cc1[-1]:
                calc = (closep1[l] - oo1[-1]) / renkosize
                for s1 in range(0, round(calc)):
                    if s1 == 0:
                        hh1.append(round(oo1[-1] + renkosize, 5))
                        ll1.append(round(oo1[-1], 5))
                        cc1.append(round(oo1[-1] + renkosize, 5))
                        oo1.append(round(oo1[-1], 5))
                        dd1.append(dd1[-1] + 1)
                    if s1 > 0:
                        oo1.append(round(cc1[-1], 5))
                        hh1.append(round(cc1[-1] + renkosize, 5))
                        ll1.append(round(cc1[-1], 5))
                        cc1.append(round(cc1[-1] + renkosize, 5))
                        dd1.append(dd1[-1] + 1)
        elif closep1[l] < openp1[l] and cc1[-1] > oo1[-1]:
            if closep1[l] + ((candlec + 1) * renkosize) <= cc1[-1]:
                calc = (oo1[-1] - closep1[l]) / renkosize
                if calc >= 2:
                    for s in range(0, round(calc)):
                        if s == 0:
                            hh1.append(round(oo1[-1], 5))
                            ll1.append(round(oo1[-1] - renkosize, 5))
                            cc1.append(round(oo1[-1] - renkosize, 5))
                            oo1.append(round(oo1[-1], 5))
                            dd1.append(dd1[-1] + 1)
                        else:
                            oo1.append(round(cc1[-1], 5))
                            hh1.append(round(cc1[-1], 5))
                            ll1.append(round(cc1[-1] - renkosize, 5))
                            cc1.append(round(cc1[-1] - renkosize, 5))
                            dd1.append(dd1[-1] + 1)
        elif closep1[l] > openp1[l] and cc1[-1] > oo1[-1] and closep1[l] > cc1[-1]:
            oo1.append(round(cc1[-1], 5))
            hh1.append(round(cc1[-1] + renkosize, 5))
            ll1.append(round(cc1[-1], 5))
            cc1.append(round(cc1[-1] + renkosize, 5))
            dd1.append(dd1[-1] + 1)
        elif closep1[l] < openp1[l] and cc1[-1] < oo1[-1] and closep1[l] < cc1[-1]:
            oo1.append(round(cc1[-1], 5))
            hh1.append(round(cc1[-1], 5))
            ll1.append(round(cc1[-1] - renkosize, 5))
            cc1.append(round(cc1[-1] - renkosize, 5))
            dd1.append(dd1[-1] + 1)
    return oo1, hh1, ll1, cc1, dd1
Python
Expand

Online Version :

def noisecandlebuilder():
    global renkosize, oo, hh, ll, cc, dd, noisecount, datep, closep, openp, highp, lowp
    candlec = noisecount

    if closep[-1] > openp[-1] and cc[-1] < oo[-1]:
        if closep[-1] - ((candlec + 1) * renkosize) >= cc[-1]:
            calc = (closep[-1] - oo[-1]) / renkosize
            for s1 in range(0, round(calc)):
                if s1 == 0:
                    dd.append(dd[-1] + 1)
                    hh.append(round(oo[-1] + renkosize, 5))
                    ll.append(round(oo[-1], 5))
                    cc.append(round(oo[-1] + renkosize, 5))
                    oo.append(round(oo[-1], 5))
                if s1 > 0:
                    dd.append(dd[-1] + 1)
                    oo.append(round(cc[-1], 5))
                    hh.append(round(cc[-1] + renkosize, 5))
                    ll.append(round(cc[-1], 5))
                    cc.append(round(cc[-1] + renkosize, 5))
                if len(dd) > 500:
                    hh.pop(0)
                    cc.pop(0)
                    ll.pop(0)
                    oo.pop(0)
                    dd.pop(0)
    elif closep[-1] < openp[-1] and cc[-1] > oo[-1]:
        if closep[-1] + ((candlec + 1) * renkosize) <= cc[-1]:
            calc = (oo[-1] - closep[-1]) / renkosize
            if calc >= 2:
                for s in range(0, round(calc)):
                    if s == 0:
                        dd.append(dd[-1] + 1)
                        hh.append(round(oo[-1], 5))
                        ll.append(round(oo[-1] - renkosize, 5))
                        cc.append(round(oo[-1] - renkosize, 5))
                        oo.append(round(oo[-1], 5))
                    else:
                        dd.append(dd[-1] + 1)
                        oo.append(round(cc[-1], 5))
                        hh.append(round(cc[-1], 5))
                        ll.append(round(cc[-1] - renkosize, 5))
                        cc.append(round(cc[-1] - renkosize, 5))
                    if len(dd) > 500:
                        hh.pop(0)
                        cc.pop(0)
                        ll.pop(0)
                        oo.pop(0)
                        dd.pop(0)
    elif closep[-1] > openp[-1] and cc[-1] > oo[-1] and closep[-1] > cc[-1]:
        calc = (closep[-1] - cc[-1]) / renkosize
        if round(calc) >= 2:
            for s1 in range(0, round(calc)):
                dd.append(dd[-1] + 1)
                oo.append(round(cc[-1], 5))
                hh.append(round(cc[-1] + renkosize, 5))
                ll.append(round(cc[-1], 5))
                cc.append(round(cc[-1] + renkosize, 5))
                if len(dd) > 500:
                    hh.pop(0)
                    cc.pop(0)
                    ll.pop(0)
                    oo.pop(0)
                    dd.pop(0)
        elif round(calc) == 1:
            dd.append(dd[-1] + 1)
            oo.append(round(cc[-1], 5))
            hh.append(round(cc[-1] + renkosize, 5))
            ll.append(round(cc[-1], 5))
            cc.append(round(cc[-1] + renkosize, 5))
            if len(dd) > 500:
                hh.pop(0)
                cc.pop(0)
                ll.pop(0)
                oo.pop(0)
                dd.pop(0)
    elif closep[-1] < openp[-1] and cc[-1] < oo[-1] and closep[-1] < cc[-1]:
        calc = (cc[-1] - closep[-1]) / renkosize
        if round(calc) >= 2:
            for s in range(0, round(calc)):
                dd.append(dd[-1] + 1)
                oo.append(round(cc[-1], 5))
                hh.append(round(cc[-1], 5))
                ll.append(round(cc[-1] - renkosize, 5))
                cc.append(round(cc[-1] - renkosize, 5))
                if len(dd) > 500:
                    hh.pop(0)
                    cc.pop(0)
                    ll.pop(0)
                    oo.pop(0)
                    dd.pop(0)
        elif round(calc) == 1:
            dd.append(dd[-1] + 1)
            oo.append(round(cc[-1], 5))
            hh.append(round(cc[-1], 5))
            ll.append(round(cc[-1] - renkosize, 5))
            cc.append(round(cc[-1] - renkosize, 5))
            if len(dd) > 500:
                hh.pop(0)
                cc.pop(0)
                ll.pop(0)
                oo.pop(0)
                dd.pop(0)


def lastpricesaver():
    while True:
        global highp, lowp, closep, openp, datep, symbol, renkosize
        if startdatasaving:
            lst = mt5.symbol_info_tick(symbol).bid
            if closep[-1] > openp[-1]:
                if lst >= round(closep[-1] + renkosize, 5):
                    calc = (lst - round(closep[-1], 5)) / renkosize
                    if calc >= 2:
                        for i in range(0, math.floor(calc)):
                            datep.append(datep[-1] + 1)
                            openp.append(round(closep[-1], 5))
                            highp.append(round(closep[-1] + renkosize, 5))
                            lowp.append(round(closep[-1], 5))
                            closep.append(round(closep[-1] + renkosize, 5))
                            if len(datep) > 410:
                                highp.pop(0)
                                closep.pop(0)
                                lowp.pop(0)
                                openp.pop(0)
                                datep.pop(0)
                    else:
                        datep.append(datep[-1] + 1)
                        openp.append(round(closep[-1], 5))
                        highp.append(round(closep[-1] + renkosize, 5))
                        lowp.append(round(closep[-1], 5))
                        closep.append(round(closep[-1] + renkosize, 5))
                    if len(datep) > 410:
                        highp.pop(0)
                        closep.pop(0)
                        lowp.pop(0)
                        openp.pop(0)
                        datep.pop(0)
                elif lst <= round(openp[-1] - renkosize, 5):
                    calc = (round(openp[-1], 5) - lst) / renkosize
                    if calc >= 2:
                        for i in range(0, math.floor(calc)):
                            if i == 0:
                                datep.append(datep[-1] + 1)
                                highp.append(round(openp[-1], 5))
                                lowp.append(round(openp[-1] - renkosize, 5))
                                closep.append(round(openp[-1] - renkosize, 5))
                                openp.append(round(openp[-1], 5))
                            else:
                                datep.append(datep[-1] + 1)
                                openp.append(round(closep[-1], 5))
                                highp.append(round(closep[-1], 5))
                                lowp.append(round(closep[-1] - renkosize, 5))
                                closep.append(round(closep[-1] - renkosize, 5))
                            if len(datep) > 410:
                                highp.pop(0)
                                closep.pop(0)
                                lowp.pop(0)
                                openp.pop(0)
                                datep.pop(0)

                    else:
                        datep.append(datep[-1] + 1)
                        highp.append(round(openp[-1], 5))
                        lowp.append(round(openp[-1] - renkosize, 5))
                        closep.append(round(openp[-1] - renkosize, 5))
                        openp.append(round(openp[-1], 5))
                    if len(datep) > 410:
                        highp.pop(0)
                        closep.pop(0)
                        lowp.pop(0)
                        openp.pop(0)
                        datep.pop(0)
            elif closep[-1] < openp[-1]:
                if lst >= round(openp[-1] + renkosize, 5):
                    calc = (lst - round(openp[-1], 5)) / renkosize
                    if calc >= 2:
                        for i in range(0, math.floor(calc)):
                            if i == 0:
                                datep.append(datep[-1] + 1)
                                highp.append(round(openp[-1] + renkosize, 5))
                                lowp.append(round(openp[-1], 5))
                                closep.append(round(openp[-1] + renkosize, 5))
                                openp.append(round(openp[-1], 5))
                            else:
                                datep.append(datep[-1] + 1)
                                openp.append(round(closep[-1], 5))
                                highp.append(round(closep[-1] + renkosize, 5))
                                lowp.append(round(closep[-1], 5))
                                closep.append(round(closep[-1] + renkosize, 5))
                            if len(datep) > 410:
                                highp.pop(0)
                                closep.pop(0)
                                lowp.pop(0)
                                openp.pop(0)
                                datep.pop(0)
                    else:
                        datep.append(datep[-1] + 1)
                        highp.append(round(openp[-1] + renkosize, 5))
                        lowp.append(round(openp[-1], 5))
                        closep.append(round(openp[-1] + renkosize, 5))
                        openp.append(round(openp[-1], 5))
                    if len(datep) > 410:
                        highp.pop(0)
                        closep.pop(0)
                        lowp.pop(0)
                        openp.pop(0)
                        datep.pop(0)
                elif lst <= round(closep[-1] - renkosize, 5):
                    calc = (round(closep[-1], 5) - lst) / renkosize
                    if calc >= 2:
                        for i in range(0, math.floor(calc)):
                            datep.append(datep[-1] + 1)
                            openp.append(round(closep[-1], 5))
                            highp.append(round(closep[-1], 5))
                            lowp.append(round(closep[-1] - renkosize, 5))
                            closep.append(round(closep[-1] - renkosize, 5))
                            if len(datep) > 410:
                                highp.pop(0)
                                closep.pop(0)
                                lowp.pop(0)
                                openp.pop(0)
                                datep.pop(0)
                    else:
                        datep.append(datep[-1] + 1)
                        openp.append(round(closep[-1], 5))
                        highp.append(round(closep[-1], 5))
                        lowp.append(round(closep[-1] - renkosize, 5))
                        closep.append(round(closep[-1] - renkosize, 5))
                    if len(datep) > 410:
                        highp.pop(0)
                        closep.pop(0)
                        lowp.pop(0)
                        openp.pop(0)
                        datep.pop(0)
            noisecandlebuilder()
Python
Expand
Hassan Safari Hassan Safari
Hassan Safari is a seasoned forex trader and data scientist with 7 years of experience in market analysis, risk management, and developing AI-driven trading tools.