
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, dd1PythonOnline 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