【マルチプロセス】1000銘柄を数秒でスキャン!Pythonの並列処理で監視を高速化する方法

  • URLをコピーしました!

投資の世界では、全銘柄をリアルタイムで監視し「条件に一致した瞬間」を誰よりも早く見つけることが利益に直結します。しかし、多くの個人投資家が直面するのが、スキャンスピードの壁です。1000もの銘柄を一つずつチェックしていては、分析が終わる頃には絶好の買い場が過ぎ去っていることも珍しくありません。

この記事では、Pythonの「マルチプロセス」という技術を使い、あなたのPCが持つ本来の性能を引き出す方法を解説します。標準的なプログラムでは眠っているCPUの複数コアをフル稼働させることで、1000銘柄の分析をわずか数秒で完結させる仕組みを構築しましょう。

目次

なぜ1000銘柄の監視には「マルチプロセス」が必要か?

Pythonは非常に書きやすく優れた言語ですが、標準的な書き方では「一度に一つの作業」しか進められません。これを1000銘柄のスキャンに当てはめると、最初の1銘柄目の計算が終わってから、ようやく2銘柄目の処理が始まるという「長蛇の列」が出来上がってしまいます。

この章では、なぜ従来の「逐次処理(一つずつ順番にやる方法)」が投資において命取りになるのかを整理します。また、Python特有の仕組みを理解し、マルチプロセスを導入することでどのようにスピードの壁を突破できるのか、その論理的な仕組みを明らかにしていきます。

1銘柄ずつの「逐次処理」ではチャンスを逃す

1銘柄ずつの分析を順番に行う「逐次処理」は、銘柄数が増えるほど指数関数的に待ち時間が膨らみます。例えば、1銘柄のデータ取得とテクニカル計算に合計1秒かかると仮定すると、1000銘柄を終えるまでに16分以上もかかってしまいます。5分足チャートでゴールデンクロスを探している場合、16分後にはすでにトレンドが終わっている可能性が高いでしょう。

投資において「速さ」は、それ自体が大きな優位性になります。

他のトレーダーがまだスキャンの途中で足踏みしている間に、あなたは数秒で結果を得て、即座にエントリー注文を出すことが可能になります。

例えば、急激な出来高を伴って急騰し始めた銘柄を見つける際、全銘柄スキャンが10秒で終わる人と10分かかる人では、得られる利益に天と地ほどの差が生まれます。

どれほど優れたロジックを持っていても、実行スピードが遅ければ、それは「終わった後の解説」にしかなりません。

確かに、銘柄を絞り込めば速くなりますが、それではまだ誰も注目していない「お宝銘柄」を見落とすリスクがあります。

全銘柄を網羅しつつ、かつ超高速で処理を終えることこそが、エンジニア投資家が目指すべき理想の姿です。

逐次処理のデメリットをまとめると、以下のようになります。

  • 処理が終わる頃には相場の状況が変わってしまう
  • CPUが持つ複数のコアを有効活用できず、宝の持ち腐れになる
  • スキャン頻度を上げることができず、リアルタイム性に欠ける
  • 複数の時間足を同時に監視することが物理的に困難になる

Pythonの限界(GIL)を突破して複数コアを使い切る

Pythonには「GIL(Global Interpreter Lock)」という仕組みがあり、標準的なマルチスレッドではCPUの計算パワーを分散させることができません。スレッドを増やしても、実は一つのコアが高速に切り替わっているだけで、本当の意味での「同時進行」ではないのです。

マルチプロセスはこの制限を完全に回避できます。

プログラム自体を複数の独立したプロセスとして立ち上げるため、あなたのPCに搭載された8コアや16コアといったCPU性能を、文字通り「100%」使い切ることが可能です。

例えば、8コアのPCであれば、理論上は1つの作業を8分割して同時に進めることができます。

これにより、重たいテクニカル指標の計算であっても、実行時間を1/8近くまで短縮できる計算になります。

ただし、プロセスを増やしすぎると「指示を出す手間(オーバーヘッド)」が大きくなり、逆に遅くなる場合もあります。

自分のPCの性能に合わせた最適な設定を見つけることが、高速化を成功させるための大事な一歩です。

CPUのパワーを最大限に引き出すためのポイントです。

  • os.cpu_count() を使って、自分のPCの論理コア数を把握する
  • スレッドではなく「プロセス」を分けることで、真の並列化を実現する
  • メモリの使用量に注意し、各プロセスが重くなりすぎないよう設計する
  • 計算負荷の高いライブラリ(pandasやnumpy)と組み合わせて運用する

「マルチプロセス」と「マルチスレッド」はどう使い分ける?

並列化の手法を調べると、必ず「マルチスレッド」という言葉も出てきます。どちらも似たような響きですが、投資分析における役割は全く異なります。これらを混同して使うと、せっかくコードを書いても期待したほど速くならないという事態になりかねません。

この章では、通信待ちが得意な「スレッド」と、計算処理が得意な「プロセス」の違いを明確にします。1000銘柄のスキャナーを作る際、どちらの技術をどの部分に適用すべきか、その「適材適所」の判断基準を身につけていきましょう。

ネットワーク待ちが得意なマルチスレッド

マルチスレッドは、データのダウンロードのような「待ち時間」が多い作業に向いています。プログラムが取引所のAPIに「データをください」とリクエストを送り、返事が来るまでの間、別のスレッドが次のリクエストを送るという使い方が得意です。

PythonのGIL制限下でも、こうした「I/O(入出力)待ち」の間は他のスレッドに処理を譲ることができるため、通信の並列化はスレッドでも十分に効果があります。

1000銘柄のデータを個別に取得する場合などは、スレッドを使うことで通信の隙間時間を埋めることができます。

例えば、1つの注文を出す間に次の銘柄の価格を確認するといった、同時並行のやり取りにはスレッドが適しています。

しかし、取得した大量のデータを使って「RSIの14日平均を計算する」といった計算作業になると、スレッドでは速度が上がりません。

スレッドの特性を理解して、まずは「通信部分」の効率化を考えましょう。

以下の表に、スレッドとプロセスの使い分けの基準をまとめました。

並列化手法の比較表です。

項目マルチスレッドマルチプロセス
得意な作業API取得、ファイル読み書き複雑な計算、画像処理、統計分析
CPUの利用1つのコアを共有(GIL制限あり)複数のコアを独立して使用
メモリ消費少ない(メモリを共有する)多い(プロセスごとにメモリを持つ)
実装の難易度低いやや高い(データの受け渡しが必要)

重い計算処理を分散できるマルチプロセス

一方で、取得したデータに対してテクニカル指標を計算したり、複雑なシミュレーションを行ったりする場合は、マルチプロセス一択です。計算処理はCPUに負荷をかけるため、1つのコアで頑張るよりも、複数のプロセスに仕事を割り振るほうが圧倒的に速くなります。

1000銘柄分のデータフレームに対して、一斉に関数を適用するような場面で真価を発揮します。

各プロセスが独立したメモリ空間で動くため、計算の衝突を気にせずフルスピードで回すことが可能です。

例えば、全銘柄に対して過去1年分のデータを使ったバックテストを回す場合、逐次処理では1時間かかる作業が、マルチプロセスなら数分で終わることもあります。

スキャンの心臓部である「判定ロジック」の部分にこの技術を導入することで、数秒での完結が見えてきます。

ただし、プロセス間で大きなデータのやり取りを頻繁に行うと、その通信コストで遅くなることがあります。

「各プロセスに仕事を投げたら、あとは結果だけを返してもらう」というシンプルな設計を心がけるのがコツです。

マルチプロセスを最大限に活かすための考え方です。

  • CPUに負荷がかかる「ロジック計算」をターゲットにする
  • 各プロセスが独立して完結できるような関数を作る
  • プロセス間のデータ共有は最小限に抑える
  • 完了した銘柄から順に結果を受け取る仕組みにする

Pythonでマルチプロセスを実装する基本コードを書こう

理屈が分かったところで、具体的なコードの書き方を見ていきましょう。Pythonには concurrent.futures という非常に強力で使いやすいライブラリがあります。これを使うと、複雑なプロセスの管理を意識することなく、数行の追加でプログラムを並列化できます。

ここでは、初心者の方でもコピーしてすぐに試せる「マルチプロセスのテンプレート」を紹介します。1000銘柄を捌くための「エンジンの作り方」をマスターしましょう。この基本形を覚えるだけで、あらゆる投資分析を高速化できるようになります。

ProcessPoolExecutorを使って並列化する方法

ProcessPoolExecutor は、その名の通りプロセスの「プール(貯め池)」を作り、そこに仕事を投げ込むための仕組みです。自分で一つずつプロセスを立ち上げる必要はなく、AIが裏側で自動的に管理してくれます。

使い方は非常にシンプルで、with 構文を使ってプールを作成し、map 関数で銘柄リストを流し込むだけです。

以下のコードは、複数の計算を同時に実行する基本的な構造を示しています。

並列処理を実行するための基本形です。

from concurrent.futures import ProcessPoolExecutor
import os

def analyze_stock(ticker):
    # ここに個別の分析ロジックを書く
    # 例:RSIの計算や条件判定
    return f"{ticker}: 分析完了"

if __name__ == "__main__":
    tickers = ["AAPL", "TSLA", "MSFT", "GOOGL"] # 実際には1000銘柄
    
    # 物理コア数に合わせてプロセス数を設定
    max_workers = os.cpu_count()
    
    with ProcessPoolExecutor(max_workers=max_workers) as executor:
        results = list(executor.map(analyze_stock, tickers))
    
    for res in results:
        print(res)

このコードを実行すると、あなたのPCにある複数のコアが手分けして銘柄を処理していきます。

たったこれだけの記述で、1000銘柄のスキャン速度が劇的に変わる体験ができるはずです。

物理コア数に合わせた最適なプロセス数の決め方

プロセスを何個立ち上げるのがベストなのかは、あなたのPCの性能によります。一般的には、os.cpu_count() で取得できる「論理コア数」と同じにするのが最も効率的です。

欲張ってコア数以上に増やしても、プロセス同士が「席の奪い合い」を始めてしまい、逆に遅くなる原因になります。

また、メモリを大量に消費する分析の場合、プロセスを増やしすぎるとPC全体がフリーズするリスクもあります。

例えば、メモリが8GBしかないPCで、1プロセス1GB使うような重い処理を16並列で動かすと、即座にメモリ不足で止まります。

自分のマシンの「体力」を考慮して、最適な max_workers を設定することが大切です。

最適なプロセス数を見極めるためのヒントです。

  • まずは os.cpu_count() を基準にする
  • 処理が重い場合はコア数の半分程度から試してみる
  • 実行時間を計測しながら、少しずつ数字を調整する
  • 監視モニター(タスクマネージャー等)でCPU使用率が100%付近か確認する

計算量とメモリのバランスを整えることが、安定した高速スキャンの秘訣です。

yfinanceで1000銘柄のデータを高速に集める工夫

マルチプロセスで「計算」を速くしても、その前段階の「データ取得」が遅ければ、システム全体の速度は上がりません。実は1000銘柄のスキャンで最も時間がかかるのは、通信の部分であることが多いのです。

この章では、yfinance ライブラリの隠れた機能を使い、1000銘柄のデータを一気に、かつ効率的に取得するテクニックを解説します。通信回数を最小限に抑えることで、APIの制限を回避しながら、数秒で全データを手元に揃える工夫を学びましょう。

全銘柄を一括ダウンロードして通信回数を減らす

1000銘柄のデータを取得する際、for文で1銘柄ずつ yf.download() を呼び出すのは最悪の方法です。1000回の通信リクエストが発生し、それぞれの接続待ち時間だけで数分が経過してしまいます。

yfinance は、銘柄名をスペース区切りの文字列として渡すことで、複数の銘柄を一度に取得できます。

これにより、1000回の通信を「わずか数回」にまとめることができ、劇的にスピードアップします。

例えば、以下のように記述することで、複数のデータを一括で手に入れることが可能です。

一括ダウンロードを行うためのコード例です。

import yfinance as yf

# 1000銘柄をスペースで繋げた文字列にする
tickers_list = ["AAPL", "TSLA", "MSFT", "GOOGL"] # 実際は1000個
tickers_str = " ".join(tickers_list)

# 一気にダウンロード(threads=Trueにすると通信も並列化される)
data = yf.download(tickers_str, period="1mo", group_by='ticker', threads=True)

# Appleの終値にアクセス
apple_close = data["AAPL"]["Close"]

この方法を使うだけで、データの準備にかかる時間が「分単位」から「秒単位」へ短縮されます。

通信コストを抑えることは、API提供側への配慮にもなり、安定した運用に繋がります。

銘柄リストを適切な「塊(チャンク)」に分割して処理する

一度に1000銘柄すべてのデータをリクエストすると、URLが長すぎてエラーになったり、サーバー側から拒否されたりすることがあります。そこで、銘柄リストを50個や100個単位の「塊(チャンク)」に分けるのが賢いやり方です。

100銘柄ずつ10回に分けて取得することで、通信の安定性と速度のバランスを保つことができます。

この分割処理をマルチプロセスと組み合わせることで、さらなる高速化が可能になります。

例えば、100銘柄の取得を8つのプロセスで分担すれば、1000銘柄のスキャンはあっという間に終わります。

大規模なデータを扱う際は、こうした「細分化して統治せよ」の精神が欠かせません。

データ取得の効率を上げるための比較表です。

手法通信回数速度安定性
1銘柄ずつ取得1000回非常に遅い低い(BANされやすい)
1000銘柄一括1回速い低い(エラーが出やすい)
100銘柄×10回10回非常に速い高い(推奨)

1000銘柄のテクニカル指標を数秒で計算するロジック

データが揃ったら、いよいよマルチプロセスの出番です。1000銘柄分の株価データに対して、RSIや移動平均線を計算し、あなたの「買い条件」に一致するかを判定します。

ここでは、並列処理を最大限に活かしつつ、メモリの消費を最小限に抑えるためのロジックの書き方を解説します。各銘柄の結果を集約し、最終的に「今買うべき銘柄リスト」を数秒で弾き出すプログラミングのコツを掴んでください。

テクニカル指標の計算を各プロセスに分散させる

取得した巨大なデータフレームをそのまま各プロセスに渡すと、コピーが発生してメモリを圧迫します。必要なのは「特定の銘柄の、特定の列」だけですので、分析に必要な分だけを関数に渡すように工夫しましょう。

各プロセスは、自分の担当する銘柄の価格データだけを受け取り、計算を行い、「True(条件一致)」か「False(不一致)」かを返します。

これにより、メインプロセスの負荷を下げ、計算効率を極限まで高めることができます。

例えば、1000銘柄中、50銘柄が条件に一致したなら、その50個の銘柄名だけがあなたの手元に残るように設計します。

「計算は現場(子プロセス)で、集計は本部(親プロセス)で」という役割分担が重要です。

pandasを使ってメモリ消費を抑えながら処理する方法

大量の銘柄を扱う際、全ての過去データをメモリに載せ続けると、あっという間にメモリ不足に陥ります。分析が終わった銘柄のデータは速やかに破棄するか、必要な期間(直近100日分など)だけに絞って保持するようにしましょう。

Pythonの pandas は非常に便利ですが、メモリ消費量も大きめです。

並列処理を行う際は、各プロセスが消費するメモリの合計が、あなたのPCの搭載メモリを超えないように注意を払ってください。

例えば、1プロセスが500MB使う分析を16並列で動かせば、それだけで8GBのメモリを占有します。

「一度に読み込むデータを最小限にする」ことは、高速化と同じくらい大切なテクニックです。

メモリを節約しながら計算するポイントです。

  • 浮動小数点の精度(float64からfloat32へ)を下げる検討をする
  • 不要な列(Volume以外など)は取得時に削除しておく
  • 1銘柄の分析が終わるたびに、各プロセスの内部メモリをリセットする
  • 必要最小限の過去期間だけを指定してダウンロードする

これらを徹底することで、スペックがそれほど高くないPCでも、1000銘柄のスキャンが可能になります。

API制限を回避して安定的に監視を続けるコツ

高速スキャナーが完成しても、それを24時間動かし続けるには「マナー」が必要です。短時間にあまりに多くのリクエストを投げすぎると、Yahoo Financeなどのデータ提供側から「攻撃」とみなされ、IPアドレスが遮断(BAN)されてしまうことがあります。

この章では、ツールを安定して運用するための「安全装置」の作り方を学びます。スピードを追求しつつも、相手のサーバーに負担をかけない賢い運用のコツを身につけ、永続的に監視を続けられる環境を整えましょう。

リクエストの間に「意図的な待ち時間」を入れるべき?

1000銘柄を一瞬でスキャンしようとすると、リクエストが集中しすぎます。これを防ぐには、各銘柄やチャンクの取得の間に、数秒の time.sleep() を入れるのが有効です。

「速くしたいのに待つの?」と思うかもしれませんが、BANされて0秒になるよりは、10秒かけて確実に取得するほうが遥かに賢明です。

マルチプロセスを使えば計算自体は一瞬で終わるため、その浮いた時間を「通信のインターバル」に充てることで、安全性を確保できます。

例えば、100銘柄取得するごとに1秒休む、といった設定にするだけで、サーバー側からの印象は大きく変わります。

「相手の懐に入る」ような優しいリクエストを心がけましょう。

データ取得はスレッド、計算はプロセスで分ける理由

さらに高度な手法として、データの取得には「マルチスレッド」を使い、重い計算には「マルチプロセス」を使うというハイブリッド構成があります。通信はスレッドで束ねて行い、返ってきたデータをプロセスに投げて計算させるやり方です。

これにより、通信の効率と計算の速度を両立させつつ、OS全体の負荷をバランスよく分散できます。

Claude Codeに「通信はスレッド、計算はプロセスで分ける設計に書き換えて」と頼めば、理想的なコードを作成してくれます。

確かに実装は少し複雑になりますが、プロのクオンツ(分析官)が使うような、極めて洗練されたスキャナーへと進化します。

まずは基本形から始め、慣れてきたらこの構成に挑戦してみましょう。

安定運用のためのチェックリストです。

  • 通信回数を最小限にする「一括取得」を導入しているか
  • リクエストの間に適切なスリープ(休憩)が入っているか
  • 休日や夜間など、市場が閉まっている間は休止する設定になっているか
  • エラーが連続した際に、自動でプログラムを一時停止する仕組みがあるか

ローカル環境で実行する際のメモリ不足を回避する

自宅のPCで24時間回す場合、OSのバックグラウンド処理やブラウザのメモリ消費も考慮しなければなりません。並列数を最大まで上げるのではなく、少し余裕を持たせた設定(例:コア数のマイナス2)にするのが安定のコツです。

メモリが足りなくなると、PC全体がカクついたり、最悪の場合は分析途中でクラッシュしたりします。

「8割の力で淡々と回し続ける」のが、長期投資における自動化の鉄則です。

例えば、昼間はフルパワーで動かし、自分がPCを使う夜間は並列数を下げるといった柔軟な設定も面白いでしょう。

無理のない範囲で、ツールを自分の生活の一部に馴染ませていきましょう。

プログラムをさらに研ぎ澄ますためのチェックポイント

スキャナーが動き始めたら、最後は「本当に想定通りの速度が出ているか」を確認します。並列化には目に見えない落とし穴があり、設定一つで速度が倍になったり、半分になったりすることがあるからです。

ここでは、ツールのパフォーマンスを計測し、さらに高速化するためのデバッグとチューニングの秘訣を紹介します。1000銘柄のスキャン時間を「1秒でも削る」ための、エンジニアらしいこだわりを形にしていきましょう。

プログラムの実行時間を計測してボトルネックを探す

どこが遅いのかを知らずに、適当に並列数を変えても意味がありません。Pythonの time モジュールを使って、「データ取得にかかった時間」と「計算にかかった時間」を別々に計測しましょう。

もし通信に10秒、計算に0.1秒かかっているなら、マルチプロセスの設定をいじるよりも、データの取得方法を改善したほうが効果的であることが一目で分かります。

数字を元に、効率的な改善ポイントを見極めることが重要です。

例えば、以下のように時間を測るだけで、改善すべき場所が浮き彫りになります。

実行時間を計測するための簡単な方法です。

import time

start_time = time.time()
# ここに処理を書く
elapsed_time = time.time() - start_time
print(f"実行時間: {elapsed_time:.2f}秒")

こうした計測を癖にすることで、無駄のない洗練されたコードへと磨き上げることができます。

デバッグが難しい並列処理のエラーを追跡する方法

マルチプロセスは、エラーが起きても画面に詳細が表示されず、ひっそりとプロセスが死んでいることがよくあります。これを防ぐには、関数全体を try-except 文で囲み、エラーの内容をログファイルに書き出すようにしましょう。

「どの銘柄の、何の計算で失敗したのか」が分からなければ、1000銘柄の中から犯人を探すのは不可能です。

例外処理を丁寧に書くことが、止まらないシステムを作るための絶対条件です。

例えば、特定の銘柄だけデータが空だったために計算エラーになる、といったケースは非常に多いです。

こうした「例外」をあらかじめ想定し、スキップして次に進むようなタフなプログラムを目指しましょう。

エラー対策で盛り込むべき要素です。

  • ログファイル(error.log)への詳細な出力
  • エラーが起きた銘柄名の特定
  • エラー発生時のリトライ(再試行)回数の設定
  • 致命的なエラーの際の管理者への通知(Slack等)

クラウドサーバー(VPS)へ移行して24時間稼働させる

自宅のPCで動作が安定したら、いよいよクラウドサーバー(VPS)への移行を検討しましょう。VPSなら24時間安定した回線で、電気代を気にせずスキャナーを回し続けることができます。

マルチプロセスを最大限に活かすなら、CPUのコア数を変更できるプランを選ぶのがおすすめです。

スキャン対象を1000銘柄から全米市場の数千銘柄に広げても、クラウドのパワーなら余裕で捌ききれます。

例えば、月額数百円のプランから始め、銘柄数が増えたらプランをアップグレードするといった柔軟な運用が可能です。

ここまで来れば、あなたはもう立派な「システムトレーダー」の仲間入りです。

まとめ:高速スキャンを武器に市場をリードしよう

この記事では、Pythonのマルチプロセスを活用して、1000銘柄の監視を劇的に高速化する方法を解説しました。

  • 逐次処理(for文)の限界を理解し、マルチプロセスでCPUパワーを全開放する
  • 「通信はスレッド」「計算はプロセス」という適材適所の使い分けが鍵
  • concurrent.futures ライブラリを使えば、並列化は驚くほど簡単に実装できる
  • yfinanceの一括ダウンロードと銘柄分割(チャンク)で通信の壁を突破する
  • API制限やメモリ不足といった実務的なリスクを回避する術を身につける

1000銘柄を数秒でスキャンできる能力は、あなたに圧倒的な「時間的余裕」と「情報量」をもたらします。他の投資家が気づく前に、いち早くシグナルを捉え、冷静に戦略を実行する。そんなスマートな投資スタイルを、ぜひ今回作成したツールで手に入れてください。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

目次