見出し画像

lightweight-chartsを使用してFXのチャートを表示する。

TradingViewが提供するlightweight-chartsを活用し、FXチャートの作成に挑戦しました。このプロジェクトでは、バックエンドにPythonのFastAPIを、フロントエンドにはReactを使用しています。以下、そのプロセスを詳しくご紹介します。

コードはこちらになります
https://github.com/konohazukux/fxChart/tree/v0.1.0


1.バック側を構築する

Dockerを使用して環境を構築しました。
docker-compose.ymlとDockerfileを使用して、FastAPIアプリケーションをコンテナ化しました。これにより、どの環境でも同じ設定でアプリケーションを実行できるようになります。

1. 環境構築

ファイル構造は下記のようになります

% tree -L 4
.
├── api
│   ├── Dockerfile
│   ├── app
│   │   ├── data
│   │   │   └── 2023-09-12.csv
│   │   └── main.py
│   └── requirements.txt
├── docker-compose.yml
  • docker-compose.yml 

version: '3.9'

services:
  app:
    build:
      context: ./app
      dockerfile: Dockerfile
    command: ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--reload"]
    volumes:
      - ./backend/app:/app
    ports:
      - "8000:8000"
  • Dockerfile

# Dockerfile
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.9

# アプリケーションのディレクトリを作成
WORKDIR /app

# 依存関係のファイルをコピー
COPY requirements.txt .

# 依存関係をインストール
RUN pip install --no-cache-dir -r requirements.txt
# requirements.txt
fastapi==0.70.0
uvicorn==0.15.0
pandas
  • データについて
    FXの市場データをCSVファイルで管理しています。
    データの内容は下記のようになっています。(頭10行のみ表示しています)

% head ./api/app/data/2023-09-12.csv
datetime,symbol,open,high,low,close
2023-09-12 00:00, USDJPY, 146.394, 146.419, 146.385, 146.41
2023-09-12 00:01, USDJPY, 146.415, 146.433, 146.414, 146.429
2023-09-12 00:02, USDJPY, 146.43, 146.449, 146.417, 146.448
2023-09-12 00:03, USDJPY, 146.452, 146.472, 146.449, 146.464
2023-09-12 00:04, USDJPY, 146.461, 146.482, 146.461, 146.482
2023-09-12 00:05, USDJPY, 146.476, 146.495, 146.457, 146.457
2023-09-12 00:06, USDJPY, 146.456, 146.456, 146.426, 146.429
2023-09-12 00:07, USDJPY, 146.427, 146.441, 146.426, 146.436
2023-09-12 00:08, USDJPY, 146.436, 146.441, 146.428, 146.429
%
  • main.py
    main.pyでは、FastAPIを使用してデータを提供するエンドポイントを設定しました。CORSの設定を行うことで、Reactアプリケーションからのリクエストを安全に処理できます。

# main.py
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
import pandas as pd
import os

DATA_DIR = "./data"

app = FastAPI()

origins = [
    "http://localhost:3000",  # Reactのアドレス
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
def read_root():
    return {"Hello": "World"}

@app.get("/items/{item_id}")
def read_item(item_id: int, query_param: str = None):
    return {"item_id": item_id, "query_param": query_param}

@app.get("/data")
def read_data(date: str = "2023-09-12"):
    # ディレクトリの存在確認
    data_path = os.path.join(DATA_DIR, date)
    if not os.path.exists(data_path):
        raise HTTPException(status_code=404, detail="Data not found")

    # ログファイルの読み込み
    all_data = []
    for hour in range(24):
        for minute in range(60):
            log_file_name = f"{date.replace('-', '')}-{hour:02}{minute:02}.log"
            log_file_path = os.path.join(data_path, log_file_name)
            if os.path.exists(log_file_path):
                df = pd.read_csv(log_file_path, header=None, names=["datetime", "symbol", "open", "high", "low", "close"])
                all_data.append(df)

    final_df = pd.concat(all_data, ignore_index=True)
    return final_df.to_dict(orient="records")

2. dockerを再起動

docker compose down
docker compose up -d --build

3. ブラウザで確認

ブラウザのURL欄にパスを入れるとresponseが表示されます

http://localhost:8000/data?date=2023-09-12

[{"datetime":"2023-09-12 00:00","symbol":" USDJPY","open":146.394,"high":146.419,"low":146.385,"close":146.41},{"datetime":"2023-09-12 00:01","symbol":" USDJPY","open":146.415,"high":146.433,"low":146.414,"close":146.429},
        :
        :


2.フロントエンドを構築する

1. 環境構築

まず、dockerで環境を構築したいので、下記のようなファイルを用意します

 % tree -L 3 
.
├── docker-compose.yml
└── web
    └── Dockerfile
  • docker-compose.yml 

version: '3.9'

services:
  web:
    build:
      context: ./web
      dockerfile: Dockerfile
    volumes:
      - ./web:/usr/src/app          

    ports:
      - "3000:3000"
    stdin_open: true 
    tty: true
  • Dockerfile

FROM node:16.15.0-alpine
WORKDIR /usr/src/app

RUN apk add bash
RUN apk update
RUN apk add vim
RUN apk add git 
RUN npm install -g create-react-app 
  • Dockerイメージをビルドします

docker compose up -d --build
  • reactアプリケーションを作成します

docker-compose run --rm web sh -c "create-react-app fx-chart"
  • アプリケーション自動起動設定
    docker-compose.yml を修正

version: '3.9'

services:
  web:
    build:
      context: ./web
      dockerfile: Dockerfile
    volumes:
      - ./web:/usr/src/app          
    command: sh -c "cd fx-chart && yarn start"  # ここを追加

    ports:
      - "3000:3000"
    stdin_open: true 
    tty: true

dockerを再起動

docker compose down
docker compose up -d --build

ブラウザで確認
http://localhost:3000/

2.lightweight-charts のインストール

docker-compose run --rm web sh -c "npm install --save lightweight-charts axios"

3.App.jp

import React, { useEffect, useRef, useState } from "react";
import { createChart, CrosshairMode } from 'lightweight-charts';
import axios from 'axios';

const App = () => {
  const chartRef = useRef(null);
  const [date, setDate] = useState("2023-09-12"); // デフォルトの日付

  useEffect(() => {
    if(chartRef.current) {
      const chart = createChart(chartRef.current, { 
        width: 800, 
        height: 600,
        layout: {
          backgroundColor: '#ffffff',
          textColor: 'rgba(33, 56, 77, 1)',
        },
        crosshair: {
          mode: CrosshairMode.Normal,
        },
        grid: {
          vertLines: {
            color: 'rgba(197, 203, 206, 0.5)',
          },
          horzLines: {
            color: 'rgba(197, 203, 206, 0.5)',
          },
        },
      });
      const candlestickSeries = chart.addCandlestickSeries();

      // APIからデータを取得
      const fetchData = async () => {
        try {
          const response = await axios.get(`http://localhost:8000/data?date=${date}`);
          const data = response.data.map(item => {
            console.log(item);
            const timestamp = new Date(item.datetime).getTime() / 1000; // UNIXタイムスタンプに変換
            return {
              time: timestamp,
              open: parseFloat(item.open),
              high: parseFloat(item.high),
              low: parseFloat(item.low),
              close: parseFloat(item.close)
            };
          });
          candlestickSeries.setData(data);
        } catch (error) {
          console.error("Error fetching data:", error);
        }
      };

      fetchData();
    }
  }, [date]);

  return (
    <>
      <h1 style={{ color: 'red' }}> Candlestick Chart </h1>
      <div ref={chartRef} style={{ width: '800px', height: '600px' }}></div>
      <input type="date" value={date} onChange={e => setDate(e.target.value)} />
    </>
  );
};

// Appを外のファイルからも使用できるように export default で外から参照可能にする
export default App;

4. ブラウザで確認

http://localhost:3000

すべてのセットアップが完了した後、ブラウザでアプリケーションを開くと、FXチャートが表示されます。

この記事が気に入ったらサポートをしてみませんか?