Skip to content

Commit 6d20a56

Browse files
feat(letsplot): implement renko-basic (#3331)
## Implementation: `renko-basic` - letsplot Implements the **letsplot** version of `renko-basic`. **File:** `plots/renko-basic/implementations/letsplot.py` **Parent Issue:** #3294 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20823195596)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 529dd3b commit 6d20a56

2 files changed

Lines changed: 123 additions & 0 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
"""pyplots.ai
2+
renko-basic: Basic Renko Chart
3+
Library: lets-plot | Python 3.13
4+
Quality: pending | Created: 2026-01-08
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from lets_plot import *
10+
11+
12+
LetsPlot.setup_html()
13+
14+
# Generate sample price data (simulating stock prices)
15+
np.random.seed(42)
16+
n_days = 200
17+
dates = pd.date_range("2024-01-01", periods=n_days, freq="D")
18+
# Simulate price with random walk
19+
returns = np.random.normal(0.001, 0.02, n_days)
20+
prices = 100 * np.cumprod(1 + returns)
21+
22+
# Renko chart parameters
23+
brick_size = 2.0 # $2 brick size
24+
25+
# Calculate Renko bricks
26+
bricks = []
27+
current_price = prices[0]
28+
brick_start = (current_price // brick_size) * brick_size
29+
direction = None # 1 for up, -1 for down
30+
31+
for price in prices:
32+
while True:
33+
if direction is None:
34+
# Initial direction
35+
if price >= brick_start + brick_size:
36+
bricks.append({"bottom": brick_start, "top": brick_start + brick_size, "direction": 1})
37+
brick_start += brick_size
38+
direction = 1
39+
elif price <= brick_start - brick_size:
40+
bricks.append({"bottom": brick_start - brick_size, "top": brick_start, "direction": -1})
41+
brick_start -= brick_size
42+
direction = -1
43+
else:
44+
break
45+
elif direction == 1:
46+
# Currently in uptrend
47+
if price >= brick_start + brick_size:
48+
bricks.append({"bottom": brick_start, "top": brick_start + brick_size, "direction": 1})
49+
brick_start += brick_size
50+
elif price <= brick_start - 2 * brick_size:
51+
# Reversal requires 2 bricks in opposite direction
52+
brick_start -= brick_size
53+
bricks.append({"bottom": brick_start - brick_size, "top": brick_start, "direction": -1})
54+
brick_start -= brick_size
55+
direction = -1
56+
else:
57+
break
58+
else:
59+
# Currently in downtrend
60+
if price <= brick_start - brick_size:
61+
bricks.append({"bottom": brick_start - brick_size, "top": brick_start, "direction": -1})
62+
brick_start -= brick_size
63+
elif price >= brick_start + 2 * brick_size:
64+
# Reversal requires 2 bricks in opposite direction
65+
brick_start += brick_size
66+
bricks.append({"bottom": brick_start, "top": brick_start + brick_size, "direction": 1})
67+
brick_start += brick_size
68+
direction = 1
69+
else:
70+
break
71+
72+
# Create DataFrame for plotting
73+
brick_df = pd.DataFrame(bricks)
74+
brick_df["x"] = range(len(brick_df))
75+
brick_df["color"] = brick_df["direction"].map({1: "Bullish", -1: "Bearish"})
76+
# Add small gap between bricks
77+
gap = 0.1
78+
brick_df["xmin"] = brick_df["x"] - 0.4 + gap / 2
79+
brick_df["xmax"] = brick_df["x"] + 0.4 - gap / 2
80+
81+
# Create the plot
82+
plot = (
83+
ggplot(brick_df)
84+
+ geom_rect(aes(xmin="xmin", xmax="xmax", ymin="bottom", ymax="top", fill="color"), color="white", size=0.5)
85+
+ scale_fill_manual(values={"Bullish": "#22C55E", "Bearish": "#EF4444"})
86+
+ labs(title="renko-basic · letsplot · pyplots.ai", x="Brick Index", y="Price ($)", fill="Direction")
87+
+ theme_minimal()
88+
+ theme(
89+
plot_title=element_text(size=24),
90+
axis_title=element_text(size=20),
91+
axis_text=element_text(size=16),
92+
legend_text=element_text(size=16),
93+
legend_title=element_text(size=18),
94+
panel_grid_major=element_line(color="#E5E5E5", size=0.5),
95+
panel_grid_minor=element_blank(),
96+
)
97+
+ ggsize(1600, 900)
98+
)
99+
100+
# Save as PNG (scale 3x for 4800x2700)
101+
ggsave(plot, "plot.png", path=".", scale=3)
102+
103+
# Save as HTML for interactivity
104+
ggsave(plot, "plot.html", path=".")
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Per-library metadata for letsplot implementation of renko-basic
2+
# Auto-generated by impl-generate.yml
3+
4+
library: letsplot
5+
specification_id: renko-basic
6+
created: '2026-01-08T16:12:05Z'
7+
updated: '2026-01-08T16:12:05Z'
8+
generated_by: claude-opus-4-5-20251101
9+
workflow_run: 20823195596
10+
issue: 3294
11+
python_version: 3.13.11
12+
library_version: 4.8.2
13+
preview_url: https://storage.googleapis.com/pyplots-images/plots/renko-basic/letsplot/plot.png
14+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/renko-basic/letsplot/plot_thumb.png
15+
preview_html: https://storage.googleapis.com/pyplots-images/plots/renko-basic/letsplot/plot.html
16+
quality_score: null
17+
review:
18+
strengths: []
19+
weaknesses: []

0 commit comments

Comments
 (0)