QuantXでゴールデンクロスストラテジーを書いてみた．

## ゴールデンクロス

 [QuantX](https://factory.quantx.io/) で， [ゴールデンクロス](http://www.rizumu.net/signal/%E3%82%B4%E3%83%BC%E3%83%AB%E3%83%87%E3%83%B3%E3%82%AF%E3%83%AD%E3%82%B9/)のサンプルコードを書いてみました．

ゴールデンクロスとは, ２５日移動平均線を５日移動平均線が上抜いたもの。上昇相場への突入を暗示し、買いシグナルとなるそうです．

## シグナル
調整済み終値で，前日は，長期移動平均が短期移動平均より大きかったが，当日，短期が長期を上回ったタイミング．

## 手仕舞い
損切り-3％，益出し+5％

## コード

```python
def initialize(ctx):
    ctx.logger.debug("initialize() called")
    # 設定    
    # ゴールデンクロスの期間設定
    ctx.long_term = 25 
    ctx.short_term = 5
    
    # ポートフォリオの何％をオーダーするか
    ctx.target = 0.05
    
    # 損切りと益出しの水準
    ctx.loss_cut = -0.03 
    ctx.plofit = 0.05
    
    # 銘柄と使う株価データの設定
    ctx.configure(
      channels={          # 利用チャンネル
        "jp.stock": {
          "symbols": [
            "jp.stock.1305", # ダイワ 上場投信-トピックス
            "jp.stock.9984", # ソフトバンク
            "jp.stock.9983", # ファーストリテリング
            "jp.stock.7201", # 日産
            "jp.stock.9201", # JAL
            "jp.stock.9202", # ANA
            "jp.stock.7203"  # トヨタ
          ],
          "columns": ["close_price_adj",    # 終値(株式分割調整後) 
          ]}})


    def _GOLDEN_CROSS(data):
      # 欠損値を埋める
      # cp は pandasのDataFrame（memo 参照）
      cp = data["close_price_adj"].fillna(method="ffill")
      ma_long_term = cp.rolling(window=ctx.long_term, center=False).mean()
      ma_short_term = cp.rolling(window=ctx.short_term, center=False).mean()
      
      # 長期移動平均を短期移動平均が上抜く
      # 前日は ma_long > ma_short で，
      # 当日は ma_long < ma_short
      buy_signal = (ma_long_term < ma_short_term) & (ma_long_term.shift(1) > ma_short_term.shift(1))
      
      return {
        "MA_long" : ma_long_term,
        "MA_short" : ma_short_term,
        "Ratio": ma_short_term / ma_long_term - 1,
        "buy:sig": buy_signal,
        
        }

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

def handle_signals(ctx, date, current):
    
    # ポジションクローズ
    # ctx.portfolio.positionsは，Keyにシンボル名，Valueに現在の自分のポジション情報を辞書型で持つ，辞書．（memo 参照）
    for (sym,val) in ctx.portfolio.positions.items():
      rtn = val["returns"]
      
      if rtn < ctx.loss_cut:
        sec = ctx.getSecurity(sym)
        # order_target_percent （memo 参照）
        sec.order_target_percent(0, comment="損切り {:.2%}".format(rtn))
        
      elif rtn > ctx.plofit:
        sec = ctx.getSecurity(sym)
        sec.order_target_percent(0, comment="益出し {:.2%}".format(rtn))   

    # 買いシグナル
    # currentは該当日の株価やシグナルを含むDataFrame （memo 参照）    
    # DataFrameだと言うことをハッキリさせたかったので df という変数に入れた．
    df = current.copy() 
    # シグナル["buy:sig"]がTrueだけ取得
    df = df[df["buy:sig"]]
    
    if not df.empty:
      for (sym, val) in df.iterrows(): 
        sec = ctx.getSecurity(sym)
        sec.order_target_percent(ctx.target, comment= "シグナル買 {:.2%}".format(val["Ratio"]))
    

```

## memo 

### cp 

data["close_price_adj"] （cp） は，全テスト期間の調整済み終値が入った pandas の DataFrame です．
ここから察するに，まず指定されたテスト期間のデータを取得し，シグナルを計算してしまった後に，```handle_signals``` で各日のシミュレーションを行っているようです．


date|jp.stock.1305|  jp.stock.7201| jp.stock.7203 | jp.stock.9201|jp.stock.9202|jp.stock.9983|jp.stock.9984
---|---:|---:|---:|---:|---:|---:|---:
2017-10-27|1847|1101.5|7050|3838|4310|37870|10295
2017-10-30|1849|1108|7077|3846|4333|37610|10430
2017-10-31|1842|1098.5|6990|3870|4346|37610|9947
2017-11-01|1865|1109|7038|4001|4458|37620|10130
2017-11-02|1872|1111.5|7150|4012|4428|37770|10210


### ctx.portfolio.positions

ctx.portfolio.positionsには，毎日のポジションが，下記のような辞書型で格納されています． `.items()`を使うことでforループ処理を行い，益出しと損切りを行っています．

```python
{'jp.stock.7201': 
　{'position_ratio': 0.50577838205302517, 'amount': 400, 'pnl': -11200.0, 
   'total_buy_price': 457600.0, 'value': 446400.0, 'portfolio_ratio': 0.044627950453377585, 
    'returns': -0.024475524475524479, 'buy_price': 457600.0, 'total_sell_price': 0.0, 
    'sell_price': 0.0, 'max_returns': 0.0}, 
 'jp.stock.9202': 
　{'position_ratio': 0.49422161794697483, 'amount': 100, 'pnl': 13900.0, 
   'total_buy_price': 422300.0, 'value': 436200.0, 'portfolio_ratio': 0.04360822577903966, 
   'returns': 0.032914989344068113, 'buy_price': 422300.0, 'total_sell_price': 0.0, 
   'sell_price': 0.0, 'max_returns': 0.032914989344068113}}
   ....
``` 

### current 

`handle_signals(ctx, date, current)` で必ず引数に渡す current には，毎日の株価やシグナルが DataFrame の形で入っています．Index にはシンボル名（銘柄名），Columns には,`ctx.configure` の `columns` に入れた値や，シグナル登録に使った関数で return した値，それと `split_ratio` が入っているように見えます．


17:05:43|split_ratio|close_price_adj|close_price|MA_long|MA_short|buy:sig 
---|---:|---:|---:|---:|---:|---:
jp.stock.1305|1|1869|1869|1833.84|1869.8|FALSE
jp.stock.7201|1|1126.5|1126.5|1126.22|1142.4|FALSE
jp.stock.7203|1|7201|7201|6936.16|7182.2|FALSE
jp.stock.9201|1|4134|4134|4271.12|4228.2|FALSE
jp.stock.9202|1|4193|4193|4208.48|4265|FALSE
jp.stock.9983|1|48850|48850|46458|48522|FALSE
jp.stock.9984|1|8461|8461|8039.12|8435.4|FALSE


コードではbuy:sig がTrueかどうかでフィルターをかけ，オーダーを行っています．

### order_target_percent

`order_target_percent(pct)` で，ポジションの枠の`pct`%分をオーダーする注文方法です． pctは `0<pct<=1` の float です．
ただ，この注文方法のドキュメントを探せなかったので，（[公式のサンプルコード](https://factory.quantx.io/demo/c2d058fd78434b31b54fb1219454b7be)から推察して使いました．．．）「ポジションの枠」というのが正確になにを表すのかは，分かりません．今度勉強会の時に確認したいと思います．

## 成績の見方

バックテストが終わると，このような形で全体と個別株の成績を確認できます．シグナルに登録しておいた MA_long や MA_short も確認できるので，コードが思った通りに書けているかどうかの確認もでき，便利です．

(全体）
![全体](https://qiita-image-store.s3.amazonaws.com/0/14019/b8d756e8-1917-4575-09d9-d4068f1d421c.jpeg)

(個別)
![個別](https://qiita-image-store.s3.amazonaws.com/0/14019/cbb63eb8-ecaa-f63b-e900-4165f934ed2e.jpeg)

## 書いてみた感想

+ なれたら，簡単にコードは書けると思います．が，
+ python と pandas の知識は必須です．
+ ドキュメントがあまり整備されていないのは，やはりツライです．．．
+ そもそも，ゴールデンクロスは，これでいいのか知らないので間違っていたら教えて下さい．（今さらｗ）

## QuantX とは

QuantX ならびに，運営会社の SmartTrade さんに関しては， [@hiroshimoda](https://qiita.com/hiroshimoda)さんの
 [Pythonで投資アルゴリズムを開発してみる](https://qiita.com/hiroshimoda/items/e89444729fe7d25c0275)や，[公式アルゴリズム開発入門](https://factory.quantx.io/handbook/ja/) を参照して下さい．

## 週一で勉強会

SmartTradeさんで週一で， [Pythonアルゴリズム勉強会](https://python-algo.connpass.com/)が開かれています．私もちょくちょくおじゃましてます(^^)．
純粋にPythonを学びたい方から，アルゴリズムを学びたい方，Fintechに興味ある方，などなど，色々な人たちが集まって，毎回楽しいので（お菓子も出るしｗ）興味がある方はぜひ参加してみてはいかがでしょうか．