LoginSignup
9
2

More than 5 years have passed since last update.

Smart Tradeでアルゴリズムを実装してみる(エルダー線を用いたアルゴリズム)

Last updated at Posted at 2017-09-30

Smart Tradeとは

Smart Tradeとは株式会社Smart Tradeが提供する、株式投資アルゴリズムの開発プラットフォームサービスの名称
現在はβ版だが、近々正式版がリリースされるとの事
(2017/11/25追記:2017/11/20に正式版リリース完了、サービス名称も「QuantX」に)
Pythonを用いてトレーディングアルゴリズムを記述することができ、バックテストも簡便に行うことができる機能を備えている

下記HPからサービスを利用する事が可能
https://smarttrade.co.jp

実装したアルゴリズムの元ネタ

書籍名:投資苑 心理・戦略・資金管理
著者:アレキサンダー・エルダー
訳者:福井強
2000年8月 初版第1刷発行
第8章 新しいテクニカル指標 エルダー線 の頁より

アルゴリズムの説明

利用するテクニカル指標

エルダー線に基づくトレーディングルールは下記の3つの指標を利用
1.直近13日間の指数移動平均線(EMA)
2.ブル・パワー(=日足のチャートの高値-13日EMA)
3.ベア・パワー(=日足のチャートの安値-13日EMA)

トレーディングルール

買いのルール

1.トレンドが上向きである(当日の株価がEMAより上にある)
2.ベア・パワーが負の数値であるものの、上昇している
3.ブル・パワーの直近の天井が前回の天井を上回っている
(4.ベア・パワーが価格との強気の乖離を形成して上昇しつつある)
*本文中には望ましいが必須ではないとの事なので今回は割愛

売りのルール

1.トレンドが下向きである(当日の株価がEMAより下にある)
2.ブル・パワーが正の数値であるものの、下落している
3.ベア・パワーの直近の底が前回の底を下回っている
(4.ブル・パワーが価格との弱気の乖離を形成して下落しつつある)
*本文中には望ましいが必須ではないとの事なので今回は割愛

実装したコード

13日間の指数移動平均線

EMA.py
#buy signal
#終値を設定
cp = data["close_price_adj"].fillna(method='ffill')
pd_cp = pd.DataFrame(data=cp,columns=cp.columns, index=cp.index)
#売買条件判定用
result1 = pd.DataFrame(data=cp,columns=cp.columns, index=cp.index)
result2 = pd.DataFrame(data=cp,columns=cp.columns, index=cp.index)
#EMAの計算        
EMA13 = {}
for (sym,val) in cp.items():
    EMA13[sym] = ta.EMA(cp[sym].values.astype(np.double),timeperiod=13)

pd_EMA13 = pd.DataFrame(data=EMA13,columns=cp.columns, index=cp.index)
#EMAの売買条件判定のための指標を設定
EMA_ratio = cp / pd_EMA13

ブル・パワー

bull_power.py
#bull power
hp = data["high_price_adj"].fillna(method='ffill')
bull_power = hp - pd_EMA13

pd_bull_power = pd.DataFrame(data=bull_power)
#直近25日間でのブル・パワーの最大値
pd_bull_power_max = pd.DataFrame(data=bull_power).rolling(25).max()
pd_bull_power_shift = pd_bull_power.shift(periods=2)
#ブル・パワーの差分(負なら2日前より下落している)
delta_bull = pd_bull_power - pd_bull_power_shift

ベア・パワー

bear_power.py
#bear_power < 0 & delta > 0
lp = data["low_price_adj"].fillna(method='ffill')
bear_power = lp - pd_EMA13

pd_bear_power = pd.DataFrame(data=bear_power)
#直近25日間でのベア・パワーの最小値
pd_bear_power_min = pd.DataFrame(data=bear_power).rolling(25).min()
pd_bear_power_shift = pd_bear_power.shift(periods=2)
#ベア・パワーの差分(正なら2日前より上昇している)
delta_bear = pd_bear_power - pd_bear_power_shift

売買条件の設定

buy_sell.py
buy_sig =  result1[(EMA_ratio>1) & (bear_power < 0) & (delta_bear > 0) & (pd_bull_power == pd_bull_power_max)]
sell_sig = result2[(EMA_ratio<1) & (bull_power > 0) & (delta_bull < 0) & (pd_bear_power == pd_bear_power_min)]

コード全体

elder_ray.py
import pandas as pd
import talib as ta
import numpy as np

def initialize(ctx):

    ctx.logger.debug("initialize() called")
    ctx.configure(
      target="jp.stock.daily",
      channels={          
        "jp.stock": {
          "symbols": [
             "jp.stock.7203",  "jp.stock.7267",
                "jp.stock.7270",  "jp.stock.7269",
                "jp.stock.7259",  "jp.stock.7202",
                "jp.stock.7203",  "jp.stock.7205",
                "jp.stock.7208",  "jp.stock.7211"
          ],
          "columns": [
            #"open_price_adj",    
            "high_price_adj",    
            "low_price_adj",     
            #"close_price1",       
            "close_price_adj",     
            #"volume_adj",         
            #"txn_volume",         
          ]
        }
      }
    )

    def _elder_ray_signal(data):

        #buy signal
        cp = data["close_price_adj"].fillna(method='ffill')
        pd_cp = pd.DataFrame(data=cp,columns=cp.columns, index=cp.index)
        result1 = pd.DataFrame(data=cp,columns=cp.columns, index=cp.index)
        result2 = pd.DataFrame(data=cp,columns=cp.columns, index=cp.index)

        EMA13 = {}

        for (sym,val) in cp.items():
          EMA13[sym] = ta.EMA(cp[sym].values.astype(np.double),timeperiod=13)

        pd_EMA13 = pd.DataFrame(data=EMA13,columns=cp.columns, index=cp.index)
        #EMA condition
        EMA_ratio = cp / pd_EMA13

        #bear power
        #bear_power < 0 & delta > 0
        lp = data["low_price_adj"].fillna(method='ffill')
        bear_power = lp - pd_EMA13

        pd_bear_power = pd.DataFrame(data=bear_power)
        pd_bear_power_min = pd.DataFrame(data=bear_power).rolling(25).min()
        pd_bear_power_shift = pd_bear_power.shift(periods=2)
        delta_bear = pd_bear_power - pd_bear_power_shift

        #sell signal

        #bull power
        hp = data["high_price_adj"].fillna(method='ffill')
        bull_power = hp - pd_EMA13

        pd_bull_power = pd.DataFrame(data=bull_power)
        pd_bull_power_max = pd.DataFrame(data=bull_power).rolling(25).max()
        pd_bull_power_shift = pd_bull_power.shift(periods=2)
        delta_bull = pd_bull_power - pd_bull_power_shift


        buy_sig =  result1[(EMA_ratio>1) & (bear_power < 0) & (delta_bear > 0) & (pd_bull_power == pd_bull_power_max)]
        sell_sig = result2[(EMA_ratio<1) & (bull_power > 0) & (delta_bull < 0) & (pd_bear_power == pd_bear_power_min)]

        return {
            "EMA13_price": pd_EMA13,
            "bear_power": bear_power,
            "delta_bear": delta_bear,
            "bull_power": bull_power,
            "bull_power_max": pd_bull_power_max,
            "delta_bull": delta_bull,
            "cp":cp,
            "buy:sig": buy_sig,
            "sell:sig": sell_sig,
            "buy:sig2": (EMA_ratio>1) & (bear_power < 0) & (delta_bear > 0) & (pd_bull_power == pd_bull_power_max),
            "sell:sig2": (EMA_ratio<1) & (bull_power > 0) & (delta_bull < 0) & (pd_bear_power == pd_bear_power_min)
        }


    ctx.regist_signal("elder_ray_signal", _elder_ray_signal)


def handle_signals(ctx, date, current):

    buy = current["buy:sig"].dropna()
    for (sym, val) in buy.items():

        sec = ctx.getSecurity(sym)
        sec.order(sec.unit()*5, comment="シグナル買(%f)" % val)
        pass

    sell = current["sell:sig"].dropna()

    for (sym, val) in sell.items():

        sec = ctx.getSecurity(sym)
        sec.order_target_percent(0, comment="シグナル売(%f)" % val)
        pass

    pass

バックテスト結果

実行したアルゴリズムに関してバックテストの結果を下記にて記載

バックテスト条件

バックテストの条件は
資金:1000万円
バックテスト期間:2015/7/1-2017/3/31の21カ月間を設定
スライド1.PNG

売買に用いた銘柄

バックテストにはデフォルトでサービスに入っていた自動車関連銘柄のセットを使用
スライド3.PNG

バックテスト結果サマリー

バックテストの結果、ベンチマークである日経225をアウトパフォームして、期間の損益率9.36%を達成
スライド2.PNG

個別銘柄の売買の様子

個別銘柄の売買の様子
アイシン精機は良いタイミングで売買出来ているように見える
(赤丸:買いのポイント、青丸:売りのポイント、縦線は発生した売買シグナル)
売買シグナルが発生したのに売買が行われていない箇所は、ポジションが一杯もしくは0だったと思われる
スライド4.PNG
トヨタ自動車は負けている
スライド5.PNG
ホンダも同じく負けている
スライド6.PNG

単純にロングした場合との比較

ロングのみのコード

long.py
    def long_only(data):
        data = data["close_price_adj"].fillna(method='ffill')


        buy_sig = data[(data>-10)]
        sell_sig = data[(data<0)]
        return {
            "data": data,
            "buy:sig": buy_sig,
            "sell:sig": sell_sig,
        }

    # シグナル登録
    ctx.regist_signal("long_only", long_only)

def handle_signals(ctx, date, current):

    buy = current["buy:sig"].dropna()

    for (sym, val) in buy.items():

        sec = ctx.getSecurity(sym)
        sec.order(500000/val, comment="シグナル買(%f)" % val)
        pass

    sell = current["sell:sig"].dropna()
    for (sym, val) in sell.items():

        sec = ctx.getSecurity(sym)
        sec.order_target_percent(0, comment="シグナル売(%f)" % val)
        pass

    pass

単純にロングした場合というのは下記のような状況
スライド7.PNG
パフォーマンスは日経225と同じような推移
スライド8.PNG
スライド9.PNG

今後の取り組み

優良なアルゴリズムの実装と開発

9
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
2