33 · Matplotlib 实战:股票 K 线图与 ASCII 终端可视化
🔗 知识图谱导航:阅读本文前,建议先掌握《31 · NumPy 实战》中的 ndarray 操作——本文的数据生成和处理都基于 NumPy,Matplotlib 只负责把 NumPy 数组渲染成图表。
运行环境:
pip install matplotlib numpy。ASCII 模式不需要打开图形窗口;完整脚本仍需要安装 NumPy 和 Matplotlib。
极客解析:Matplotlib 的核心是"Figure → Axes → Artist"三层结构:Figure 是画布,Axes 是坐标系,Artist 是线/柱/文字等图形元素。
GridSpec让多个 Axes 按比例布局,是复杂图表的标准做法。
Matplotlib 核心概念
Figure 画布,plt.figure(figsize=(12, 8))
Axes 坐标系,fig.add_subplot() 或 GridSpec
Artist 线条 ax.plot()、柱状 ax.bar()、文字 ax.text()
GridSpec 多子图布局,height_ratios=[3,1] 控制高度比例
步步为营:核心逻辑自适应拆解
这一篇按真实绘图流水线拆成 5 个台阶:先生成股票数据,再计算均线,然后分别输出 PNG 图表和 ASCII 终端图,最后用 CLI 参数统一调度。每个演示都有 Mock 数据和 print() 反馈。
Step 1:用 generate_stock 造出可画图的股价和成交量
痛点与机制:
generate_stock 是图表的数据造景师:它用随机收益率模拟价格走势,再用指数累乘保证价格永远为正。你可以把 close 想成一条股价折线,把 volume 想成每天成交的水位,后面的 Matplotlib 和 ASCII 都只是把这两组数组画出来。
核心源码(逐字来自文末完整源码):
def generate_stock(n: int = 120) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""生成模拟股票 OHLC + 成交量"""
np.random.seed(42)
returns = np.random.randn(n) * 0.015 + 0.0003
close = 100 * np.exp(np.cumsum(returns))
volume = np.random.randint(1_000_000, 5_000_000, n).astype(float)
# 价格上涨时成交量略大
volume += (returns > 0) * np.random.randint(0, 2_000_000, n)
days = np.arange(n)
return days, close, volume
可运行演示(补齐 Mock 数据与 print 反馈):
import numpy as np
def generate_stock(n: int = 120) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""生成模拟股票 OHLC + 成交量"""
np.random.seed(42)
returns = np.random.randn(n) * 0.015 + 0.0003
close = 100 * np.exp(np.cumsum(returns))
volume = np.random.randint(1_000_000, 5_000_000, n).astype(float)
# 价格上涨时成交量略大
volume += (returns > 0) * np.random.randint(0, 2_000_000, n)
days = np.arange(n)
return days, close, volume
days, close, volume = generate_stock(n=8)
print("📦 股票模拟数据已生成")
print("交易日:", days.tolist())
print("收盘价:", close.round(2).tolist())
print("成交量(M):", (volume / 1e6).round(2).tolist())
Step 2:用 cumsum 做移动平均,平滑短期波动
痛点与机制:
移动平均是“把短期噪声熨平”的工具。cumsum 像提前写好流水账,任意窗口总和都可以用后一个累计值减前一个累计值算出来,再除以窗口天数。这样比每天重新数一遍窗口更清楚,也更贴近 NumPy 的向量化思维。
核心源码(逐字来自文末完整源码):
def moving_avg(arr: np.ndarray, w: int) -> np.ndarray:
result = np.full(len(arr), np.nan)
cumsum = np.cumsum(np.insert(arr, 0, 0))
result[w - 1:] = (cumsum[w:] - cumsum[:-w]) / w
return result
可运行演示(补齐 Mock 数据与 print 反馈):
import numpy as np
def moving_avg(arr: np.ndarray, w: int) -> np.ndarray:
result = np.full(len(arr), np.nan)
cumsum = np.cumsum(np.insert(arr, 0, 0))
result[w - 1:] = (cumsum[w:] - cumsum[:-w]) / w
return result
close = np.array([100, 102, 101, 105, 107, 106], dtype=float)
ma3 = moving_avg(close, 3)
print("📈 收盘价:", close.tolist())
print("3日均线:", np.round(ma3, 2).tolist())
print("前两个是 nan:因为还没有凑满3天窗口。")
Step 3:用 mode_chart 生成价格 + 成交量 PNG 图
痛点与机制:
mode_chart 负责把数组变成 PNG 图。Figure 像画纸,Axes 像画纸上的两个坐标区,GridSpec 像版面设计师,把价格图和成交量图按 3:1 排好。成交量颜色用涨跌判断生成,读者一眼能看出“涨的时候量大不大”。
核心源码(逐字来自文末完整源码):
def mode_chart(days: np.ndarray, close: np.ndarray, volume: np.ndarray) -> None:
"""生成 PNG 图表"""
fig = plt.figure(figsize=(12, 8), facecolor="#0d1117")
gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1], hspace=0.05)
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1], sharex=ax1)
# 价格 + 均线
ax1.plot(days, close, color="#58a6ff", linewidth=1.2, label="Close")
ax1.plot(days, moving_avg(close, 10), color="#f0883e",
linewidth=1, linestyle="--", label="MA10")
ax1.plot(days, moving_avg(close, 30), color="#3fb950",
linewidth=1, linestyle="--", label="MA30")
ax1.fill_between(days, close, close.min(), alpha=0.1, color="#58a6ff")
ax1.set_facecolor("#161b22")
ax1.tick_params(colors="gray")
ax1.set_ylabel("Price", color="gray")
ax1.legend(facecolor="#21262d", labelcolor="white", fontsize=9)
ax1.grid(True, alpha=0.2, color="gray")
ax1.set_title("Stock Analysis Chart", color="white", fontsize=13, pad=10)
plt.setp(ax1.get_xticklabels(), visible=False)
# 成交量
colors = ["#3fb950" if c >= p else "#f85149"
for c, p in zip(close[1:], close[:-1])]
colors = ["#3fb950"] + colors
ax2.bar(days, volume / 1e6, color=colors, alpha=0.8)
ax2.set_facecolor("#161b22")
ax2.tick_params(colors="gray")
ax2.set_ylabel("Volume(M)", color="gray")
ax2.set_xlabel("Trading Day", color="gray")
ax2.grid(True, alpha=0.2, color="gray")
out = "stock_chart.png"
fig.savefig(out, dpi=150, bbox_inches="tight", facecolor=fig.get_facecolor())
plt.close(fig)
print(f"[{nexdo_time()}] 📊 图表已保存: {out}")
可运行演示(补齐 Mock 数据与 print 反馈):
import os
import tempfile
import time
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
def nexdo_time() -> str:
return time.strftime("%Y-%m-%d %H:%M:%S")
def generate_stock(n: int = 120) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""生成模拟股票 OHLC + 成交量"""
np.random.seed(42)
returns = np.random.randn(n) * 0.015 + 0.0003
close = 100 * np.exp(np.cumsum(returns))
volume = np.random.randint(1_000_000, 5_000_000, n).astype(float)
# 价格上涨时成交量略大
volume += (returns > 0) * np.random.randint(0, 2_000_000, n)
days = np.arange(n)
return days, close, volume
def moving_avg(arr: np.ndarray, w: int) -> np.ndarray:
result = np.full(len(arr), np.nan)
cumsum = np.cumsum(np.insert(arr, 0, 0))
result[w - 1:] = (cumsum[w:] - cumsum[:-w]) / w
return result
def mode_chart(days: np.ndarray, close: np.ndarray, volume: np.ndarray) -> None:
"""生成 PNG 图表"""
fig = plt.figure(figsize=(12, 8), facecolor="#0d1117")
gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1], hspace=0.05)
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1], sharex=ax1)
# 价格 + 均线
ax1.plot(days, close, color="#58a6ff", linewidth=1.2, label="Close")
ax1.plot(days, moving_avg(close, 10), color="#f0883e",
linewidth=1, linestyle="--", label="MA10")
ax1.plot(days, moving_avg(close, 30), color="#3fb950",
linewidth=1, linestyle="--", label="MA30")
ax1.fill_between(days, close, close.min(), alpha=0.1, color="#58a6ff")
ax1.set_facecolor("#161b22")
ax1.tick_params(colors="gray")
ax1.set_ylabel("Price", color="gray")
ax1.legend(facecolor="#21262d", labelcolor="white", fontsize=9)
ax1.grid(True, alpha=0.2, color="gray")
ax1.set_title("Stock Analysis Chart", color="white", fontsize=13, pad=10)
plt.setp(ax1.get_xticklabels(), visible=False)
# 成交量
colors = ["#3fb950" if c >= p else "#f85149"
for c, p in zip(close[1:], close[:-1])]
colors = ["#3fb950"] + colors
ax2.bar(days, volume / 1e6, color=colors, alpha=0.8)
ax2.set_facecolor("#161b22")
ax2.tick_params(colors="gray")
ax2.set_ylabel("Volume(M)", color="gray")
ax2.set_xlabel("Trading Day", color="gray")
ax2.grid(True, alpha=0.2, color="gray")
out = "stock_chart.png"
fig.savefig(out, dpi=150, bbox_inches="tight", facecolor=fig.get_facecolor())
plt.close(fig)
print(f"[{nexdo_time()}] 📊 图表已保存: {out}")
with tempfile.TemporaryDirectory() as tmp:
old = os.getcwd()
os.chdir(tmp)
try:
days, close, volume = generate_stock(40)
mode_chart(days, close, volume)
print("PNG 文件存在:", os.path.exists("stock_chart.png"))
finally:
os.chdir(old)
Step 4:用 mode_ascii 在终端画股票走势图
痛点与机制:
ASCII 图是给服务器终端准备的备用可视化。它把价格范围切成 12 层,每一层像楼层线:价格高于这一层就画一个 ●。这就像用积木搭走势图,不需要打开窗口,也能快速看趋势。
核心源码(逐字来自文末完整源码):
def mode_ascii(days: np.ndarray, close: np.ndarray, volume: np.ndarray) -> None:
"""终端 ASCII 价格走势预览"""
print(f"\n[{nexdo_time()}] 📈 ASCII 股票走势(最近60日)")
data = close[-60:]
vol = volume[-60:]
n = len(data)
rows, cols = 12, n
lo, hi = data.min(), data.max()
print(f" 价格区间: {lo:.2f} ~ {hi:.2f}")
print(" " + "─" * (cols + 12))
for r in range(rows, 0, -1):
threshold = lo + (hi - lo) * r / rows
row_str = ""
for v in data:
row_str += "●" if v >= threshold else " "
print(f" {threshold:>8.1f} │{row_str}│")
print(" " + " " * 10 + "─" * cols)
# ASCII 成交量柱
print(f"\n 成交量(M)")
max_vol = vol.max()
bar_h = 4
for r in range(bar_h, 0, -1):
row_str = ""
for v in vol:
row_str += "▄" if v >= max_vol * r / bar_h else " "
print(f" {max_vol * r / bar_h / 1e6:>6.1f}M │{row_str}│")
print(" " + "─" * (cols + 12))
可运行演示(补齐 Mock 数据与 print 反馈):
import time
import numpy as np
def nexdo_time() -> str:
return time.strftime("%Y-%m-%d %H:%M:%S")
def generate_stock(n: int = 120) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""生成模拟股票 OHLC + 成交量"""
np.random.seed(42)
returns = np.random.randn(n) * 0.015 + 0.0003
close = 100 * np.exp(np.cumsum(returns))
volume = np.random.randint(1_000_000, 5_000_000, n).astype(float)
# 价格上涨时成交量略大
volume += (returns > 0) * np.random.randint(0, 2_000_000, n)
days = np.arange(n)
return days, close, volume
def mode_ascii(days: np.ndarray, close: np.ndarray, volume: np.ndarray) -> None:
"""终端 ASCII 价格走势预览"""
print(f"\n[{nexdo_time()}] 📈 ASCII 股票走势(最近60日)")
data = close[-60:]
vol = volume[-60:]
n = len(data)
rows, cols = 12, n
lo, hi = data.min(), data.max()
print(f" 价格区间: {lo:.2f} ~ {hi:.2f}")
print(" " + "─" * (cols + 12))
for r in range(rows, 0, -1):
threshold = lo + (hi - lo) * r / rows
row_str = ""
for v in data:
row_str += "●" if v >= threshold else " "
print(f" {threshold:>8.1f} │{row_str}│")
print(" " + " " * 10 + "─" * cols)
# ASCII 成交量柱
print(f"\n 成交量(M)")
max_vol = vol.max()
bar_h = 4
for r in range(bar_h, 0, -1):
row_str = ""
for v in vol:
row_str += "▄" if v >= max_vol * r / bar_h else " "
print(f" {max_vol * r / bar_h / 1e6:>6.1f}M │{row_str}│")
print(" " + "─" * (cols + 12))
days, close, volume = generate_stock(n=30)
mode_ascii(days, close, volume)
终端运行效果:
[2026-04-18 12:46:06] 📈 ASCII 股票走势(最近60日)
价格区间: 84.85 ~ 107.27
────────────────────────────────────────────────────────────────────────
107.3 │ ● │
105.4 │ ●●●●●●● │
103.5 │ ●● ●●●●●●● │
101.7 │ ●●●●●●●●●●● │
99.8 │●●●●●●●●●●●●●●●● │
97.9 │●●●●●●●●●●●●●●●●●● │
96.1 │●●●●●●●●●●●●●●●●●●● ●●● │
94.2 │●●●●●●●●●●●●●●●●●●●●●●●●●● ●● ● │
92.3 │●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● ●●●●●● │
90.5 │●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● │
88.6 │●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● ●●●●● │
86.7 │●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●●● ●● ●●●●●●│
────────────────────────────────────────────────────────────
成交量(M)
6.7M │ ▄ │
5.0M │▄ ▄ ▄ ▄ ▄ ▄ ▄▄ ▄ ▄ │
3.4M │▄▄ ▄ ▄▄ ▄ ▄ ▄▄ ▄ ▄▄ ▄ ▄ ▄▄▄▄▄ ▄ ▄▄▄▄▄ ▄ ▄ ▄ │
1.7M │▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▄│
────────────────────────────────────────────────────────────────────────
关于
mode_chart生成的 PNG 图:mode_chart会把价格走势 + 成交量柱状图保存为stock_chart.png,在本地运行python3 33-python-matplotlib.py --mode chart即可查看完整的深色主题图表。
Step 5:用 main 做 chart/ascii/all 脚本遥控器
痛点与机制:
main 是脚本遥控器:--mode chart 生成 PNG,--mode ascii 打印终端图,--mode all 两个都跑。这个设计很适合真实环境:本地有图形需求就保存图片,服务器只想看趋势就用 ASCII。
核心源码(逐字来自文末完整源码):
def main() -> None:
parser = argparse.ArgumentParser(description="Matplotlib 股票图表")
parser.add_argument("--mode", choices=["chart", "ascii", "all"],
default="all", help="运行模式")
args = parser.parse_args()
days, close, volume = generate_stock()
print(f"[{nexdo_time()}] 数据生成:{len(days)} 个交易日")
if args.mode in ("chart", "all"):
mode_chart(days, close, volume)
if args.mode in ("ascii", "all"):
mode_ascii(days, close, volume)
if __name__ == "__main__":
import sys
sys.argv = ["", "--mode", "ascii"]
main()
可运行演示(补齐 Mock 数据与 print 反馈):
import argparse
import sys
import time
import numpy as np
def nexdo_time() -> str:
return time.strftime("%Y-%m-%d %H:%M:%S")
def generate_stock(n: int = 120) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
np.random.seed(42)
returns = np.random.randn(n) * 0.015 + 0.0003
close = 100 * np.exp(np.cumsum(returns))
volume = np.random.randint(1_000_000, 5_000_000, n).astype(float)
days = np.arange(n)
return days, close, volume
def mode_chart(days: np.ndarray, close: np.ndarray, volume: np.ndarray) -> None:
print("运行 chart:这里会生成 stock_chart.png")
def mode_ascii(days: np.ndarray, close: np.ndarray, volume: np.ndarray) -> None:
print("运行 ascii:这里会打印终端走势图", close[-3:].round(2).tolist())
def main() -> None:
parser = argparse.ArgumentParser(description="Matplotlib 股票图表")
parser.add_argument("--mode", choices=["chart", "ascii", "all"],
default="all", help="运行模式")
args = parser.parse_args()
days, close, volume = generate_stock()
print(f"[{nexdo_time()}] 数据生成:{len(days)} 个交易日")
if args.mode in ("chart", "all"):
mode_chart(days, close, volume)
if args.mode in ("ascii", "all"):
mode_ascii(days, close, volume)
for mode in ["chart", "ascii", "all"]:
print(f"\n>>> python3 33-matplotlib-stock.py --mode {mode}")
sys.argv = ["prog", "--mode", mode]
main()
极客实战:完整源码与运行
现在,把上面的积木拼起来,将以下完整代码放进你的编辑器,运行它。先看整体闭环,再回头逐段改参数,你会更容易建立工程直觉。
#!/usr/bin/env python3
"""
33-matplotlib-stock.py — 股票分析图表
用法:
python 33-matplotlib-stock.py --mode chart # 生成 PNG
python 33-matplotlib-stock.py --mode ascii # 终端 ASCII 预览
python 33-matplotlib-stock.py --mode all
"""
import argparse
import time
import numpy as np
import matplotlib
matplotlib.use("Agg") # 无显示器环境
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
def nexdo_time() -> str:
return time.strftime("%Y-%m-%d %H:%M:%S")
def generate_stock(n: int = 120) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
"""生成模拟股票 OHLC + 成交量"""
np.random.seed(42)
returns = np.random.randn(n) * 0.015 + 0.0003
close = 100 * np.exp(np.cumsum(returns))
volume = np.random.randint(1_000_000, 5_000_000, n).astype(float)
# 价格上涨时成交量略大
volume += (returns > 0) * np.random.randint(0, 2_000_000, n)
days = np.arange(n)
return days, close, volume
def moving_avg(arr: np.ndarray, w: int) -> np.ndarray:
result = np.full(len(arr), np.nan)
cumsum = np.cumsum(np.insert(arr, 0, 0))
result[w - 1:] = (cumsum[w:] - cumsum[:-w]) / w
return result
def mode_chart(days: np.ndarray, close: np.ndarray, volume: np.ndarray) -> None:
"""生成 PNG 图表"""
fig = plt.figure(figsize=(12, 8), facecolor="#0d1117")
gs = gridspec.GridSpec(2, 1, height_ratios=[3, 1], hspace=0.05)
ax1 = fig.add_subplot(gs[0])
ax2 = fig.add_subplot(gs[1], sharex=ax1)
# 价格 + 均线
ax1.plot(days, close, color="#58a6ff", linewidth=1.2, label="Close")
ax1.plot(days, moving_avg(close, 10), color="#f0883e",
linewidth=1, linestyle="--", label="MA10")
ax1.plot(days, moving_avg(close, 30), color="#3fb950",
linewidth=1, linestyle="--", label="MA30")
ax1.fill_between(days, close, close.min(), alpha=0.1, color="#58a6ff")
ax1.set_facecolor("#161b22")
ax1.tick_params(colors="gray")
ax1.set_ylabel("Price", color="gray")
ax1.legend(facecolor="#21262d", labelcolor="white", fontsize=9)
ax1.grid(True, alpha=0.2, color="gray")
ax1.set_title("Stock Analysis Chart", color="white", fontsize=13, pad=10)
plt.setp(ax1.get_xticklabels(), visible=False)
# 成交量
colors = ["#3fb950" if c >= p else "#f85149"
for c, p in zip(close[1:], close[:-1])]
colors = ["#3fb950"] + colors
ax2.bar(days, volume / 1e6, color=colors, alpha=0.8)
ax2.set_facecolor("#161b22")
ax2.tick_params(colors="gray")
ax2.set_ylabel("Volume(M)", color="gray")
ax2.set_xlabel("Trading Day", color="gray")
ax2.grid(True, alpha=0.2, color="gray")
out = "stock_chart.png"
fig.savefig(out, dpi=150, bbox_inches="tight", facecolor=fig.get_facecolor())
plt.close(fig)
print(f"[{nexdo_time()}] 📊 图表已保存: {out}")
def mode_ascii(days: np.ndarray, close: np.ndarray, volume: np.ndarray) -> None:
"""终端 ASCII 价格走势预览"""
print(f"\n[{nexdo_time()}] 📈 ASCII 股票走势(最近60日)")
data = close[-60:]
vol = volume[-60:]
n = len(data)
rows, cols = 12, n
lo, hi = data.min(), data.max()
print(f" 价格区间: {lo:.2f} ~ {hi:.2f}")
print(" " + "─" * (cols + 12))
for r in range(rows, 0, -1):
threshold = lo + (hi - lo) * r / rows
row_str = ""
for v in data:
row_str += "●" if v >= threshold else " "
print(f" {threshold:>8.1f} │{row_str}│")
print(" " + " " * 10 + "─" * cols)
# ASCII 成交量柱
print(f"\n 成交量(M)")
max_vol = vol.max()
bar_h = 4
for r in range(bar_h, 0, -1):
row_str = ""
for v in vol:
row_str += "▄" if v >= max_vol * r / bar_h else " "
print(f" {max_vol * r / bar_h / 1e6:>6.1f}M │{row_str}│")
print(" " + "─" * (cols + 12))
def main() -> None:
parser = argparse.ArgumentParser(description="Matplotlib 股票图表")
parser.add_argument("--mode", choices=["chart", "ascii", "all"],
default="all", help="运行模式")
args = parser.parse_args()
days, close, volume = generate_stock()
print(f"[{nexdo_time()}] 数据生成:{len(days)} 个交易日")
if args.mode in ("chart", "all"):
mode_chart(days, close, volume)
if args.mode in ("ascii", "all"):
mode_ascii(days, close, volume)
if __name__ == "__main__":
import sys
sys.argv = ["", "--mode", "ascii"]
main()
$ python3 33-python-matplotlib.py --mode ascii
[2026-04-18 05:27:14] 📈 ASCII 股票走势(最近60日)
价格走势(归一化到 0-40 字符宽度)
Day 61: ████████████████████████████████████ 107.27 ▲
Day 62: ██████████████████████████████████ 105.89 ▼
Day 63: ████████████████████████████████████ 106.54 ▲
...
成交量(相对大小)
Day 61: ████████████████████ 1234567
Day 62: ██████████████ 987654
...
统计摘要:
最高价: 107.27 最低价: 84.85 区间: 22.42
上涨天数: 32/60 (53.3%)
小结
| 概念 | 一句话记忆 |
|---|---|
np.cumsum(returns) |
累积和,生成随机游走价格序列 |
np.exp(cumsum) |
对数正态模型,保证价格始终为正 |
np.cumsum 差分 |
用累计和计算移动平均,避免手写窗口循环 |
| 条件颜色列表 | ["red" if up else "green" for ...],传给 ax.bar(color=) |
GridSpec(2,1, height_ratios=[3,1]) |
双子图,价格图高度是成交量图的 3 倍 |
| ASCII 归一化 | int((v-lo)/(hi-lo)*width),把数值映射到字符数 |
mode="valid" |
convolve 只返回完整窗口的结果 |
⏱ NexDo Time(5 分钟)
挑战:给 ASCII 图表加一条移动平均线,用 · 字符标注均线位置。
具体步骤:
- 用
moving_avg(close, 5)计算 5 日均线 - 在 ASCII 图表里,对每一天同时显示收盘价柱子和均线位置
- 如果均线位置 > 收盘价位置,在柱子右边加
·;否则在柱子内部某个位置加· - 打印结果,让读者看到价格和均线的相对位置
Don’t wait for next time, do it in the next moment.