CSV ファイル 呼んでロウソク足をSVG ファイルに出力

プログラム

import csv
import svgwrite
from datetime import datetime

# 入力CSVと出力SVGファイル
csv_file = r'D:\WinPython\notebooks\stock_prices.csv'
output_file = r'Y:\candles.svg'

# ローソク足のサイズ設定
candle_width = 6
candle_gap = 4
candle_step = candle_width + candle_gap

# 高さ拡大!
price_top = 0
price_bottom = 300
volume_top = 300
volume_bottom = 500

# 初期スケール値
price_min = float('inf')
price_max = 0
volume_max = 0

# データ読み込み&スケール取得
rows = []
with open(csv_file, newline='') as f:
    reader = csv.reader(f)
    next(reader)  # ヘッダーをスキップ
    for row in reader:
        if len(row) < 6:
            continue
        try:
            date = datetime.strptime(row[0], "%Y-%m-%d")
            open_, high, low, close, volume = map(int, row[1:6])
        except ValueError:
            continue
        price_max = max(price_max, high)
        price_min = min(price_min, low)
        volume_max = max(volume_max, volume)
        rows.append((date, open_, high, low, close, volume))

# スケール調整
price_range = price_max - price_min
if price_range == 0:
    raise ValueError("price_range がゼロです!")

price_span = price_bottom - price_top
volume_span = volume_bottom - volume_top

def scale_price(p): return ((p - price_min) * price_span) / price_range
def price_to_y(p): return price_bottom - scale_price(p)

# 横幅をデータ数に合わせて自動調整
width = len(rows) * candle_step + 20
height = volume_bottom + 40

# SVG描画
dwg = svgwrite.Drawing(output_file, size=(width, height))
x = 10
month_labels = {}

# 移動平均線の準備
sma_25 = []
sma_50 = []
sma_75 = []
close_prices = []

x_positions = []

for i, (date, open_, high, low, close, volume) in enumerate(rows):
    fill = '#f44336' if close >= open_ else '#4caf50'

    y_open = price_to_y(open_)
    y_close = price_to_y(close)
    y_high = price_to_y(high)
    y_low = price_to_y(low)

    body_top = min(y_open, y_close)
    body_bottom = max(y_open, y_close)
    body_height = body_bottom - body_top

    scaled_volume = volume * volume_span / volume_max
    vol_y = volume_bottom - scaled_volume

    x_center = x + candle_width // 2
    x_positions.append(x_center)
    close_prices.append(close)

    # ヒゲ(中央から上下に)
    dwg.add(dwg.line(start=(x_center, y_high), end=(x_center, body_top), stroke='black', stroke_width=1))
    dwg.add(dwg.line(start=(x_center, body_bottom), end=(x_center, y_low), stroke='black', stroke_width=1))

    # 胴体
    dwg.add(dwg.rect(insert=(x, body_top), size=(candle_width, body_height), fill=fill, stroke='black'))

    # Volume棒
    dwg.add(dwg.rect(insert=(x, vol_y), size=(candle_width, scaled_volume), fill='#2196f3'))

    # 月ラベル(1ヶ月に1回だけ表示)
    month_key = date.strftime("%Y-%m")
    if month_key not in month_labels:
        dwg.add(dwg.text(month_key, insert=(x_center, volume_bottom + 12), font_size='8px', text_anchor='middle'))
        month_labels[month_key] = True

    x += candle_step

# 移動平均線の描画関数
def draw_sma_line(period, color):
    points = []
    for i in range(len(close_prices)):
        if i + 1 >= period:
            avg = sum(close_prices[i + 1 - period:i + 1]) / period
            y = price_to_y(avg)
            x = x_positions[i]
            points.append((x, y))
    for i in range(1, len(points)):
        dwg.add(dwg.line(start=points[i - 1], end=points[i], stroke=color, stroke_width=1.5))

# 移動平均線を描画
draw_sma_line(25, '#e91e63')  # ピンク
draw_sma_line(50, '#4caf50')  # 緑
draw_sma_line(75, '#2196f3')  # 青

# Y軸ラベル(価格:1000円単位)
step = 1000
label_price = (price_min // step) * step
while label_price <= price_max:
    y = price_to_y(label_price)
    dwg.add(dwg.text(f"{label_price}円", insert=(2, y + 4), font_size='8px', fill='black'))
    label_price += step

# Volume Y軸ラベル(25%刻み)
for i in range(1, 5):
    ratio = i * 0.25
    value = int(volume_max * ratio)
    y = volume_bottom - (volume_span * ratio)
    dwg.add(dwg.text(f"{value:,}", insert=(2, y + 4), font_size='8px', fill='blue'))

# 保存
dwg.save()
print(f"[完了] SVGファイルを生成しました: {output_file}")

Python,

Posted by eightban