Skip to content

Commit 9111e10

Browse files
update(candlestick-basic): seaborn — comprehensive quality review
Comprehensive quality review of seaborn implementation for candlestick-basic.
1 parent f543569 commit 9111e10

2 files changed

Lines changed: 82 additions & 81 deletions

File tree

Lines changed: 77 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,112 @@
11
""" pyplots.ai
22
candlestick-basic: Basic Candlestick Chart
3-
Library: seaborn 0.13.2 | Python 3.13.11
4-
Quality: 90/100 | Created: 2025-12-23
3+
Library: seaborn 0.13.2 | Python 3.14.3
4+
Quality: /100 | Updated: 2026-02-24
55
"""
66

77
import matplotlib.pyplot as plt
88
import numpy as np
99
import pandas as pd
1010
import seaborn as sns
11-
from matplotlib.patches import Patch
12-
13-
14-
# Set seaborn style
15-
sns.set_theme(style="whitegrid")
11+
from matplotlib.collections import PatchCollection
12+
from matplotlib.patches import Patch, Rectangle
13+
14+
15+
# Seaborn theme and context
16+
sns.set_theme(
17+
style="whitegrid",
18+
rc={
19+
"axes.facecolor": "#f8f9fa",
20+
"figure.facecolor": "white",
21+
"grid.color": "#dee2e6",
22+
"text.color": "#212529",
23+
"axes.labelcolor": "#495057",
24+
"xtick.color": "#495057",
25+
"ytick.color": "#495057",
26+
},
27+
)
1628
sns.set_context("talk", font_scale=1.2)
29+
up_fill, up_edge = "#2978b5", "#1b5a8a"
30+
down_fill, down_edge = "#e07a3a", "#b35a1f"
1731

18-
# Data - 30 days of simulated stock OHLC data
32+
# Data - 30 trading days with rally then selloff pattern
1933
np.random.seed(42)
2034
n_days = 30
21-
dates = pd.date_range("2024-01-02", periods=n_days, freq="B") # Business days
22-
23-
# Generate realistic OHLC data
24-
price = 150.0
35+
dates = pd.date_range("2024-01-02", periods=n_days, freq="B")
36+
37+
price = 145.0
38+
drift = np.concatenate(
39+
[
40+
np.linspace(0.4, 0.8, 12), # Uptrend phase
41+
np.linspace(-0.1, -0.6, 18), # Reversal and selloff
42+
]
43+
)
2544
opens, highs, lows, closes = [], [], [], []
26-
27-
for _ in range(n_days):
28-
# Daily volatility
29-
change = np.random.randn() * 3
30-
daily_range = abs(np.random.randn()) * 2 + 1
31-
45+
for i in range(n_days):
46+
change = drift[i] + np.random.randn() * 2.5
47+
volatility = abs(np.random.randn()) * 1.5 + 0.8
3248
open_price = price
3349
close_price = price + change
34-
35-
# High and low include the open/close range plus some extra
36-
high_price = max(open_price, close_price) + abs(np.random.randn()) * daily_range
37-
low_price = min(open_price, close_price) - abs(np.random.randn()) * daily_range
38-
50+
high_price = max(open_price, close_price) + abs(np.random.randn()) * volatility
51+
low_price = min(open_price, close_price) - abs(np.random.randn()) * volatility
3952
opens.append(open_price)
4053
highs.append(high_price)
4154
lows.append(low_price)
4255
closes.append(close_price)
43-
4456
price = close_price
4557

4658
df = pd.DataFrame({"date": dates, "open": opens, "high": highs, "low": lows, "close": closes})
47-
48-
# Determine bullish (up) vs bearish (down) days - use colorblind-safe colors
4959
df["bullish"] = df["close"] >= df["open"]
50-
# Colorblind-safe: blue for up, orange for down
51-
color_up = "#1f77b4" # Blue for bullish
52-
color_down = "#ff7f0e" # Orange for bearish
53-
df["color"] = df["bullish"].map({True: color_up, False: color_down})
54-
df["x"] = range(len(df))
60+
df["x"] = range(n_days)
5561

56-
# Create plot
62+
# Plot
5763
fig, ax = plt.subplots(figsize=(16, 9))
5864

59-
# Draw candlesticks manually with seaborn styling applied
60-
body_width = 0.6
65+
# Wicks
66+
wick_colors = [up_edge if b else down_edge for b in df["bullish"]]
67+
ax.vlines(df["x"], df["low"], df["high"], colors=wick_colors, linewidth=1.5)
6168

69+
# Candle bodies via PatchCollection
70+
body_width = 0.6
71+
rects, fcolors, ecolors = [], [], []
6272
for _, row in df.iterrows():
63-
x = row["x"]
64-
color = row["color"]
65-
66-
# Draw wick (high-low line) using seaborn's lineplot via dataframe
67-
wick_data = pd.DataFrame({"x": [x, x], "price": [row["low"], row["high"]]})
68-
sns.lineplot(data=wick_data, x="x", y="price", ax=ax, color=color, linewidth=2, legend=False)
69-
70-
# Draw candle body using matplotlib Rectangle for precise control
71-
body_bottom = min(row["open"], row["close"])
72-
body_height = abs(row["close"] - row["open"])
73-
# Ensure minimum body height for doji candles
74-
if body_height < 0.2:
75-
body_height = 0.2
76-
body_bottom = (row["open"] + row["close"]) / 2 - 0.1
77-
78-
rect = plt.Rectangle(
79-
(x - body_width / 2, body_bottom), body_width, body_height, facecolor=color, edgecolor=color, linewidth=1
80-
)
81-
ax.add_patch(rect)
82-
83-
# Set x-axis to show dates
84-
tick_positions = range(0, n_days, 5) # Show every 5th date
85-
tick_labels = [df["date"].iloc[i].strftime("%b %d") for i in tick_positions]
86-
ax.set_xticks(tick_positions)
87-
ax.set_xticklabels(tick_labels, rotation=0)
88-
89-
# Style the plot
73+
bottom = min(row["open"], row["close"])
74+
height = max(abs(row["close"] - row["open"]), 0.15)
75+
if abs(row["close"] - row["open"]) < 0.15:
76+
bottom = (row["open"] + row["close"]) / 2 - 0.075
77+
rects.append(Rectangle((row["x"] - body_width / 2, bottom), body_width, height))
78+
fcolors.append(up_fill if row["bullish"] else down_fill)
79+
ecolors.append(up_edge if row["bullish"] else down_edge)
80+
81+
bodies = PatchCollection(rects, facecolors=fcolors, edgecolors=ecolors, linewidths=0.8)
82+
ax.add_collection(bodies)
83+
84+
# X-axis date labels
85+
tick_idx = range(0, n_days, 5)
86+
ax.set_xticks(list(tick_idx))
87+
ax.set_xticklabels([dates[i].strftime("%b %d") for i in tick_idx])
88+
89+
# Style
9090
ax.set_xlabel("Date", fontsize=20)
9191
ax.set_ylabel("Price ($)", fontsize=20)
92-
ax.set_title("candlestick-basic · seaborn · pyplots.ai", fontsize=24)
93-
ax.tick_params(axis="both", labelsize=16)
94-
95-
# Add legend with colorblind-safe colors - positioned outside data area
96-
legend_elements = [Patch(facecolor=color_up, label="Bullish (Up)"), Patch(facecolor=color_down, label="Bearish (Down)")]
97-
ax.legend(handles=legend_elements, fontsize=14, loc="upper right", bbox_to_anchor=(0.99, 0.99))
98-
99-
# Subtle grid
100-
ax.grid(True, alpha=0.3, linestyle="--", axis="y")
92+
ax.set_title("candlestick-basic \u00b7 seaborn \u00b7 pyplots.ai", fontsize=24, fontweight="medium", pad=16)
93+
ax.tick_params(axis="both", labelsize=16, length=0)
94+
sns.despine(ax=ax)
95+
ax.yaxis.grid(True, alpha=0.15, linewidth=0.8)
96+
ax.xaxis.grid(False)
10197
ax.set_axisbelow(True)
10298

103-
# Set axis limits
104-
ax.set_xlim(-0.5, n_days - 0.5)
105-
y_min = df["low"].min()
106-
y_max = df["high"].max()
107-
y_padding = (y_max - y_min) * 0.1
108-
ax.set_ylim(y_min - y_padding, y_max + y_padding)
99+
# Legend
100+
legend_handles = [
101+
Patch(facecolor=up_fill, edgecolor=up_edge, label="Bullish"),
102+
Patch(facecolor=down_fill, edgecolor=down_edge, label="Bearish"),
103+
]
104+
ax.legend(handles=legend_handles, fontsize=14, loc="upper left", framealpha=0.9, edgecolor="#dee2e6")
105+
106+
# Axis limits
107+
ax.set_xlim(-0.8, n_days - 0.2)
108+
y_pad = (df["high"].max() - df["low"].min()) * 0.08
109+
ax.set_ylim(df["low"].min() - y_pad, df["high"].max() + y_pad)
109110

110111
plt.tight_layout()
111112
plt.savefig("plot.png", dpi=300, bbox_inches="tight")

plots/candlestick-basic/metadata/seaborn.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
library: seaborn
22
specification_id: candlestick-basic
33
created: '2025-12-23T10:00:56Z'
4-
updated: '2025-12-23T10:22:22Z'
5-
generated_by: claude-opus-4-5-20251101
4+
updated: '2026-02-24T20:50:21+00:00'
5+
generated_by: claude-opus-4-6
66
workflow_run: 20457529450
77
issue: 0
8-
python_version: 3.13.11
9-
library_version: 0.13.2
8+
python_version: "3.14.3"
9+
library_version: "0.13.2"
1010
preview_url: https://storage.googleapis.com/pyplots-images/plots/candlestick-basic/seaborn/plot.png
1111
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/candlestick-basic/seaborn/plot_thumb.png
1212
preview_html: null
13-
quality_score: 90
13+
quality_score: null
1414
impl_tags:
1515
dependencies: []
1616
techniques:

0 commit comments

Comments
 (0)