Skip to content

Instantly share code, notes, and snippets.

@jitenspin
Last active May 9, 2020 02:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jitenspin/15980b6772012dedddd4a772db55a9df to your computer and use it in GitHub Desktop.
Save jitenspin/15980b6772012dedddd4a772db55a9df to your computer and use it in GitHub Desktop.
L-M戦略検証
import queue
import pandas as pd
import matplotlib.pyplot as plt
def yahoo_finance_open_close(filename):
df = pd.read_csv(filename)
series = []
for _, item in df.iterrows():
series.append({'open': item['Open'], 'close': item['Close']})
return series
class Tsumitate:
def __init__(self, per_day):
self.per_day = per_day
def run(self, chart, ignore_fee=False):
fee_rate = 0.00495 # SBI証券の手数料率 0.495% (消費税込)
if ignore_fee:
fee_rate = 0.0
fee_max = 22 # SBI証券の上限手数料 $22 (消費税込)
value_series = []
n_stocks = 0.0
cash = 0.0
for day in chart:
cash += self.per_day
# 買付時手数料を考慮
exec_amount = cash - min(cash * fee_rate, fee_max)
fee = min(exec_amount * fee_rate, fee_max)
n_stocks += exec_amount / day['open']
cash -= exec_amount + fee
value_series.append(n_stocks * day['close'] + cash)
return value_series
class LM:
def __init__(self, per_day, ma_len):
self.per_day = per_day # 一日に積み立てる額
self.n_stocks = 0.0 # 口数 (端数計算面倒なので float にしてしまう)
self.avg_cost = 0.0 # 平均取得金額
self.cash = 0 # 現金
self.ma = queue.Queue(maxsize=ma_len) # ma_len は移動平均線の長さ
# レバレッジ基準 (以下ベース) となるチャートと、レバレッジをかけたチャートを与えて、
# 資産額の推移を返す
def run(self, base_chart, leveraged_chart):
value_series = []
order = 'buy' # buy | sell. 最初は buy (積み立て含む)
for base, leveraged in zip(base_chart, leveraged_chart):
# 最初に現金に積み立て額を追加
self.cash += self.per_day
# レバ始値で売買
self.exec_order(leveraged['open'], order)
# 次の売買はベース終値で判断
order = self.next_order(base['close'])
# 現在の資産額はレバ終値で判断
value_series.append(self.value(leveraged['close']))
return value_series
# 次回の売買をどうするかを返す
def next_order(self, price):
if self.ma.full():
self.ma.get()
self.ma.put(price)
# 移動平均線を上回れば買い、下回れば売り
if price >= sum(list(self.ma.queue)) / self.ma.maxsize:
return 'buy'
else:
return 'sell'
else:
# とりあえず日数がたまるまでは積み立て
self.ma.put(price)
return 'buy'
# 注文を実行
# order='buy' : 全力買い
# order='sell' : 全力売り
def exec_order(self, price, order):
tax_rate = 0.20315 # ドル計算だけどとりあえず日本の譲渡益税率 20.315%
fee_rate = 0.00495 # SBI証券の手数料率 0.495% (消費税込)
fee_max = 22 # SBI証券の上限手数料 $22 (消費税込)
if order == 'buy':
# 手数料を考慮して買う (絶対現金がマイナスにならない程度の雑な計算)
exec_amount = self.cash - min(self.cash * fee_rate, fee_max)
# 端数処理面倒なので口数は float。誤差は気にしない
quantity = exec_amount / price
# 必ず self.cash > exec_amount になるので現金は足りる
fee = min(exec_amount * fee_rate, fee_max)
# 平均取得金額の更新
self.avg_cost = (self.avg_cost * self.n_stocks +
price * quantity) / (self.n_stocks + quantity)
self.n_stocks += quantity
self.cash -= exec_amount + fee
elif order == 'sell':
# 売却益の計算
exec_amount = self.n_stocks * price # 約定金額
tax = max((exec_amount - self.n_stocks * self.avg_cost)
* tax_rate, 0) # 利益が出ていなかったら税金は0
fee = min(exec_amount * fee_rate, fee_max)
self.cash += exec_amount - tax - fee
self.n_stocks = 0 # 全売却
self.avg_cost = 0 # 全売却
else:
return
def value(self, unit):
return self.n_stocks * unit + self.cash
def to_leveraged(base_chart, x=3):
chart = []
prev = base_chart[0] # 数を合わせるため、レバレッジ版の初日は1倍にしてしまう
for base in base_chart:
# 前日の終値からの騰落率の x 倍 (レバレッジの計算方法これであってる?)
open = base['open'] * (1 + (base['open'] / prev['close'] - 1) * x)
close = base['close'] * (1 + (base['close'] / prev['close'] - 1) * x)
chart.append({'open': open, 'close': close})
return chart
base_series = yahoo_finance_open_close('./SP500_daily.csv')[-200 * 10:]
leveraged_series = to_leveraged(base_series)
cash_series = [{'open': 1, 'close': 1} for _ in range(len(base_series))]
# 毎日10ドル積み立て
lm50_chart = LM(per_day=10, ma_len=50).run(base_series, leveraged_series)
lm100_chart = LM(per_day=10, ma_len=100).run(base_series, leveraged_series)
lm200_chart = LM(per_day=10, ma_len=200).run(base_series, leveraged_series)
lm400_chart = LM(per_day=10, ma_len=400).run(base_series, leveraged_series)
cash_chart = Tsumitate(per_day=10).run(cash_series, ignore_fee=True)
base_chart = Tsumitate(per_day=10).run(base_series)
lv_chart = Tsumitate(per_day=10).run(leveraged_series)
plt.plot(lm50_chart, label='L-M (50)')
plt.plot(lm100_chart, label='L-M (100)')
plt.plot(lm200_chart, label='L-M (200)')
plt.plot(lm400_chart, label='L-M (400)')
plt.plot(cash_chart, label='Cash')
plt.plot(base_chart, label='Buy and Hold (base)')
plt.plot(lv_chart, label='Buy and Hold (leveraged)')
plt.legend()
plt.show()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment