このプロジェクトについて
EDINET(有価証券報告書等)の開示データをもとに、日本株の財務指標・投資判断スコアを日次で算出・公開しているサービスです。
公開サイトはこちら → https://stocks.happy-life-design.net
前回の記事(2026-10-15公開)はこちら → https://qiita.com/rightcross-ae86/items/7308f69291dc98f57bc5
この記事について
株価データには、毎晩自動で動く保守処理があります。その中に、終値が100万円を超える行を「明らかなデータの誤り」として削除する規則がありました。2026-10-11に、この規則が、正当な過去の株価まで毎晩消していたことが分かりました。株式併合の調整で、昔の価格が100万円を超えて表示される会社があったためです。
この記事は、見つけ方、直し方、直したあとの確かめ方をまとめたものです。2026-10-12には、同じ型のもう1社も補完しました。
この記事は、単独で読めます。2026-10-15公開の復元の検証の記事の続きではなく、別の話題です。2026-10-13・2026-10-14公開の株式分割の記事とは、別の原因です。
この記事では、次のように呼びます。
- 「行」: 1社の1日分の株価(始値・高値・安値・終値・出来高)
- 「保守処理」: 毎晩自動で動く、データの整合を確かめ、不要なデータを取り除く処理
- 「株式併合」: 複数の株を1株にまとめること。10株を1株にまとめると、株価はおよそ10倍になります
- 「調整」: 併合や分割があると、前後で株価が不連続になります。過去の価格も同じ比率で換算して、前後をつなげて比べられるようにすること。1,000分の1の併合なら、過去の価格を1,000倍します
- 「補完」: データベースに無い行を、外部サイトの値で足すこと
- 「テクニカル指標」: 株価と出来高から計算する指標(移動平均・RSI・OBVなど)。株価の行を足すと、その周りの日付の指標も変わります
- 「全社の指紋」: 会社ごとに、全ての行の内容から計算した値。反映の前後で比べて、対象外の会社が1社も変わっていないことを確かめます
- 「ドライラン」: 実際には書き込まずに、何を書くかだけを確かめる実行
- 「往復試験」: 書き込み、取り消して、全てが元どおりになることを確かめる試験
- 「本番」: 実際に稼働していて、公開サイトが読んでいるデータベース
- 「OBV」: 出来高を、株価が上がった日は足し、下がった日は引いて、積み上げた指標
先に要点
- 毎晩の保守処理に、終値が100万円を超える行を削除する規則がありました。システムを作った当初からある規則です
- 株式併合の調整で、昔の価格が100万円を超える会社があり、その正当な過去の行が消えていました。1社で334日分、別の1社で78日分、計412日分です
- 最初は「最初から取り込めていなかった」と見ていましたが、誤りでした。欠けていた334日が全て終値100万円超だったことで、削除規則が原因だと分かりました
- 補完の前に、規則に会社単位の例外を入れました。補完しても、翌朝の保守処理でまた消えるためです。例外は印を付けた会社だけで、1,000万円を超える桁外れの値は従来どおり削除します
- 334行を補完し、対象外の4,619社は株価・指標・企業情報とも全社で変わっていないことを確かめました
- 補完すると、既存の指標も変わりました。OBVは、既存の全4,847行が変わりました
- 2026-10-12の翌朝の保守処理のあとも、補完した行は消えていませんでした
起きたこと: データベースに無い334日
2026-10-11、別の調査の途中で、ある会社の株価を、外部サイトに載っている現在の値と突き合わせました。外部サイトには5,181行、データベースには4,847行があり、差の334日(2005-09-29〜2007-02-27)が、データベースに無い状態でした。
この334日について、最初は「削除されたのではなく、最初に取り込んだ時点から無かったのだろう」と考え、原因を調べる前に、そう記録していました。これは誤りでした。
原因: 欠けた334日は、全て終値が100万円を超えていた
欠けていた334日の外部サイトの値を確かめると、全て終値が100万円を超えていました(最大197.8万円)。データベースに残っている行は、全て100万円以下で、最大の終値はちょうど1,000,000円でした。
この境目が、毎晩の保守処理の削除規則と一致しました。終値が100万円を超える行を、明らかなデータの誤りとして削除する規則です(元のSQLは「技術的な補足」に載せています)。
なぜ、正当な価格が100万円を超えるのか。この会社は、2回の株式併合(100株を1株に、10株を1株に)で、累計1,000分の1になっています。そのため、調整後の過去の価格は、当時の実際の価格の1,000倍です。2005年当時の実際の終値は約1,000円で、調整後は100万〜200万円になります。
次の小さなコードで、この仕組みを再現しました。このコードは、この記事のために書いたもので、実際の保守処理のコードではありません。値は説明用で、実際の株価ではありません。
THRESHOLD = 1_000_000 # 毎晩の削除規則の基準: 終値がこれを超えたら削除
FACTOR = 1000 # 累計の調整倍率(1,000分の1の併合なら、過去の価格を1,000倍する)
for raw in (980, 1000, 1050, 1980): # 当時の実際の終値(説明用の値。実際の株価ではない)
adjusted = raw * FACTOR # 調整後の終値
print(raw, adjusted, "削除される" if adjusted > THRESHOLD else "残る")
実行すると、次のように出ます(実行結果のままです)。
980 980000 残る
1000 1000000 残る
1050 1050000 削除される
1980 1980000 削除される
| 当時の実際の終値(説明用) | 調整後の終値 | 削除規則 |
|---|---|---|
| 980円 | 980,000円 | 残る |
| 1,000円 | 1,000,000円 | 残る |
| 1,050円 | 1,050,000円 | 削除される |
| 1,980円 | 1,980,000円 | 削除される |
データベースに残っていた最大の終値がちょうど100万円なのは、この境目のためです。
流れを図にすると、次のとおりです。
他に、同じ型の会社はあるか
データベースの最大終値が10万円以上の76社を、外部サイトの値と突き合わせました。終値が100万円を超えていて、データベースに無い行があったのは、ここまでの1社(334日分)と、もう1社(78日分・2005-02-17〜2006-05-09・最大143万円)の2社だけでした。
10万円未満の会社は、価格の連続性から該当しにくいと考えていますが、全社を確かめたわけではありません。
直す順番: 補完の前に、規則の例外
欠けた行を足しても、翌朝の保守処理で、また消えます。そこで、補完より先に、規則に例外を入れました。
- 企業マスタ(会社ごとの基本情報の表)の補足情報に、例外の印を付けた会社だけ、上限を100万円から1,000万円に引き上げる
- 印のない会社は、従来どおり100万円を超えたら削除する
- 印のある会社でも、1,000万円を超える桁外れの値は削除する
変更前と変更後の削除SQLは、「技術的な補足」に載せています。
例外が効くことは、次の2つで確かめました。
- 本番のデータベースの中で、印を付け、補完する334行を仮に入れてから、新しい規則を実行する試験を、最後に必ず取り消す(ROLLBACK)形で行いました。削除は0行で、行数は変わりませんでした
- 反映後の本番に対して、保守処理のコードから取り出した実際のSQLを実行して、削除が0行で、334行が残ることを確かめました
補完: 334行を足して、全社で変わっていないことを確かめる
外部サイトの現在の値で、334行を足しました。次の順で進めました。
- 補完の前の状態をバックアップする(株価・指標とも4,847行)
- 全社の指紋を取る
- ドライランをする
- 往復試験をする
- 本番に334行を書き込む
- テクニカル指標を再計算する(5,181件)
- 全社の指紋を取り直して、前と比べる
確かめた結果は、次のとおりです。
| 確かめたこと | 結果 |
|---|---|
| 足した334行 | 期待した値と全列が一致 |
| 既存の4,847行 | 行の内容から作った値(ハッシュ値)をまとめた値が、補完の前と同一 |
| 対象外の4,619社 | 株価・指標・企業情報とも全社一致(違うもの0・増えたもの0・消えたもの0) |
| 指標 | 株価から独立に計算し直した値との最大差が0.00005 |
| API | 2005-09-29〜2007-03-05の368行を返す |
補完すると、過去の指標も変わった
欠けていた行を足すと、その周りの日付の指標も変わります。補完で変わった、既存の指標の行数は、次のとおりです。
| 指標 | 変わった既存の行 |
|---|---|
| 移動平均(直近の複数日の終値の平均)(5日・25日・75日・200日) | 各24〜30行 |
| 指数移動平均・MACD(終値の履歴から計算する指標) | 130〜262行 |
| RSI(上昇と下落の勢いを測る指標) | 155行 |
| OBV(出来高を積み上げる指標) | 全4,847行 |
OBV以外は、足した期間のあとの、おおむね2008年2月までの行が変わりました。OBVは積み上げの指標なので、既存の全ての行が変わりました。2007-02-28以降の4,817行は、一律に2,410株増えています。
2社目と、補完しなかった1社
2026-10-12、もう1社(78日分)も同じ手順で補完しました。対象外の4,619社は、株価・指標・企業情報とも全社で変わらず、指標は独立に計算し直した値との最大差が0.00005でした。
穴に見えても、原因が違う会社もありました。ある1社は、2006年に65日分の行がありませんが、欠損ではなく、別の規則(1円未満の異常な値を取り除く規則)で、意図して削除した行でした。外部サイトは、この期間だけ、前後のおよそ1,000分の1の価格を返しています。同じ行を戻すと、その規則でまた消えます。1,000倍して戻す案もありますが、根拠が前後の連続性だけなので、していません。
本番で効いたか
2026-10-12の毎晩の保守処理のあと、次を確認しました。
- 保守処理のタスクが成功していた
- 補完した会社の行数が、5,181行のままだった
- 終値が100万円を超える334行が、残っていた
- データの完全性チェックが、正常だった
気づくのが遅れた理由と、次から守ること
欠けた行は、エラーも警告も出しません。消す規則は、書かれたとおりに動いていました。規則が、併合の調整後の価格まで巻き込むことに、気づいていませんでした。
次から守ることを、2つ決めました。
- データが欠けているとき、「最初から無かった」と推定する前に、既存の削除規則(保守処理・取り込み時の検査)の条件に、欠けた行が全て当てはまらないかを、先に調べる
- 規則の例外を入れるときは、補完より先に入れる。補完しても、翌朝の保守処理で消えるため
限界
- 例外の印を付けたのは2社だけです。他の会社で同じ型が起きても、毎晩の保守処理は従来どおり削除します。全社の確認は、最大終値が10万円以上の76社が対象で、10万円未満の会社は、全社を確かめたわけではありません
- 取り込み時の検査(終値が100万円を超える行は取り込まない)には、例外を入れていません。2社の現在の価格は小さく、通常の取り込みでは当たらないためです
- 補完した値は、外部サイトの現在の値を正として足しました。そのうち31行は出来高が0の行です(取引のなかった日の穴埋めなどの行)。他の会社に残っている同じ種類の行を残す方針に合わせて、足しました
- 公開サイトのチャートは最長2年の表示なので、補完した2005年からの行は、画面では見られません(APIでは返ります)
- 翌朝の保守処理での確認は、先に補完した1社の334行についての記録です。2社目の78行について、同じ確認の記録は読み取れていません
技術的な補足(実際に使ったコード)
ここに載せるのは、実際のソースコードからの抜粋で、元のファイルの連続した行です。実物と変えたのは、共通のインデントを除いたことと、例外の印の値(社内で問題を管理するための番号)を<番号>に置き換えたことの2つです。データベースの接続情報は含まれません。
1. 変更前の削除規則
毎晩の保守処理の一部です。終値が100万円を超える行を、全社一律に削除します。RETURNINGは、削除した行の会社と日付を、後続の処理に渡します。
cur.execute("DELETE FROM stock_prices WHERE close_price > 1000000 RETURNING company_id, trade_date")
2. 変更後の削除規則(印を付けた会社だけ、1,000万円まで残す)
AND NOT (...)の中が、例外です。終値が100万円を超える行のうち、「1,000万円以下で、かつ、印のある会社の行」だけを、削除の対象から外します。
cur.execute("""
DELETE FROM stock_prices
WHERE close_price > 1000000
AND NOT (close_price <= 10000000
AND company_id IN (SELECT id FROM companies WHERE meta->>'price_cap_exempt' = '<番号>'))
RETURNING company_id, trade_date
""")
まとめ
- 毎晩の保守処理の「終値100万円超を削除」する規則が、株式併合の調整で100万円を超える会社の、正当な過去の行まで消していました(2026-10-11に判明)
- 欠けた日が全て終値100万円超だったことで、原因が分かりました。「最初から無かった」という見立ては、原因を調べる前の推定で、誤りでした
- 補完の前に、規則へ会社単位の例外を入れ、334行(1社)と78行(もう1社)を補完しました。対象外の4,619社は全社で変わりませんでした
- 補完すると、既存の指標も変わります。OBVは、既存の全4,847行が変わりました
- 例外は、印を付けた2社だけです。他の会社には広げていません
気になる点・ご質問があればコメントでお知らせください。