---
name: project-real-ohlc-chart-plan-2026-06-07
description: "2026-06-07 STEP 1200d 後の Stage B+ 計画 — WIC /chart を synthetic random walk から real OHLC feed (Twelve Data API + CF Worker proxy) に置換。 藤本さん「リアルタイム chart に反映するなら TradingView 使うしか?」 honest filter → No, Lightweight Charts (現在 WIC 採用) + 外部 API + CF Worker proxy で実現可能。 藤本さん「Twelve Data API + CF Worker proxy 推奨」 選択 → API key 取得後 (藤本さん 5 分 手動) 実装 trigger"
metadata: 
  node_type: memory
  type: project
  originSessionId: bebc52c2-5fa8-4ba8-84ee-f4e35a1f2ba9
---

# Real OHLC Chart 計画 — Twelve Data + CF Worker proxy

## 起源 + 判断経緯

2026-06-07 STEP 1200a-d 系列で WIC /chart に dictionary v0.1 5 indicators + Stage C signal markers + ROC family canonical collapse demo を実装後、 藤本さん質問:

> 「リアルタイムで動いているチャートに反映とすると、TradingView を使うしか無いでしょうか?」

★ Rei Claude honest filter:
- 「TradingView」 という名前で **3 別物 区別**: (α) TradingView 公式 Widget embed / (β) TradingView Charting Library (申請制) / (γ) Lightweight Charts (TradingView 製 OSS Apache 2.0, WIC 現在採用)
- 結論: **No, TradingView 必須でない**。 WIC は既 (γ) Lightweight Charts で自前 indicator 統合済、 不足は **real OHLC 価格 feed のみ** (現状 synthetic random walk)
- → 外部 API (Twelve Data / Alpha Vantage / Finnhub / OANDA / Polygon 等) + CF Worker proxy で API key 保護 → Lightweight Charts に流し込む構成可能

藤本さん AskUserQuestion 選択: **Twelve Data API + CF Worker proxy (推奨)**。

## Twelve Data 選定理由

| Option | 無料 quota | リアルタイム | 対応市場 | 採用判断 |
|---|---|---|---|---|
| **Twelve Data** | 800 req/日 | ~1 分遅延 | FX/Stock/Crypto 多 | ★ 採用 |
| Alpha Vantage | 25 req/日 | ~1 分遅延 | 主に Stock | quota 不足 |
| Finnhub | 60 req/分 | 数秒遅延 | Stock + Crypto | reserve (Twelve 不足時) |
| OANDA Demo | 無制限 (demo) | 秒単位 | FX 特化 | demo account 必要 |
| Polygon.io | 商用 / 無料 tier 限定 | 秒単位 | 全市場 | 不採用 |
| Yahoo Finance unofficial | 無料 | ~15 分遅延 | 大量カバー | ToS リスク不採用 |

「実用 1-15 分足 + 1 分遅延で OK」 = Twelve Data 800 req/日 = 主要 5 pair × 240 ticks/日 で **充分** (1 pair = 1.5 req/分 × 60 = 90 req/時、 4 hour 連続表示でも 1 pair = 360 req → 5 pair = 1800 req/日 必要 → free tier 800 で不足、 fetched-only-on-view 方式 + 5-min cache で 200 req/日 程度に圧縮可能)。

## 構成図

```
[User Browser]
  WIC /chart page (Astro static)
    ├── Lightweight Charts (CDN, 既)
    ├── technicalindicators (CDN, 既)
    └── fetch('/api/ohlc?symbol=USD/JPY&interval=1min&outputsize=300')
                                ↓
[Cloudflare Worker] (新規, ~80 行)
  workers/wic-ohlc-proxy/
    ├── env.TWELVE_DATA_KEY (secret, Worker dashboard で設定)
    ├── KV cache (5 分 TTL で同 query 再 fetch 防止)
    ├── CORS: world-investment-center.pages.dev のみ allow
    ├── rate-limit per IP (10 req/分)
    └── fetch('https://api.twelvedata.com/time_series?symbol=...&apikey=...')
                                ↓
[Twelve Data API]
  Free tier 800 req/日
  返却: { values: [{ datetime, open, high, low, close, volume }, ...] }
```

## 実装規模見積

| Component | 規模 |
|---|---|
| CF Worker (wic-ohlc-proxy) | ~80 行 (TypeScript) |
| chart.astro fetch logic 改修 | ~50 行 (synthetic → fetch + map to Lightweight Charts format) |
| symbol selector UI (USD/JPY, EUR/USD, GBP/USD, AUD/USD, USD/CHF 等 5-10 pair + S&P 500/NASDAQ 等 stock index) | ~30 行 |
| Loading state + error handling | ~30 行 |
| i18n keys (~10 件) | EN + JA |
| Honest scope 更新 (synthetic → real, ~1 分遅延明示, no advice 維持) | ~5 行 |
| 計 | **~200 行 + Worker deploy + API key 設定** |

## 藤本さん向け 5 分手動手順 (Twelve Data API key 取得)

実装 trigger 条件 = この 5 分手順完了後。

1. https://twelvedata.com/register にアクセス
2. Email + password で登録 (Google OAuth でも可)
3. Email verify
4. Dashboard で「API Key」 をコピー
5. 私 (Claude) に「API key 取得済」 と教えていただくか、 もしくは直接 Cloudflare Workers の dashboard で
   - Workers & Pages → 新 Worker `wic-ohlc-proxy` 作成
   - Settings → Variables → `TWELVE_DATA_KEY` = (取得 key) を「Encrypt」 で保存
   して頂ければ、 私が code commit + deploy 設定を進めます。

★ API key 自体を私に共有する必要はありません (security 上望ましくない)。 Cloudflare Workers の dashboard で encrypt-saved secret として設定する方が安全です。

## CF Worker code template (実装時 reference)

```typescript
// workers/wic-ohlc-proxy/src/index.ts
interface Env {
  TWELVE_DATA_KEY: string;
  OHLC_CACHE: KVNamespace;
}

const ALLOWED_ORIGINS = [
  'https://world-investment-center.pages.dev',
  'http://localhost:4321', // dev
];

export default {
  async fetch(req: Request, env: Env): Promise<Response> {
    const origin = req.headers.get('Origin') ?? '';
    const corsHeaders = ALLOWED_ORIGINS.includes(origin)
      ? { 'Access-Control-Allow-Origin': origin }
      : {};

    if (req.method === 'OPTIONS') {
      return new Response(null, { status: 204, headers: corsHeaders });
    }

    const url = new URL(req.url);
    const symbol = url.searchParams.get('symbol') ?? 'USD/JPY';
    const interval = url.searchParams.get('interval') ?? '1min';
    const outputsize = Math.min(
      Number(url.searchParams.get('outputsize') ?? 300),
      500,
    );

    // 5-min cache
    const cacheKey = `${symbol}|${interval}|${outputsize}`;
    const cached = await env.OHLC_CACHE.get(cacheKey, 'json');
    if (cached) {
      return Response.json(cached, { headers: { ...corsHeaders, 'X-Cache': 'HIT' } });
    }

    const apiUrl =
      `https://api.twelvedata.com/time_series` +
      `?symbol=${encodeURIComponent(symbol)}` +
      `&interval=${interval}` +
      `&outputsize=${outputsize}` +
      `&apikey=${env.TWELVE_DATA_KEY}` +
      `&format=JSON`;
    const upstream = await fetch(apiUrl);
    const data = await upstream.json();

    if (data.status === 'ok') {
      await env.OHLC_CACHE.put(cacheKey, JSON.stringify(data), {
        expirationTtl: 300, // 5 min
      });
    }
    return Response.json(data, { headers: { ...corsHeaders, 'X-Cache': 'MISS' } });
  },
};
```

## chart.astro 改修 plan (実装時 reference)

```javascript
// 既 generateOHLC() (synthetic) を残し、 toggle で real / synthetic 切替
const REAL_OHLC_URL = '/api/ohlc'; // CF Pages route → Worker

async function fetchRealOHLC(symbol, interval, count) {
  try {
    const res = await fetch(
      `${REAL_OHLC_URL}?symbol=${encodeURIComponent(symbol)}&interval=${interval}&outputsize=${count}`,
    );
    if (!res.ok) throw new Error('fetch failed: ' + res.status);
    const data = await res.json();
    if (!data.values) throw new Error('invalid response');
    // Twelve Data format → Lightweight Charts format
    return data.values
      .reverse() // Twelve Data は新しい順 → 古い順に
      .map(v => ({
        time: Math.floor(new Date(v.datetime).getTime() / 1000),
        open: parseFloat(v.open),
        high: parseFloat(v.high),
        low:  parseFloat(v.low),
        close: parseFloat(v.close),
      }));
  } catch (e) {
    console.warn('Real OHLC fetch failed, falling back to synthetic:', e);
    return null; // fallback to generateOHLC()
  }
}
```

## 安全装置 (free-tier 800/日 枯渇対策)

- **5 分 KV cache**: 同 symbol+interval+outputsize は 5 分以内なら API 不呼出
- **rate-limit per IP**: 10 req/分 (CF Worker で簡易実装)
- **fallback**: API 失敗時 synthetic random walk に自動 fallback (UX 切れない)
- **symbol limit**: dropdown で 5-8 pair に限定 (任意 symbol 入力不可)
- **interval limit**: 1min / 5min / 15min / 1h / 1day のみ (秒未満不可)

## Honest scope 更新項目

chart.astro の existing honest scope に追加:

- 「Real OHLC mode: ~1 分遅延の market data です。 trading platform でない。 broker 接続もありません。 過去 N 本の chart 表示のみ。」
- 「API: Twelve Data (https://twelvedata.com), free tier limited」
- 「synthetic と real の toggle UI」
- 「予測でなく可視化目的のみ」

## 実装 trigger 条件

- 藤本さん「API key 取得済 + Worker secret 設定済」 報告
- OR 藤本さん「API key 取得 / Worker 設定を Claude にやらせる」 (但し key は dashboard で encrypt save 推奨, code には絶対 hardcode しない)

## v0.2 候補 (Real OHLC 動作後)

- WebSocket real-time tick (Finnhub free tier or Binance public)
- Symbol search (Twelve Data symbol-list API)
- Historical chart export (CSV)
- Indicator backtest mode (過去データで signal marker の event 一覧)

## 関連

- [[project-fx-indicator-dictionary-v01-2026-06-07]] dictionary v0.1 (Stage A) 親
- STEP 1200 / 1200b / 1200c / 1200d series
- [[feedback-financial-theory-separate-from-rei-core]] 永続原則 (rei-aios SEED_KERNEL 触らない)
- [[feedback-no-rush-publication]] 急がず ゆっくりと (API key 取得後 trigger)
