テーブルから一歩進んだ詳細表示が欲しい
前回までで、信用残データのシグナル計算とフィルタ・ソート可能なテーブル表示ができるようになりました。ただ、テーブルだけでは「この銘柄の売残・買残がどう推移してきたか」という時系列の流れが見えません。
そこで、テーブルの行(銘柄)をクリックすると詳細モーダルがポップアップし、その中で売残高・買残高・取組比率の推移グラフと、できれば実際の株価チャートまで確認できるようにしたい、という方向で機能を追加していきました。
モーダルウィンドウの基本構成
モーダルには以下の要素を詰め込みました。
モーダル内のコンテンツ
- 銘柄名・コード・市場・データ公表日
- 外部チャートサービス(TradingView・株探・Yahoo!ファイナンス)へのリンクボタン
- 主要指標カード(売残高・買残高・取組比率・各スコアなど)
- シグナルタグの一覧
- 売残高・買残高推移、取組比率推移、売残前日比、一般信用/制度信用の内訳をChart.jsで描画
Chart.jsによる推移グラフ自体は、蓄積したCSV履歴をその銘柄の分だけ抜き出して描画するだけなので、データ構造が整っていれば比較的すんなり実装できました。一方、TradingViewの日足チャート埋め込みは予想以上に試行回数がかかりました。
TradingViewチャートの埋め込みを試みた3つの方法
株価チャートを表示する方法として、まず以下の選択肢を検討しました。
| 方法 | メリット | デメリット |
|---|---|---|
| TradingViewウィジェット | 無料・日本株対応・見た目が良い | X-Frame-Optionsで外部サイトへのiframe埋め込みが禁止。scriptタグ方式も銘柄シンボルが反映されず断念 |
| Yahoo!ファイナンス画像URL | シンプルな画像埋め込み | 仕様変更に弱い・取得できないケースあり |
| 株探・みんかぶへのリンクボタン | 確実に動く・実装が簡単 | 埋め込み表示ではなく別タブに飛ぶ |
最初はTradingViewウィジェットをメインで埋め込む方針で進めました。しかし後述するように複数の技術的な壁にぶつかり、最終的にはTradingView・株探・Yahoo!ファイナンスへの外部リンクボタン方式に落ち着きました。試行錯誤の過程で分かったことを順番に記録します。
innerHTMLにscriptを書いても動かない問題
TradingViewのウィジェットは、公式が提供している埋め込みコードをそのままページに貼ると動作します。しかし今回はモーダルを開いたタイミングで動的にチャートを生成する必要があったため、JavaScriptで innerHTML にウィジェットのコードを丸ごと挿入する方法を最初に試しました。
// うまくいかなかった実装
wrap.innerHTML = `
<div class="tradingview-widget-container">
<div class="tradingview-widget-container__widget"></div>
<script src="https://s3.tradingview.com/external-embedding/embed-widget-advanced-chart.js" async>
{ "symbol": "TSE:7203", "interval": "D" }
</script>
</div>`;
結果はチャートが表示されないままでした。ブラウザの仕様として、innerHTML でDOMに挿入した<script>タグは実行されません。
これはHTML仕様上の安全対策で、意図しないスクリプトの実行を防ぐためのものです。今回は完全にこの制約にハマっていました。
正しい方法は、document.createElement("script") でscript要素を生成し、appendChild() でDOMツリーに追加することです。
この方法でDOMに追加されたscriptは正しく実行されます。
const container = document.getElementById("tv-container");
const sc = document.createElement("script");
sc.type = "text/javascript";
sc.async = true;
sc.innerHTML = JSON.stringify({
symbol: tvSymbol,
interval: "D",
timezone: "Asia/Tokyo",
theme: "dark",
// ...その他設定
});
sc.src = "https://s3.tradingview.com/external-embedding/embed-widget-advanced-chart.js";
container.appendChild(sc); // これで初めて実行される
設定値(JSON)の渡し方はsc.textContentではなくsc.innerHTMLに設定する必要があります。
この方法でscriptは実行されるようになりましたが、別の問題が発覚しました。指定した銘柄シンボルが無視され、デフォルトのApple(AAPL)が常に表示されるのです。
TradingViewのウィジェットがシンボル設定を読み取るタイミングと、動的にscriptを追加するタイミングがずれていることが原因と考えられます。
ブラウザのキャッシュが残っているとAppleのチャートが表示され続けるため、「動いている」ように見えて実態は違ったという状況でした。
下図のように銘柄コード5074のチャートを表示したいのに実際のチャートはAppleになってしまいました。
TradingViewの高さが効かない問題:autosizeの落とし穴
スクリプトの動的読み込み自体は解決したものの、次に直面したのが「チャートの高さがCSSで指定しても全く反映されない」という問題でした。
コンテナにheight: 840pxと指定しても、実際に表示されるチャートはなぜか別の高さになってしまいます。
原因はウィジェットの設定オブジェクトにある autosize: true でした。この設定が有効な場合、
TradingViewのウィジェットは親要素のCSSによる高さ指定を無視して自律的にリサイズする仕様になっています。
CSSをどれだけ調整しても効かなかったのは、そもそも見ているパラメータが違ったためでした。
解決策はautosize: falseに変更し、設定オブジェクト内にwidthとheightを明示的に指定することです。
const tvConf = {
autosize: false, // ← これがポイント
symbol: tvSymbol,
interval: "D",
width: "100%",
height: 600, // 明示的に高さを指定
timezone: "Asia/Tokyo",
theme: "dark",
style: "1",
locale: "ja",
};
その後、ユーザーから「思っていたより大きすぎる」というフィードバックがありheight: 450に調整しました。しかしその後さらに大きな問題が発覚します。
最終的にiframe埋め込みも断念した理由
scriptタグ方式でシンボルが反映されないため、次にiframeのsrcにシンボルをパラメータとして直接渡す方式を試しました。 しかしTradingViewはX-Frame-OptionsというHTTPヘッダで外部サイトからのiframe埋め込みを全面禁止しています。 iframeを使うと禁止マーク(🚫)が表示されるだけで、チャートは一切表示されません。これはTradingViewの仕様であり、回避する方法はありません。
結論として、TradingView・株探・Yahoo!ファイナンスへの外部リンクボタンをモーダル内に配置する形に落ち着きました。 クリックすると別タブで正しい銘柄のチャートが開きます。信用残の推移グラフ(Chart.js)はモーダル内にそのまま表示されます。
下図のようにiframe方式では、TradingView画面はうまく表示することができませんでした。
銘柄コード変換の見落とし
第1回の記事でも触れましたが、JPXのデータの銘柄コードは末尾に0が付いた5桁形式(14190、135A0)で、
TradingViewや株探などの外部サイトに渡す際は末尾を取り除いた4桁(1419、135A)にする必要があります。
最初の実装では、この変換ロジックを「数字のみ5桁」というパターンでしか判定していませんでした。
// 不完全だった実装
function dispCode(code) {
if (/^\d{5}$/.test(code)) return code.slice(0, 4);
return code; // アルファベット入りはそのまま →ここがバグ
}
これだと 135A0 のようなアルファベットを含む新コード体系の銘柄が変換されず、モーダルに「コード:135A0」と5桁のまま表示されてしまい、株探やみんかぶへのリンクも見つからないURLを生成してしまいました。
修正は「全コードの末尾は必ず0なので、単純に末尾1文字を削除する」というルールに統一することでした。実データを確認したところ、例外なく全コードの末尾が0だったため、これでシンプルかつ確実に解決できました。
// 修正後:パターンマッチではなく「末尾0を削除」に統一
function dispCode(code) {
if (!code) return code;
return code.endsWith('0') ? code.slice(0, -1) : code;
}
// TradingView用:英数字混在4桁も許可
function tvCode(code) {
if (!code) return null;
const dc = dispCode(code);
return /^[0-9A-Z]{4}$/.test(dc) ? dc : null;
}
「パターンを場当たり的に増やす」よりも「データの実態から共通ルールを見つけて一本化する」ほうが、結果的にバグが減るという、よくある教訓を再確認した場面でした。
レイアウトの試行錯誤:ヘッダ被り・横スクロール
モーダルやチャート以外にも、ダッシュボード全体のレイアウトでいくつか細かい修正を重ねました。代表的なものを紹介します。
テーブルヘッダのstickyが原因でコンテンツと被る
テーブルのヘッダ行をposition: stickyで固定し、スクロールしても見えるようにしていましたが、ページ全体のヘッダ(ロゴなどがあるグローバルヘッダ)と固定位置がずれてしまい、データ行と重なって見えてしまう問題が発生しました。
参考にした既存のHTML実装を見直したところ、そちらではテーブルヘッダ自体をstickyにしない構成になっていました。シンプルにstickyを外し、テーブル全体をラップする要素の中にページャー(ページ送り)を内包する構成に変更することで解決しました。
ページ送りでスクロール位置が変わってしまう
ページネーションのボタンをクリックした際、window.scrollTo()を呼んでページ上部にスクロールする処理を入れていましたが、これが「ページを送るたびに画面が動いて見にくい」という指摘につながりました。該当の処理を削除し、ページ切り替え時はスクロール位置を一切変更しないようにしています。
スマホで見たときにテーブルが崩れる
列数が多いテーブルをスマートフォンの狭い画面で見ると、文字が詰まって読みにくくなります。テーブルをラップする要素にoverflow-x: autoを指定し、テーブル自体にはmin-widthを設定することで、スマホでは横スクロールで全列を確認できるようにしました。
.tscroll {
overflow-x: auto;
-webkit-overflow-scrolling: touch; /* iOSのスクロール挙動改善 */
}
table {
width: 100%;
min-width: 820px; /* これより狭い画面では横スクロールが発生 */
}
細かい調整の積み重ねですが、こうした地味な修正を重ねることで、実際に使いやすいダッシュボードに近づいていきました。データの自動収集から需給シグナルの計算、そして見やすいUIまで、シリーズを通して一通りの仕組みが完成しました。今後はデータが積み上がってきた段階で、シグナルの精度や実際の株価との相関を検証する記事も書いていきたいと思います。
FAQ
TradingViewのウィジェットをinnerHTMLで挿入してもscriptが動かないのはなぜですか?
innerHTMLでscriptタグを含むHTMLを挿入しても、ブラウザの仕様上そのscriptは実行されません。document.createElement('script')でscript要素を生成しappendChildでDOMに追加する方法を試しましたが、今度は指定した銘柄シンボルが無視されてApple(デフォルト銘柄)が表示され続けるという問題が残りました。最終的にTradingViewのiframe埋め込みはX-Frame-Optionsでブロックされることが判明し、埋め込みそのものを断念しています。
TradingViewウィジェットの高さがCSSで指定しても反映されないのはなぜですか?
ウィジェット設定でautosize: trueを指定していると、コンテナのCSSによる高さ指定を無視して自律的にリサイズします。autosize: falseに変更しwidthとheightを明示的に指定することで一時的には改善しましたが、そもそもTradingViewのiframe埋め込みがX-Frame-Optionsでブロックされることが後から判明したため、高さの調整自体が不要になりました。
アルファベットを含む銘柄コードでTradingViewチャートが表示されないことがあるのはなぜですか?
TradingViewへ渡すシンボルの判定が数字4桁のみに限定されていたためです。判定の正規表現を英数字混在の4桁([0-9A-Z]{4})に拡張しましたが、そもそもTradingViewのiframe埋め込み自体がX-Frame-Optionsでブロックされるため、最終的にはTradingView・株探・Yahoo!ファイナンスへの外部リンクボタンに切り替えています。