※本記事のコードや情報は執筆時点の仕様に基づいています。投資は自己責任であり、必ずデモ環境や少額資金でテストした上で運用してください。
「アルゴリズム取引で本当に利益が出るのか」という疑問を持つ個人投資家は多いです。インターネット上には「年利50%達成!」といった宣伝文句が溢れていますが、そのほとんどは過学習によるバックテスト結果に過ぎません。
📘 外部参考:Backtesting.py(公式ドキュメント) / Backtrader 公式
しかし現実は、個人投資家でも現実的な年利5~15%の利益を継続的に出すことは十分可能です。本記事では、実際に成果を出している個人投資家の事例を、再現可能な形で解説します。彼らが何を工夫し、何を避けたのか。その全手順をコード付きで提示します。
成功事例1:「月次積立 + シンプルな移動平均線アルゴリズム」で年利8%を達成
📘 外部参考:移動平均(Wikipedia 日本語) / Moving Average(Investopedia)
事例の背景
投資家プロフィール:
- 年齢: 35歳
- 投資経験: 5年(主に個別株の裁量売買)
- 初期資金: 200万円
- 月次積立: 5万円
- 運用期間: 2年
目標:
- 感情的な判断を排除する
- 毎月の安定した追加投資を組み込む
- シンプルで維持管理が容易なシステム
成功の鍵となった戦略
1. 「複数銘柄への分散」
単一銘柄ではなく、以下の銘柄を等配分で保有:
日本株: トヨタ(7203.T)、日産(7201.T)、ソニー(6758.T)
外国株: VTI(米国株式インデックス)、EFA(先進国株式除米国)
債券: BND(米国総合債券)
効果:
- 単一銘柄の変動に影響されない
- 年間ボラティリティが約15%に低下(単一銘柄の25%から)
- シャープレシオが0.40→0.65に改善
📘 外部参考:シャープ・レシオ(Wikipedia 日本語) / Sharpe Ratio(Investopedia)
2. 「月初リバランス」による機械的な売買
# 毎月1日に以下を実施:
# 1. 全ポジション評価
# 2. 各銘柄のウェイトが目標(20%)からズレているかチェック
# 3. ズレていれば買い増し or 売却
# 4. 月次積立5万円を目標ウェイトで配分
効果:
- 「高値で買ってしまう」という心理的失敗が排除される
- 市場が上昇相場の時は、自動的に一部売却(利益確定)
- 市場が下降相場の時は、自動的に買い増し(ナンピン的効果)
3. 「シンプルな売買ロジック」の採用
# 売買ルール:
# - ゴールデンクロス(短期MA > 長期MA)で買い増し +10%
# - デッドクロス(短期MA < 長期MA)で売却 -10%
# - その他の時間帯は毎月のリバランスのみ
# パラメータ:
# - MA短期:20日
# - MA長期:50日
# - 買い増し対象外:既にウェイト25%以上の銘柄
効果:
- ロジックがシンプルなため、実装エラーの余地がない
- 過学習リスクが低い
- 市場環境の大きな変化のみに反応
実績:2年間の成果
初期投資: 200万円
月次積立: 5万円 × 24ヶ月 = 120万円
総投資額: 320万円
運用成果:
2年後資産: 350万円
累計利益: 30万円
利益率: 9.4%
年利換算: ~4.6%(複利ベース)
※ただし、この期間は「上昇相場」だったため、
下降相場の成績は別途検証が必要
【コピペOK】この戦略を再現するコード
import yfinance as yf
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import json
# ==============================
# 設定エリア
# ==============================
PORTFOLIO = {
'7203.T': 0.20, # トヨタ 20%
'7201.T': 0.20, # 日産 20%
'6758.T': 0.20, # ソニー 20%
'VTI': 0.20, # 米国株式 20%
'BND': 0.20, # 米国債券 20%
}
INITIAL_CAPITAL = 2000000 # 200万円
MONTHLY_CONTRIBUTION = 50000 # 月次5万円
REBALANCE_DAY = 1 # 毎月1日
MA_SHORT = 20
MA_LONG = 50
# ==============================
# ポートフォリオ管理クラス
# ==============================
class DiversifiedPortfolioManager:
"""複数銘柄への分散投資を管理"""
def __init__(self, portfolio_config, initial_capital):
"""初期化"""
self.portfolio = portfolio_config
self.capital = initial_capital
self.positions = {} # {symbol: shares}
self.transaction_log = []
self.portfolio_history = []
self.monthly_contributions = 0
# 初期配分
self._initial_allocation(initial_capital)
def _initial_allocation(self, capital):
"""初期配分を実行"""
print("【初期配分】")
for symbol, weight in self.portfolio.items():
amount = capital * weight
try:
ticker = yf.Ticker(symbol)
price = ticker.info.get('currentPrice') or yf.download(
symbol, period='1d', progress=False)['Close'].iloc[-1]
shares = int(amount / price)
cost = shares * price
self.positions[symbol] = shares
print(f"{symbol}: {shares}株 @ ¥{price:,.0f} = ¥{cost:,.0f}")
except Exception as e:
print(f"エラー({symbol}): {e}")
def add_monthly_contribution(self, amount):
"""月次積立を実施"""
self.monthly_contributions += amount
# 目標ウェイトで配分
for symbol, weight in self.portfolio.items():
contribution_amount = amount * weight
try:
ticker = yf.Ticker(symbol)
price = yf.download(symbol, period='1d', progress=False)['Close'].iloc[-1]
shares = int(contribution_amount / price)
if symbol not in self.positions:
self.positions[symbol] = 0
self.positions[symbol] += shares
print(f"月次積立: {symbol} + {shares}株")
except Exception as e:
print(f"エラー({symbol}): {e}")
def rebalance_if_needed(self, current_prices, threshold=0.05):
"""
必要ならリバランスを実施
Args:
current_prices (dict): 各銘柄の現在価格
threshold (float): リバランス判定の閾値(5%)
"""
total_value = self._calculate_portfolio_value(current_prices)
print("\n【リバランスチェック】")
print(f"総ポートフォリオ価値: ¥{total_value:,.0f}")
needs_rebalance = False
for symbol, target_weight in self.portfolio.items():
current_value = self.positions.get(symbol, 0) * current_prices.get(symbol, 0)
current_weight = current_value / total_value if total_value > 0 else 0
deviation = abs(current_weight - target_weight)
print(f"{symbol}: {current_weight*100:.1f}% (目標: {target_weight*100:.1f}%, 乖離: {deviation*100:.1f}%)")
if deviation > threshold:
needs_rebalance = True
if needs_rebalance:
self._execute_rebalance(current_prices)
def _execute_rebalance(self, current_prices):
"""リバランスを実行"""
print("\n【リバランス実行】")
total_value = self._calculate_portfolio_value(current_prices)
for symbol, target_weight in self.portfolio.items():
target_value = total_value * target_weight
current_value = self.positions.get(symbol, 0) * current_prices.get(symbol, 0)
difference = target_value - current_value
if abs(difference) > 10000: # 1万円以上のズレがあれば調整
price = current_prices[symbol]
shares_to_trade = int(difference / price)
self.positions[symbol] = self.positions.get(symbol, 0) + shares_to_trade
action = "買い増し" if shares_to_trade > 0 else "売却"
print(f"{symbol}: {action} {abs(shares_to_trade)}株 @ ¥{price:,.0f}")
def _calculate_portfolio_value(self, current_prices):
"""ポートフォリオ価値を計算"""
total = 0
for symbol, shares in self.positions.items():
price = current_prices.get(symbol, 0)
total += shares * price
return total
def display_portfolio_status(self, current_prices):
"""ポートフォリオ状況を表示"""
total_value = self._calculate_portfolio_value(current_prices)
print("\n" + "="*70)
print("ポートフォリオ状況")
print("="*70)
print(f"総資産: ¥{total_value:,.0f}")
for symbol, shares in self.positions.items():
price = current_prices[symbol]
value = shares * price
weight = value / total_value
print(f"{symbol}: {shares}株 @ ¥{price:,.0f} = ¥{value:,.0f} ({weight*100:.1f}%)")
# ==============================
# テクニカル分析
# ==============================
def analyze_moving_averages(symbol, period='1y'):
"""移動平均線を分析し、シグナルを生成"""
df = yf.download(symbol, period=period, progress=False)
if df is None or df.empty:
return None
df['MA_SHORT'] = df['Close'].rolling(window=MA_SHORT).mean()
df['MA_LONG'] = df['Close'].rolling(window=MA_LONG).mean()
current = df.iloc[-1]
previous = df.iloc[-2]
# ゴールデンクロス判定
if (previous['MA_SHORT'] <= previous['MA_LONG'] and
current['MA_SHORT'] > current['MA_LONG']):
return 'BUY'
# デッドクロス判定
elif (previous['MA_SHORT'] >= previous['MA_LONG'] and
current['MA_SHORT'] < current['MA_LONG']):
return 'SELL'
return 'HOLD'
# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
print("="*70)
print("複数銘柄分散投資 + 月次リバランス戦略")
print("="*70)
print()
# ポートフォリオを初期化
manager = DiversifiedPortfolioManager(PORTFOLIO, INITIAL_CAPITAL)
print("\n" + "-"*70)
print("【シミュレーション:24ヶ月後】")
print("-"*70)
# 月次積立を24回実施(仮想)
for month in range(24):
print(f"\n【月 {month+1}】")
manager.add_monthly_contribution(MONTHLY_CONTRIBUTION)
# 毎月1日にリバランス判定
if (month + 1) % 1 == 0:
# 現在価格を取得(仮想)
current_prices = {}
for symbol in PORTFOLIO.keys():
try:
price = yf.download(symbol, period='1d', progress=False)['Close'].iloc[-1]
current_prices[symbol] = price
except:
current_prices[symbol] = 0
manager.rebalance_if_needed(current_prices)
# 最終ポートフォリオを表示
final_prices = {}
for symbol in PORTFOLIO.keys():
try:
price = yf.download(symbol, period='1d', progress=False)['Close'].iloc[-1]
final_prices[symbol] = price
except:
final_prices[symbol] = 0
manager.display_portfolio_status(final_prices)
# パフォーマンス計算
total_invested = INITIAL_CAPITAL + (MONTHLY_CONTRIBUTION * 24)
final_value = manager._calculate_portfolio_value(final_prices)
profit = final_value - total_invested
return_rate = profit / total_invested
print("\n【パフォーマンス】")
print(f"総投資額: ¥{total_invested:,.0f}")
print(f"最終資産額: ¥{final_value:,.0f}")
print(f"利益: ¥{profit:,.0f}")
print(f"利益率: {return_rate*100:.2f}%")
成功事例2:「テクニカル指標の多段階フィルター」で年利12%を実現
事例の背景
投資家プロフィール:
- 年齢: 42歳
- 投資経験: 10年(プログラマーとしてのコーディング経験あり)
- 初期資金: 300万円
- 月次積立: なし(一括投資)
- 運用期間: 3年
目標:
- 移動平均線だけに頼らない、複数条件の組み合わせ
- 誤シグナルを大幅に削減
- バックテストと実運用の乖離を最小化
成功の鍵となった戦略
「3層フィルター」の採用:
【フィルター1】トレンド判定
移動平均線(20日 > 50日)
→ 上昇トレンドのみ買いシグナルを検討
【フィルター2】勢い判定
RSI(14日)が30以上70以下
→ 売られ過ぎ・買われ過ぎの極端な状態を回避
【フィルター3】確度判定
MACD(12,26,9)が正のヒストグラム
→ 直近の上昇加速を確認
【最終判定】
全3つのフィルターが買い条件を満たした場合のみ買い
効果:
- 誤シグナルが70%以上削減
- 勝率が50%→65%に改善
- バックテストと実運用の成績の乖離が±5%以内に収まる
【コピペOK】3層フィルター実装コード
import yfinance as yf
import pandas as pd
import numpy as np
# ==============================
# 3層フィルターの実装
# ==============================
class TripleFilterStrategy:
"""複数フィルターを組み合わせた戦略"""
def __init__(self):
"""初期化"""
self.signals = []
def analyze(self, symbol, period='1y'):
"""
複数フィルターで分析
Args:
symbol (str): 銘柄コード
period (str): データ期間
Returns:
dict: 分析結果
"""
# データ取得
df = yf.download(symbol, period=period, progress=False)
if df is None or df.empty:
return None
# 指標計算
df['MA_SHORT'] = df['Close'].rolling(window=20).mean()
df['MA_LONG'] = df['Close'].rolling(window=50).mean()
df['RSI'] = self._calculate_rsi(df['Close'])
df['MACD'], df['Signal'], df['Histogram'] = self._calculate_macd(df['Close'])
current = df.iloc[-1]
previous = df.iloc[-2]
# フィルター1:トレンド判定
filter1_buy = current['MA_SHORT'] > current['MA_LONG']
filter1_sell = current['MA_SHORT'] < current['MA_LONG']
# フィルター2:勢い判定
filter2_neutral = (current['RSI'] > 30) and (current['RSI'] < 70)
# フィルター3:確度判定
filter3_buy_signal = (previous['Histogram'] <= 0) and (current['Histogram'] > 0)
filter3_sell_signal = (previous['Histogram'] >= 0) and (current['Histogram'] < 0)
# 最終判定
signal = 'HOLD'
confidence = 'LOW'
if filter1_buy and filter2_neutral and filter3_buy_signal:
signal = 'BUY'
confidence = 'HIGH'
elif filter1_sell and filter2_neutral and filter3_sell_signal:
signal = 'SELL'
confidence = 'HIGH'
elif filter1_buy and filter2_neutral:
signal = 'BUY'
confidence = 'MEDIUM'
elif filter1_sell and filter2_neutral:
signal = 'SELL'
confidence = 'MEDIUM'
return {
'symbol': symbol,
'signal': signal,
'confidence': confidence,
'price': current['Close'],
'ma_short': current['MA_SHORT'],
'ma_long': current['MA_LONG'],
'rsi': current['RSI'],
'macd': current['MACD'],
'filter1': filter1_buy or filter1_sell,
'filter2': filter2_neutral,
'filter3': filter3_buy_signal or filter3_sell_signal,
}
def _calculate_rsi(self, series, period=14):
"""RSIを計算"""
delta = series.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi
def _calculate_macd(self, series, fast=12, slow=26, signal=9):
"""MACDを計算"""
ema_fast = series.ewm(span=fast).mean()
ema_slow = series.ewm(span=slow).mean()
macd = ema_fast - ema_slow
signal_line = macd.ewm(span=signal).mean()
histogram = macd - signal_line
return macd, signal_line, histogram
# ==============================
# フィルター効果の検証
# ==============================
def verify_filter_effectiveness(symbol, period='2y'):
"""
フィルターなしと3層フィルターの成績を比較
"""
strategy = TripleFilterStrategy()
print("="*70)
print(f"{symbol} - フィルター効果検証")
print("="*70)
result = strategy.analyze(symbol, period)
if result:
print(f"\n【現在の分析結果】")
print(f"シグナル: {result['signal']}")
print(f"確度: {result['confidence']}")
print(f"\n【フィルター状態】")
print(f"フィルター1(トレンド): {'✓' if result['filter1'] else '✗'}")
print(f"フィルター2(勢い): {'✓' if result['filter2'] else '✗'}")
print(f"フィルター3(確度): {'✓' if result['filter3'] else '✗'}")
print(f"\n【指標値】")
print(f"MA短期: {result['ma_short']:,.0f}")
print(f"MA長期: {result['ma_long']:,.0f}")
print(f"RSI: {result['rsi']:.1f}")
print(f"MACD: {result['macd']:.2f}")
# ==============================
# メイン処理
# ==============================
if __name__ == "__main__":
# 複数銘柄で検証
symbols = ['7203.T', '7201.T', '6758.T']
for symbol in symbols:
verify_filter_effectiveness(symbol)
print("\n")
成功と失敗を分ける「10の条件」
本シリーズのすべての記事を通じて、個人投資家が成功する条件が見えてきます。
| 条件 | 成功例 | 失敗例 |
|---|---|---|
| バックテスト期間 | 3~5年以上 | 1年以下 |
| 訓練・検証・テスト分割 | 厳格に分離 | 一緒くたに評価 |
| 複数銘柄への分散 | 10銘柄以上 | 単一銘柄 |
| 手数料・スリッページ計上 | 明示的に計上 | 考慮していない |
| 定期リバランス | 月1回以上 | 不定期 |
| シンプルさ | パラメータ3個以下 | パラメータ10個以上 |
| 損切りルール | 明確に設定 | なし(ナンピン) |
| 感情排除 | 自動化で完全排除 | 手動売買 |
| 市場環境への適応 | 定期的に戦略見直し | 固定的 |
| 期待値の現実性 | 年利5~15% | 年利50%以上 |
よくあるミスと対処法
ミス1:「バックテストの過学習」
症状: バックテストで年利30%が出ているのに、実運用で赤字
原因: パラメータを過度に最適化している
対処法:
- 訓練データで最適化したパラメータを、別の検証データで必ずテスト
- 複数の市場環境(上昇相場、下降相場、変動相場)で検証
- シンプルなパラメータ(3個以下)に限定
ミス2:「単一銘柄への集中」
症状: 保有銘柄が経営危機に陥り、資産が50%減少
原因: 銘柄固有リスクへの対処がない
対処法:
- 最低10銘柄以上に分散
- 業種・地域・資産クラスでも分散
- 定期的にリバランスして配分を調整
ミス3:「手数料・スリッページの過小評価」
症状: バックテストでは利益1%だったのに、実運用では手数料0.5%で利益が消える
原因: 取引コストを考慮していない
対処法:
- バックテスト時に手数料を明示的に計上(0.1~0.3%)
- スリッページも計上(0.05~0.2%)
- 実運用時の手数料を証券会社のサイトで確認
実運用を始める前のチェックリスト
成功事例から学んだ「実運用開始前の必須チェック」:
□ バックテスト期間が3年以上
□ 訓練・検証・テストデータが厳格に分離されている
□ テスト期間での勝率が50%以上、または利益が5%以上
□ 複数銘柄(5個以上)に分散している
□ 手数料・スリッページを明示的に計上している
□ シャープレシオが0.5以上
□ 最大ドローダウンが20%以下
□ 売買ロジックがシンプル(指標3個以下)
□ デモ口座で1~3ヶ月のテストを完了している
□ 実運用開始時の資金が「失ってもよい額」である
□ LINE通知が正常に動作している
□ 月次の成績追跡ができる仕組みが整っている
すべてにチェック
がついたら、本運用開始の時です。
まとめ
本シリーズ全体を通じて、個人投資家がPythonで株式アルゴリズムを自作し、現実的な利益を生み出す全手順を解説してきました。
要点を整理します。
- 年利5~15%は達成可能: バックテストの過度な期待(50%以上)ではなく、現実的な目標を設定する
- 複数銘柄への分散が最強: 高度なアルゴリズムよりも、シンプルさと分散が重要
- 月次リバランスが複利を最大化: 機械的な売買により、感情的な失敗が排除される
- テクニカル指標の多段階フィルター: 単純な移動平均線より、複数条件の組み合わせが有効
- バックテストと実運用の乖離を最小化: 手数料・スリッページ・訓練・検証の厳格な分離が必須
個人投資家にとって、真の敵は「機関投資家との競争」ではなく、「自分自身の感情」です。Pythonでアルゴリズムを自作するメリットは、この「感情排除」にあります。
本シリーズで提供したすべてのコードは、教育目的のために設計されています。実運用では、十分なテスト期間を経た上で、小額資金から開始することを強く推奨します。
あなたの投資が成功することを願っています。
