|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | ohlc-bar: OHLC Bar Chart |
3 | | -Library: matplotlib 3.10.8 | Python 3.13.11 |
4 | | -Quality: 93/100 | Created: 2026-01-08 |
| 3 | +Library: matplotlib 3.10.9 | Python 3.13.13 |
| 4 | +Quality: 91/100 | Updated: 2026-05-17 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | + |
7 | 9 | import matplotlib.dates as mdates |
8 | 10 | import matplotlib.pyplot as plt |
9 | 11 | import numpy as np |
10 | 12 | import pandas as pd |
11 | 13 | from matplotlib.lines import Line2D |
12 | 14 |
|
13 | 15 |
|
| 16 | +# Theme tokens |
| 17 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 18 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 19 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 20 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 21 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 22 | + |
| 23 | +# Okabe-Ito palette |
| 24 | +COLOR_UP = "#009E73" # Brand green for up bars |
| 25 | +COLOR_DOWN = "#D55E00" # Vermillion for down bars |
| 26 | + |
14 | 27 | # Data - Generate 45 trading days of synthetic stock OHLC data |
15 | 28 | np.random.seed(42) |
16 | 29 | n_days = 45 |
|
40 | 53 | df = pd.DataFrame({"date": dates, "open": open_prices, "high": high_prices, "low": low_prices, "close": close_prices}) |
41 | 54 |
|
42 | 55 | # Plot |
43 | | -fig, ax = plt.subplots(figsize=(16, 9)) |
| 56 | +fig, ax = plt.subplots(figsize=(16, 9), facecolor=PAGE_BG) |
| 57 | +ax.set_facecolor(PAGE_BG) |
44 | 58 |
|
45 | 59 | # Draw OHLC bars |
46 | 60 | tick_width = 0.4 # Width of open/close ticks in days |
|
50 | 64 | date_num = mdates.date2num(row["date"]) |
51 | 65 |
|
52 | 66 | # Determine color based on price direction |
53 | | - if row["close"] >= row["open"]: |
54 | | - color = "#306998" # Python Blue for up bars |
55 | | - else: |
56 | | - color = "#D62728" # Red for down bars |
| 67 | + color = COLOR_UP if row["close"] >= row["open"] else COLOR_DOWN |
57 | 68 |
|
58 | 69 | # Draw high-low vertical line |
59 | 70 | ax.plot([date_num, date_num], [row["low"], row["high"]], color=color, linewidth=line_width, solid_capstyle="round") |
|
76 | 87 | solid_capstyle="butt", |
77 | 88 | ) |
78 | 89 |
|
79 | | -# Styling |
80 | | -ax.set_xlabel("Date", fontsize=20) |
81 | | -ax.set_ylabel("Price (USD)", fontsize=20) |
82 | | -ax.set_title("ohlc-bar · matplotlib · pyplots.ai", fontsize=24) |
83 | | -ax.tick_params(axis="both", labelsize=16) |
| 90 | +# Style |
| 91 | +ax.set_xlabel("Date", fontsize=20, color=INK) |
| 92 | +ax.set_ylabel("Price (USD)", fontsize=20, color=INK) |
| 93 | +ax.set_title("ohlc-bar · matplotlib · anyplot.ai", fontsize=24, fontweight="medium", color=INK) |
| 94 | +ax.tick_params(axis="both", labelsize=16, colors=INK_SOFT) |
84 | 95 |
|
85 | 96 | # Format x-axis dates |
86 | 97 | ax.xaxis.set_major_locator(mdates.WeekdayLocator(byweekday=mdates.MONDAY)) |
87 | 98 | ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %d")) |
88 | 99 | ax.xaxis.set_minor_locator(mdates.DayLocator()) |
89 | 100 | fig.autofmt_xdate(rotation=45) |
90 | 101 |
|
| 102 | +# Spine styling |
| 103 | +for spine in ("top", "right"): |
| 104 | + ax.spines[spine].set_visible(False) |
| 105 | +for spine in ("left", "bottom"): |
| 106 | + ax.spines[spine].set_color(INK_SOFT) |
| 107 | + |
91 | 108 | # Grid for reading price levels |
92 | | -ax.grid(True, alpha=0.3, linestyle="--", axis="y") |
93 | | -ax.grid(True, alpha=0.15, linestyle="--", axis="x") |
| 109 | +ax.yaxis.grid(True, alpha=0.15, linewidth=0.8, color=INK) |
| 110 | +ax.xaxis.grid(True, alpha=0.08, linewidth=0.8, color=INK) |
94 | 111 |
|
95 | 112 | # Add padding to y-axis |
96 | 113 | y_min, y_max = ax.get_ylim() |
|
99 | 116 |
|
100 | 117 | # Add legend for up/down bars |
101 | 118 | legend_elements = [ |
102 | | - Line2D([0], [0], color="#306998", linewidth=3, label="Up (Close ≥ Open)"), |
103 | | - Line2D([0], [0], color="#D62728", linewidth=3, label="Down (Close < Open)"), |
| 119 | + Line2D([0], [0], color=COLOR_UP, linewidth=3, label="Up (Close ≥ Open)"), |
| 120 | + Line2D([0], [0], color=COLOR_DOWN, linewidth=3, label="Down (Close < Open)"), |
104 | 121 | ] |
105 | | -ax.legend(handles=legend_elements, fontsize=16, loc="upper left") |
| 122 | +legend = ax.legend(handles=legend_elements, fontsize=16, loc="upper left") |
| 123 | +legend.get_frame().set_facecolor(ELEVATED_BG) |
| 124 | +legend.get_frame().set_edgecolor(INK_SOFT) |
| 125 | +for text in legend.get_texts(): |
| 126 | + text.set_color(INK_SOFT) |
106 | 127 |
|
107 | 128 | plt.tight_layout() |
108 | | -plt.savefig("plot.png", dpi=300, bbox_inches="tight") |
| 129 | +plt.savefig(f"plot-{THEME}.png", dpi=300, bbox_inches="tight", facecolor=PAGE_BG) |
0 commit comments