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

#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日間の指数移動平均線
```python: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
```

##ブル・パワー
```python: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
```
##ベア・パワー
```python: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
```
##売買条件の設定
```python: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)]
```

##コード全体
```python: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](https://qiita-image-store.s3.amazonaws.com/0/191566/8b190e3d-8f2e-4c5b-bfb7-2f0798ab0b1d.png)
##売買に用いた銘柄
バックテストにはデフォルトでサービスに入っていた自動車関連銘柄のセットを使用
![スライド3.PNG](https://qiita-image-store.s3.amazonaws.com/0/191566/a1eb8a1d-1d2a-cc48-a473-794b591f730c.png)
##バックテスト結果サマリー
バックテストの結果、ベンチマークである日経225をアウトパフォームして、期間の損益率9.36%を達成
![スライド2.PNG](https://qiita-image-store.s3.amazonaws.com/0/191566/5ef2d400-ad17-005e-da3c-1c2d6f1ee364.png)
##個別銘柄の売買の様子
個別銘柄の売買の様子
アイシン精機は良いタイミングで売買出来ているように見える
（赤丸：買いのポイント、青丸：売りのポイント、縦線は発生した売買シグナル）
売買シグナルが発生したのに売買が行われていない箇所は、ポジションが一杯もしくは0だったと思われる
![スライド4.PNG](https://qiita-image-store.s3.amazonaws.com/0/191566/f6e425c2-6a36-5cc2-3cc3-7ddae5817916.png)
トヨタ自動車は負けている
![スライド5.PNG](https://qiita-image-store.s3.amazonaws.com/0/191566/2cec7cd7-4030-2e22-0471-351412ddf419.png)
ホンダも同じく負けている
![スライド6.PNG](https://qiita-image-store.s3.amazonaws.com/0/191566/786ec05a-9ca4-42fc-b94c-4479edd7958b.png)

##単純にロングした場合との比較
###ロングのみのコード
```python: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](https://qiita-image-store.s3.amazonaws.com/0/191566/6487d0a2-1728-1967-17c1-c0dd7124b8dc.png)
パフォーマンスは日経225と同じような推移
![スライド8.PNG](https://qiita-image-store.s3.amazonaws.com/0/191566/069453b0-d730-1659-85d1-6202f6627eb7.png)
![スライド9.PNG](https://qiita-image-store.s3.amazonaws.com/0/191566/4324dbed-719c-280c-80c5-409d99f5e7ea.png)

#今後の取り組み
優良なアルゴリズムの実装と開発