Edited at

上放れタスキのアルゴリズム #QuantX

More than 1 year has passed since last update.


上放れタスキ

image.png

[出典]上放れタスキ | 銘柄・株スクリーニング | 投資顧問比較ナビ

今回は,上放れタスキというシグナルをQuantXで書いてみたいと思います.

上放れタスキとは、上昇トレンドにおいて,窓を開けて上昇した陽線が出現し、翌日に窓を開けずに安寄りした陰線が現れた状態のチャートだそうです.

チャート画像を見ると,あ〜はん:smirk:とわかった気になるのですが,DataFrameではどう表現すればいいのでしょうか?


条件

自分なりに考えてみました.


トレンド

上昇トレンドであること.

具体的には,日足の変化率を過去5日間観察し,毎日ポジティブであること.


直近3日

(以下,t-2,t-1,t0 日と表現する)


  1. t-2の高値よりt-1の安値が高い

  2. t-1の陽線が,過去100日の陽線の長さの2倍以上(必要?)

  3. t0の始値がt-1の始値を上回っいて,かつt0は陰線

  4. t0の安値がt-2の高値を上回っている


シグナル

上記の条件が発生したt2の終値でシグナルを出すことにします.ただし,過去5日間(=ホールド期間)に既にシグナルが発生していた場合は,不採用とします.


手仕舞い

シグナルが出てから5日間ホールドして、クローズすることにします。


コード

import talib as ta 

import numpy as np

def initialize(ctx):
# 設定
ctx.up_trend_term = 5
ctx.yousen_term = 100
ctx.yousen_threshold = 1.0
ctx.holding_days = 5
ctx.target = 0.50

# 銘柄設定 memo参照
ctx.codes = [6758, 8316, 2914, 7267, 7182, 8058, 8411, 4063, 7751, 5411,
6981, 6902, 3382, 8766, 6367, 6752, 4911, 8001, 6301, 6503,
8031, 5108, 4519, 4578, 1925, 2502, 8801, 2503, 8750, 6273,
7741, 7270, 6971, 4901, 8053, 6869, 6502, 8113, 8725, 8267,
9843, 1605, 6723, 4612, 6702, 9021, 8308, 7309, 4188, 6762,
4528, 9202, 2413, 7181, 3402, 6586, 3092, 1878, 6988, 1801,
9502]

ctx.symbol_list = ["jp.stock.{}".format(code) for code in ctx.codes[:50]]

ctx.configure(
channels={ # 利用チャンネル
"jp.stock": {
"symbols": ctx.symbol_list,
"columns": ["open_price_adj", # 始値(株式分割調整後)
"high_price_adj", # 高値(株式分割調整後)
"low_price_adj", # 安値(株式分割調整後)
"close_price", # 終値
"close_price_adj", # 終値(株式分割調整後)
]}})

def _TASUKI_GAP(data):

df_open = data["open_price_adj"].fillna(method='ffill')
df_high = data["high_price_adj"].fillna(method='ffill')
df_low = data["low_price_adj"].fillna(method='ffill')
df_close = data["close_price_adj"].fillna(method='ffill')

# 日足の変化率を過去5日間観察し,毎日ポジティブであることを確認
df_is_uptrend = df_close.pct_change().rolling(window=ctx.up_trend_term,).min() > 0
# t-2の高値よりt-1の安値が高い
df_open_window = df_high.shift(2) < df_low.shift(1)

# t-1の陽線が,過去100日の陽線の長さの2倍以上(必要?)
# memo 参照
df_intraday = df_close / df_open - 1
df_MA_yousen = df_intraday.where(df_intraday>0).rolling(window=ctx.yousen_term, min_periods=1).apply(np.nanmean)
df_is_yousen_long = df_intraday.shift(1) > df_intraday.shift(2) * ctx.yousen_threshold

# t0の始値がt-1の始値を上回っいて,かつt0は陰線
df_tasuki = (df_open > df_open.shift(1)) & (df_open > df_close)
# t0の安値がt-2の高値を上回っている
df_is_trend_continue = df_low > df_high.shift(2)

df_uwabure_tasuki = df_is_uptrend & df_open_window & df_is_yousen_long & df_tasuki & df_is_trend_continue

buy_signal = (df_uwabure_tasuki) & (df_uwabure_tasuki.rolling(window=ctx.holding_days, center=False).sum() == 1)
close_signal = buy_signal.shift(ctx.holding_days)

return {
"buy:sig": buy_signal,
"close:sig": close_signal,
}

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

def handle_signals(ctx, date, current):

df = current.copy()

# 買いシグナル
df_long = df[df["buy:sig"]]

if not df_long.empty:
for (sym, val) in df_long.iterrows():
#ctx.logger.info(val)
sec = ctx.getSecurity(sym)
msg = "買いシグナル"
sec.order_target_percent(ctx.target, comment= msg)

# # ポジションクローズ
df_position_close = df[df["close:sig"]]
if not df_position_close.empty:
for (sym, val) in df_position_close.iterrows():
sec = ctx.getSecurity(sym)
sec.order_target_percent(0, comment= "position close")


memo


銘柄設定

ctx.codes = [6758, 8316, 2914, 7267, 7182, ...]

ctx.syms = ["jp.stock.{}".format(code) for code in ctx.codes[:50]]

format関数を使ってシンボルリストを作りました.

また,QuantXでは最大50銘柄をシミュレーションに組み込む事が出来るので,50を超えないようにctx.codes[:50]としています.


t-1の陽線が,過去100日の陽線の長さの2倍以上

本当にこれが必要かどうかわかりませんが,びよーんと伸びた陽線は気分が高まるので入れてみました.

df_intraday = df_close / df_open - 1 

df_MA_yousen = df_intraday.where(df_intraday>0).rolling(window=ctx.yousen_term, min_periods=1).apply(np.nanmean)
df_is_yousen_long = df_intraday.shift(1) > df_intraday.shift(2) * ctx.yousen_threshold

df_intraday は始値から終値の変化率を出しています.

df_intraday.where(df_intraday>0)で,df_intradayの正の数だけを残し,負の数はNaNが入るDataFrameを作っています.

そのDataFrameに対して100日移動平均をとっているのですが,np.nanmean を渡してNaNを無視した平均値をとり,かつ min_periods=1を渡して,100日の間に一つでも数値が入っていれば平均値を返すように指示しています.つまり100日の間に99日はNaN,1日だけ0.05があれば,そこで返ってくる数値は0.05になります.


結果

2018-07-29-15:00:48.jpg


感想


  • 銘柄は東証一部上場銘柄を適当に入れました.

  • めったに出現しないと書いてありましたが,確かに出現しませんね.


  • shift を使うとだいたいの事は出来る気がしてきました.

  • 間違ってるかもしれないので,その時はぜひおしえてください:bow_tone1:


免責注意事項


  • このコードに基づき投資した結果、損害が発生しても,一切責任を持ちません.

  • このコードが正しく機能する保証は一切致しません.

  • このアルゴリズムを勧めているわけではありません.あくまで python のサンプルコードとして掲載しているだけです.