ポートフォリオ「前回比」の為替バグ修正と積み残し棚卸しコマンドの実装
月曜の朝、ポートフォリオ更新の通知を眺めていて、数字がおかしいことに気づいた。 前回比が+417万円と出ている。 日曜の朝と月曜の朝、2回連続で実行しただけで、その間、株式市場は開いていない。 動いたとすれば為替くらいのはずで、実際に評価額の変化はおよそ−20万円ほどだった。 桁が違う。
「前回比」が前回を見ていなかった
原因を追わせると、「前回比」の実体が、直前に実行したときの評価額との差ではなく、各銘柄の株価の前営業日比(Prev→Close)になっていることがわかった。 株式市場は土日に休むが、為替(USD/JPY)は土日を除いて毎日動く。 前営業日比のロジックは、市場が閉じている土日のあいだも起点を据え置いたままなので、週明けの月曜に実行すると、実際には数日分動いているはずの為替の値動きを前回比として正しく拾えていなかった。
修正はrunUpdateの中で比較元を差し替え、previousRowsByPersonへ実行のたびに評価額のスナップショットを書き込む形にした。
考え方を単純にすると、比較の起点をこう差し替えたことになる。
// 前回比の起点を「株価の前営業日比」ではなく「直前の実行結果」に統一する
const diff = currentValue - previousRowsByPerson[person].value
previousRowsByPerson[person] = { value: currentValue, at: now() }
テストを書き足す途中、追加したテストがロード段階で落ちた。 ディレクトリ指定が効かないNodeのバージョンだっただけで、ファイルを直接指定したら通った。 返り値はすでに文字列になっていたので、そこもテストを直した。 diffは2ファイル、+83/−2に収まった。 書き込みや通知を止めたdry-runを1回流し、コマンドドキュメントに仕様変更を1行足して締めた。
コミットとプッシュは、ポートフォリオ更新のスクリプトを置いている別リポジトリで行った。 学習ゲートに一度止められたが、日次のポートフォリオ更新まわりのコミットは毎回スキップしてよいという方針を伝えていたので、LEARN_SKIPで理由を残してそのまま通した。
積み残しの棚卸しをコマンドにする
同じ日の午前中、ふと、このプロジェクトに積み残し事項があったはずだと思い出し、既存の棚卸しスクリプトscan-carryover.mjsを走らせてみた。
進行中35件、未検証135件。
数だけ見ると多いが、実際に「次の一手」が定まっている項目は10件ほどに絞られた。
残りは、優先順位を付け直すか、そもそも見なくていいかを判断していないだけの山だった。
この積み残し検知を/carryoverというコマンドとして実装することにした。
設計はmemo/2026-08-11/carryover-command-design-plan.mdにすでにまとめてあった。
やったことは大きく3つある。
まず、スキャナ本体のバグ(日付判定のUTCずれ、ディレクトリ走査コードの重複、無意味な三項演算子)を直した。
次に、コミット前に走る.githooks/pre-commitへ、積み残し状況を点検する段階をもう1つ足して3段構成にした。
最後に、対象となる25本の計画書すべてに、機械可読な状態ブロック(statusやopened、targetsといったメタ情報)を書き込んだ。
25本への書き込みでは、1つ足を取られた。 Writeツールで上書きしたファイルにNUL文字が紛れ込んでいて、差分が汚くなっていた。 原因を追うより、対象のHTMLファイルをHEADから復元し、決まった位置にブロックだけをきれいに挿入し直すほうが早かった。 テストを32件書き足してグリーンにし、ゲートが止めるべき条件と通すべき条件の両方を実測してから、この日の実装は一区切りとした。
バラバラの変更を6コミットに仕分ける
昼前、別の頼み事が来た。
今日の作業以外にも溜まっている未整理の変更を、機能単位でコミットしてプッシュしてほしいという内容だった。
/commitの流れで確認すると、タグ索引やレッスンの図版、振り返り候補のメモなど、今日の作業とは無関係な変更が混ざっていた。
これらを仕分けて6コミット分の計画を提示し、承認を得て順番に実行した。
1本目は、朝作ったばかりの学習ゲートに止められた。
回避せず、素直に/learnの解説とクイズに答えた。
5問とも正解して受領証を受け取り、コミットを進めた。
2本目から4本目は、同じ設計の論点をこのクイズですでにカバーしていたので、理由を添えてスキップする判断にした。
5本目(memoファイル26件)で、ゲートが実運用で初めて発動した。
ただし中身を見ると様子がおかしい。
調べてみると、無関係な計画書がtargetsに日付ディレクトリ(memo/2026-08-11/)をまるごと宣言していて、同じ日付のフォルダに置かれているというだけで、他の計画書を編集してもゲートが反応する誤検知だとわかった。
該当する宣言を実際の対象ファイルだけに絞り込み、日付ディレクトリをまるごとtargetsに書かないというルールを規約に書き足した。
作った直後の道具が、その日のうちに自分自身の設計ミスを教えてくれた。
もう1つ、その計画書自身がtargetsに自分を含めていたため、これは設計どおりゲートが発動した。
指示に従って中身を照合すると、残る5項目はどれも外部作業(実機確認、別リポジトリの棚卸し、商品の意思決定、権利の確認、Xでの発信)で、この日に進んだものはなかった。
チェックは打たず、確認した日付だけを更新してゲートを通した。
6本目まで終えたところで作業ツリーがクリーンになったのを確認し、今回の6件と、前日から積み残っていた日記コミット1件をあわせた7件をまとめてpushした。 HEADとorigin/masterが一致し、この日のコミット整理は完了した。
学び
- 「前回比」のような指標名は、比較の起点を明示していないと壊れやすい。市場の休日と為替の稼働日がずれているだけで、桁違いの誤表示が起きた
- 積み残しの棚卸しは、件数の多さより「次の一手が決まっているか」で絞り込むと扱える量になる
- 新しく作ったゲートは、作った初日に自分の設計ミスを教えてくれることがある。誤検知を疑って中身を確認したから、日付ディレクトリをまるごと宣言する危うさに気づけた
- 学習ゲートは毎回同じ扱いをしなくていい。素直にクイズへ答える回もあれば、論点が重複していれば理由を残してスキップする回もある
月曜の朝に見つけた前回比のズレは、夕方には積み残し管理の仕組みそのものを鍛える材料に変わっていた。