移動平均線とは
株価チャートを見ると、ローソク足の上に何本かの線が引かれていることがあります。
その代表的なものが 移動平均線(Moving Average / MA) です。
移動平均線とは、「過去◯日間の終値の平均をつないだ線」のことです
たとえば 5日移動平均線(5日線、5MA) なら直近5営業日の終値の平均、
25日移動平均線(25日線、25MA) なら1か月分の市場参加者の平均取得コストを表していると考えられます。
「5日線を超える」と上向きの流れが強くなる
下記は任天堂(7974)の日足です。オレンジ矢印は5日線より上の時期、青矢印は5日線より下のゾーンです。

直近12月の下落がわかりやすいですが、陽線でも5日線を超えていない間は下落基調です。
逆に5日線を超えている間は上昇しています。
このように実際にトレードをしていると、
- 底値っぽい形は出た
- 下ヒゲや十字線も出た
- でもその後、下に行ってしまった
という経験を何度もします。
これは、「まだ市場全体が“買い”に合意していなかった」状態でエントリーしていることが多いからです。
そこで私が意識するようになったのが、
「5日移動平均線を明確に上回っているかどうか」
です。
5日線を超えるということは、
直近数日間の参加者の平均コストよりも高い価格で、それでも買われている
という状態です。
つまり、
- 個人
- 短期トレーダー
- アルゴ・機関投資家
を含めて「この価格帯は買い」と判断され始めたサインとも言えます。
底を当てにいくのではなく、
市場の判断に乗る「順張り」 に切り替えたことで、トレードがかなり安定しました。
このプログラムでやりたいこと
この考え方をベースに、
「5日線より上にあり、流れが上向きに変わりつつある銘柄」
を、感覚ではなく 数値でスクリーニング するために作ったのが、今回の Python プログラムです。
このスクリプトでは、全上場銘柄を対象にして、主に次の点をチェックしています。
チェック項目
① 5日移動平均線が上向いているか
短期のトレンドが上に向き始めているかどうか。
② 5日線が「折り返し」ているか
下落基調だった5日線が、直近数営業日で下げ止まり → 上向きに転じたか。
③ 終値が5日線より上にあるか
「市場が買いと判断した価格帯」に乗れているかどうか。
④ 25日移動平均線の向き
中期トレンドが悪すぎないかの確認。
(25日線が下向きの場合はスコアを抑える設計)
⑤ 5日線と25日線の距離
短期線が中期線に近づいているか、上抜け余地があるか。
⑥ 出来高ブレイクの有無(直近5営業日)
出来高が急増している銘柄は、
「参加者が増え、トレンドが出やすい」 ため別枠でチェック。
⑦ 株式分割があった銘柄への対応
分割によって移動平均線が歪まないよう、
過去データを補正した上で判定しています。
出力結果の見方
スクリーニング結果は CSV で出力され、
- 各条件は
- ○(条件クリア)
- ✕(未達)
- 右端には
- ○の数を合計したスコア
を表示しています。
スコアが高いほど、
「5日線ベースの順張りに適した形」
になっている銘柄、という見方ができます。
※ 出来高ブレイクは参考情報として表示し、スコア計算には含めていません。
このプログラムの使い方
このプログラムは、
- 「この銘柄を買え」と教えてくれるもの
- 未来の株価を予測するもの
ではありません。
あくまで、
「市場が買いだと判断し始めた銘柄候補を、効率よく並べる道具」
です。
実際のエントリーでは、
- 日足の形
- 上ヒゲ・下ヒゲ
- 地合い(指数)
- 決算・テーマ性
を必ず確認した上で判断するようにしています。
参考書籍
プログラムコード
作成したプログラムは下記です。利用される場合、必要なモジュールはインポートください。
また、銘柄名として同じフォルダに「nk.csv」が必要です。
日本取引所(https://www.jpx.co.jp/markets/statistics-equities/misc/01.html)からダウンロードし、csvで保存ください(ETFなどの余計な銘柄もあるので、加工して保存した方が良いです)。
import warnings
warnings.filterwarnings("ignore", category=FutureWarning)
import datetime
import pandas as pd
import yfinance as yf
from tqdm import tqdm
import time
import os
# ======================
# 設定
# ======================
CACHE_DIR = "yf_ma_cache"
BATCH_SIZE = 300
WAIT_TIME = 8
start = "2022-10-01"
end = datetime.datetime.today().strftime('%Y-%m-%d')
# ======================
# True / False → ○×
# ======================
def arrow(val):
return "○" if val else "✕"
# ======================
# 株式分割補正
# ======================
def adjust_for_stock_split(df):
df = df.copy()
# 前日比
ratio = df["Close"] / df["Close"].shift(1)
# 分割らしきポイント検出
split_points = ratio[(ratio < 0.45) | (ratio > 2.2)]
if split_points.empty:
return df, False, None
split_date = split_points.index[0]
# 前日と当日の終値を正しく取得
prev_close = df["Close"].shift(1).loc[split_date]
curr_close = df.loc[split_date, "Close"]
if pd.isna(prev_close) or curr_close == 0:
return df, False, None
split_ratio = round(prev_close / curr_close)
if split_ratio <= 1:
return df, False, None
# 分割日以前を補正
price_cols = ["Open", "High", "Low", "Close"]
df.loc[:split_date, price_cols] /= split_ratio
return df, True, split_date
# ======================
# キャッシュ初期化
# ======================
os.makedirs(CACHE_DIR, exist_ok=True)
for f in os.listdir(CACHE_DIR):
path = os.path.join(CACHE_DIR, f)
if os.path.isfile(path):
os.remove(path)
# ======================
# 銘柄リスト
# ======================
df_nk = pd.read_csv("nk.csv", dtype=str)
tickers = [c.strip() + ".T" for c in df_nk["コード"]]
names = df_nk["銘柄名"].tolist()
markets = df_nk["市場・商品区分"].tolist()
results = []
# ======================
# メイン処理
# ======================
for i in range(0, len(tickers), BATCH_SIZE):
batch_tickers = tickers[i:i+BATCH_SIZE]
batch_names = names[i:i+BATCH_SIZE]
batch_markets = markets[i:i+BATCH_SIZE]
cache_path = os.path.join(CACHE_DIR, f"batch_{i}.csv")
if os.path.exists(cache_path):
data = pd.read_csv(cache_path, header=[0,1], index_col=0, parse_dates=True)
else:
data = yf.download(
batch_tickers,
start=start,
end=end,
group_by="ticker",
threads=True,
progress=False
)
data.to_csv(cache_path)
time.sleep(WAIT_TIME)
for t, raw_name, raw_market in tqdm(
zip(batch_tickers, batch_names, batch_markets),
total=len(batch_tickers)
):
if t not in data.columns.get_level_values(0):
continue
df = data[t].copy()
df = df[df["Close"].notna()].sort_index()
if len(df) < 30:
continue
# ===== 株式分割補正 =====
df, split_adjusted, split_date = adjust_for_stock_split(df)
# ======================
# 移動平均
# ======================
df["MA5"] = df["Close"].rolling(5).mean()
df["MA25"] = df["Close"].rolling(25).mean()
df["VolMA5"] = df["Volume"].rolling(5).mean()
df.dropna(subset=["MA5", "MA25"], inplace=True)
if len(df) < 6:
continue
# ======================
# 最新営業日
# ======================
latest = df.iloc[-1]
prev5 = df.iloc[-6]
close = latest["Close"]
latest_volume = int(latest["Volume"])
ma5 = latest["MA5"]
ma25 = latest["MA25"]
if latest_volume < 100000:
continue
# ======================
# 判定ロジック
# ======================
ma5_up = ma5 > df["MA5"].iloc[-2]
ma5_turn = False
for j in range(2, 6):
if df["MA5"].iloc[-j] < df["MA5"].iloc[-j-1] and ma5 > df["MA5"].iloc[-2]:
ma5_turn = True
break
ma25_up = ma25 > df["MA25"].iloc[-2]
close_over_ma5 = close >= ma5
dist_now = abs(ma5 - ma25)
dist_prev = abs(prev5["MA5"] - prev5["MA25"])
ma_converging = dist_now < dist_prev
ma5_over_ma25 = ma5 > ma25
ma_diff_pct = (ma5 - ma25) / ma25 * 100
# ======================
# 出来高ブレイク(表示専用)
# ======================
vol_break = ""
for j in range(1, 6):
if df["Volume"].iloc[-j] > df["VolMA5"].iloc[-j] * 1.5:
vol_break = "○"
break
# ======================
# ↑スコア(出来高除外)
# ======================
score = sum([
ma5_up,
ma5_turn,
ma25_up,
close_over_ma5,
ma_converging,
ma5_over_ma25
])
# ===== 分割直後スコア調整 =====
if split_adjusted:
days_from_split = (df.index[-1] - split_date).days
if days_from_split <= 30:
score = max(score - 1, 0)
# ======================
# name / market 成形
# ======================
code = t.replace(".T", "")
name = raw_name.replace("ホールディングス","HD").replace("グループ","G").replace("株式会社","").strip()
market = raw_market.replace("(内国株式)","").replace("内国株式","").replace("プライム","P").replace("スタンダード","S").replace("グロース","G").strip()
# ======================
# 結果格納
# ======================
results.append([
code, name, market,
round(close,2),
latest_volume,
round(ma5,2),
round(ma25,2),
arrow(ma5_up),
arrow(ma5_turn),
arrow(ma25_up),
arrow(close_over_ma5),
arrow(ma_converging),
arrow(ma5_over_ma25),
round(ma_diff_pct,2),
vol_break,
"○" if split_adjusted else "",
score
])
# ======================
# 出力
# ======================
df_out = pd.DataFrame(results, columns=[
"コード","銘柄名","市場",
"終値","直近出来高",
"MA5","MA25",
"5日線向き",
"5日線折返",
"25日線向き",
"終値≧5日線",
"MA接近",
"5日線≧25日線",
"5日線乖離率(%)",
"出来高ブレイク",
"分割補正",
"スコア"
])
today = datetime.datetime.today().strftime("%Y%m%d")
df_out.to_csv(f"{today}_ma_screen.csv", index=False, encoding="utf-8-sig")
print(f"✅ 完了:{len(df_out)} 銘柄抽出")
実行結果の例(25/12/26)
- プライムのみ
- 5日線上向き
- 終値が5日線より上
- 25日線より5日線が下
※リストはコード順です
| コード | 銘柄名 | 市場 | 終値 | MA5 | MA25 | スコア |
|---|---|---|---|---|---|---|
| 1885 | 東亜建設工業 | P | 2759 | 2752.8 | 2781.92 | 4 |
| 1887 | 日本国土開発 | P | 532 | 527.4 | 530.17 | 5 |
| 1893 | 五洋建設 | P | 1559.5 | 1548.4 | 1595.52 | 4 |
| 1911 | 住友林業 | P | 1614 | 1608.4 | 1608.8 | 5 |
| 2353 | 日本駐車場開発 | P | 269 | 261.8 | 264 | 4 |
| 2371 | カカクコム | P | 2355 | 2239.8 | 2276.26 | 4 |
| 2461 | ファンコミュニケーションズ | P | 531 | 529.4 | 532.6 | 5 |
| 2533 | オエノンHD | P | 516 | 513 | 519.48 | 4 |
| 2670 | エービーシー・マート | P | 2706 | 2683.6 | 2702.06 | 4 |
| 2802 | 味の素 | P | 3374 | 3357.8 | 3455.88 | 4 |
| 2931 | ユーグレナ | P | 403 | 400.2 | 405.04 | 4 |
| 3103 | ユニチカ | P | 277 | 266 | 282.32 | 4 |
| 3197 | すかいらーくHD | P | 3527 | 3489.8 | 3529.32 | 4 |
| 3387 | クリエイト・レストランツ・HD | P | 776 | 762.8 | 773.84 | 4 |
| 3397 | トリドールHD | P | 4277 | 4220.8 | 4311.08 | 4 |
| 3903 | gumi | P | 356 | 348.2 | 368.52 | 4 |
| 3993 | PKSHA Technology | P | 3455 | 3373 | 3373 | 5 |
| 4151 | 協和キリン | P | 2570 | 2541.1 | 2569.74 | 4 |
| 4180 | Appier Group | P | 1109 | 1096.2 | 1116.84 | 4 |
| 4369 | トリケミカル研究所 | P | 2763 | 2732 | 2814.96 | 5 |
| 4483 | JMDC | P | 3800 | 3769 | 3929.8 | 4 |
| 4506 | 住友ファーマ | P | 2384 | 2332 | 2419.58 | 4 |
| 4523 | エーザイ | P | 4703 | 4601.6 | 4688.52 | 5 |
| 4544 | H.U.GHD | P | 3472 | 3389 | 3417.84 | 4 |
| 4927 | ポーラ・オルビスHD | P | 1343 | 1332.1 | 1332.3 | 5 |
| 4974 | タカラバイオ | P | 791 | 789.4 | 799.68 | 4 |
| 5541 | 大平洋金属 | P | 2255 | 2173.6 | 2253.48 | 4 |
| 5602 | 栗本鐵工所 | P | 1685 | 1670 | 1693.8 | 4 |
| 5715 | 古河機械金属 | P | 3780 | 3755 | 3868.6 | 5 |
| 5726 | 大阪チタニウムテクノロジーズ | P | 1885 | 1855.4 | 1954.52 | 4 |
| 6047 | Gunosy | P | 533 | 530.4 | 541.56 | 4 |
| 6055 | ジャパンマテリアル | P | 1570 | 1564 | 1619.76 | 4 |
| 6141 | DMG森精機 | P | 2663.5 | 2653 | 2662.4 | 5 |
| 6240 | ヤマシンフィルタ | P | 597 | 591.6 | 606.52 | 4 |
| 6383 | ダイフク | P | 4925 | 4921.4 | 4964.68 | 4 |
| 6417 | SANKYO | P | 2554 | 2550.2 | 2585.08 | 4 |
| 6472 | NTN | P | 360.5 | 359.24 | 366.55 | 4 |
| 6526 | ソシオネクスト | P | 2200.5 | 2198 | 2217.58 | 5 |
| 6544 | ジャパンエレベーターサービスHD | P | 1780.5 | 1762.8 | 1815.98 | 4 |
| 6740 | ジャパンディスプレイ | P | 21 | 20.2 | 20.24 | 5 |
| 6754 | アンリツ | P | 2266 | 2253.6 | 2276.24 | 5 |
| 6758 | ソニーG | P | 4045 | 4020.6 | 4241.72 | 4 |
| 6952 | カシオ計算機 | P | 1261.5 | 1260.6 | 1266.6 | 5 |
| 6966 | 三井ハイテック | P | 747 | 742.2 | 782.12 | 4 |
| 6981 | 村田製作所 | P | 3207 | 3188.4 | 3247.96 | 4 |
| 7071 | アンビスHD | P | 457 | 448.2 | 459.24 | 4 |
| 7095 | Macbee Planet | P | 1555 | 1498.4 | 1647.2 | 4 |
| 7731 | ニコン | P | 1756.5 | 1735.1 | 1788.98 | 5 |
| 7867 | タカラトミー | P | 2750 | 2733.3 | 2773 | 4 |
| 8113 | ユニ・チャーム | P | 901.8 | 896 | 902.25 | 4 |
| 8697 | 日本取引所G | P | 1697.5 | 1684.3 | 1720.16 | 4 |
| 8801 | 三井不動産 | P | 1785 | 1779.4 | 1789.24 | 5 |
| 9007 | 小田急電鉄 | P | 1731.5 | 1703.5 | 1716.42 | 4 |
| 9023 | 東京地下鉄 | P | 1605 | 1580.7 | 1591.04 | 5 |
| 9042 | 阪急阪神HD | P | 3970 | 3904 | 3908.48 | 5 |
| 9064 | ヤマトHD | P | 2215 | 2194.2 | 2204.2 | 4 |
| 9229 | サンウェルズ | P | 359 | 348.2 | 384.56 | 4 |
| 9418 | U-NEXT HOLDINGS | P | 2002 | 1968.2 | 1991.12 | 5 |
| 9434 | ソフトバンク | P | 217.1 | 215.42 | 218.38 | 4 |
| 9468 | KADOKAWA | P | 3172 | 3116.8 | 3180.28 | 4 |
| 9517 | イーレックス | P | 626 | 619.2 | 624.32 | 4 |
| 9552 | M&A総研HD | P | 1167 | 1133.8 | 1158.24 | 4 |
| 9684 | スクウェア・エニックス・HD | P | 2903.5 | 2869.5 | 2955.98 | 4 |
| 9697 | カプコン | P | 3674 | 3599.4 | 3668.52 | 4 |
| 9766 | コナミG | P | 21560 | 21437 | 22502.2 | 4 |
| 9861 | 吉野家HD | P | 3099 | 3057.6 | 3080.16 | 4 |
アウトプット例:大阪チタニウムテクノロジーズ(5726)

おわりに
「底を当てる」よりも、
「市場の流れに乗り、取れるところを取る」
という考え方に変えてから、
トレードのストレスがかなり減りました。
このスクリーニングも、
そのための 補助輪 のような位置づけです。
同じように、
- 逆張りで疲れている方
- 感覚トレードから抜けたい方
のヒントになればうれしいです。


コメント