#自己紹介
3ヶ月ほど前から趣味でPythonを触っている、金融の知識ゼロの超初心者です。
そんな超初心者の僕がSmartTrade社でインターンをさせていただくことになったので、プログラミングも投資も初心者でも理解出来るように、学んだ内容を随時更新していきたいと思います。
#QuantXとは
QuantXとはSmart Trade社が提供しているプラットホームです。QuantXを用いることで、無料で株価データを分析し、アルゴリズムを開発することができます。無料で登録することができるのでよければ登録してみてください。登録はQuantXに入門してみるを参考にするといいかもしれません。
#標準テンプレート
取り敢えず登録して標準テンプレートを動かして見ましょう。
実行してみると、上のような画面になると思います。
# Sample Algorithm
def initialize(ctx):
# 設定
ctx.logger.debug("Hello World")
ctx.configure(
channels={ # 利用チャンネル
"jp.stock": {
"symbols": [
"jp.stock.1305",
"jp.stock.9984",
"jp.stock.9983",
"jp.stock.7201",
"jp.stock.9201",
"jp.stock.9202",
"jp.stock.7203"
],
"columns": [
#"open_price_adj", # 始値(株式分割調整後)
#"high_price_adj", # 高値(株式分割調整後)
#"low_price_adj", # 安値(株式分割調整後)
#"volume_adj", # 出来高
#"txn_volume", # 売買代金
"close_price", # 終値
"close_price_adj", # 終値(株式分割調整後)
]
}
}
)
def _my_signal(data):
return {
}
# シグナル登録
ctx.regist_signal("my_signal", _my_signal)
def handle_signals(ctx, date, current):
'''
current: pd.DataFrame
'''
done_syms = set([])
for (sym,val) in ctx.portfolio.positions.items():
returns = val["returns"]
if returns < -0.03:4
sec = ctx.getSecurity(sym)
#sec.order(-val["amount"], comment="損切り(%f)" % returns)
done_syms.add(sym)
elif returns > 0.05:
sec = ctx.getSecurity(sym)
#sec.order(-val["amount"], comment="利益確定売(%f)" % returns)
done_syms.add(sym)
#buy = current["buy:sig"].dropna()
#for (sym,val) in buy.items():
# if sym in done_syms:
# continue
#
# sec = ctx.getSecurity(sym)
# #sec.order(sec.unit() * 1, comment="SIGNAL BUY")
# #ctx.logger.debug("BUY: %s, %f" % (sec.code(), val))
# pass
#sell = current["sell:sig"].dropna()
#for (sym,val) in sell.items():
# if sym in done_syms:
# continue
#
# sec = ctx.getSecurity(sym)
# #sec.order(sec.unit() * -1, comment="SIGNAL SELL")
# #ctx.logger.debug("SELL: %s, %f" % (sec.code(), val))
# pass
次にコードを読んで何をしているのかを理解して行きましょう。
(SmartTradeさんがサンプルコードの説明をしてくれているので、それが理解できればオッケーです。この記事は公式のサンプルコードの説明がすぐには理解できない僕のような超初心者を想定して書いています)
#初期化部分
銘柄や使うデータ要素を指定して行きます。
最初に一度だけ呼び出されます。一度だけ呼び出せばいい銘柄指定、売買シグナル生成などを行いましょう。
def initialize(ctx):
# 設定
ctx.logger.debug("initialize() called")
ctx.configure(
channels={ # 利用チャンネル
"jp.stock": {
"symbols": [
"jp.stock.1305",
"jp.stock.9984",
"jp.stock.9983",
"jp.stock.7201",
"jp.stock.9201",
"jp.stock.9202",
"jp.stock.7203"
],
"columns": [
#"open_price_adj", # 始値(株式分割調整後)
#"high_price_adj", # 高値(株式分割調整後)
#"low_price_adj", # 安値(株式分割調整後)
#"volume_adj", # 出来高
#"txn_volume", # 売買代金
"close_price", # 終値
"close_price_adj", # 終値(株式分割調整後)
]
}
}
)
###def initialize(ctx):
この関数の中で、銘柄や使うデータ要素を指定します。
ctxは「アルゴリズムの状態を保持するオブジェクト」と公式の説明では書いてありますが、銘柄やデータ要素など、アルゴリズム内の複数の箇所で使用したいものの情報を保持するオブジェクトだと思ってください。
###ctx.logger.debug("initialize() called")
コンソールにログメッセージが出力されます。ログとは、プログラムの実行中に起こった出来事を記録することです。ログメッセージとはQuantXのページの画面下の「ログ」と書いてあるところに表示されます。
ctx.configure()
この段落で、アルゴリズムで利用するデータを指定していきます。
###channels={"jp.stock": {
アルゴリズムで利用する日本株データを宣言しています。
###"symbols": ["jp.stock.1305",
対象とする証券コードを指定します。たとえば、jp.stock.1305はダイワ上場投信を意味します。
###"columns": ["close_price",
各銘柄において取得するデータの種類を指定します。指定可能な値はデーセットを参照してください。サンプルコードでは、終値close _priceと終値(株式分割調整後)close_price_adjを指定しています。
##売買シグナル生成部分
いよいよ売買シグナルを指定して行きます。売買シグナルとは、売り買いのタイミングを指示する情報のことです。具体的には、あらかじめ株価やチャートがこの状態のときに買う(売る)と決めておき、決めていた状態になったことを教えてくれるサインのことです。
def _my_signal(data):
return {
}
# シグナル登録
ctx.regist_signal("my_signal", _my_signal)
###def _my_signal(data):
引数のdataには
2015/1/1(指定した日付)の
close_price | close_price_adj | |
---|---|---|
jp.stock.1305 | 値 | 値 |
jp.stock.9984 | 値 | 値 |
jp.stock.9983 | 値 | 値 |
jp.stock.7201 | 値 | 値 |
jp.stock.9201 | 値 | 値 |
jp.stock.9202 | 値 | 値 |
jp.stock.7203 | 値 | 値 |
2015/1/2の
close_price | close_price_adj | |
---|---|---|
jp.stock.1305 | 値 | 値 |
jp.stock.9984 | 値 | 値 |
jp.stock.9983 | 値 | 値 |
jp.stock.7201 | 値 | 値 |
jp.stock.9201 | 値 | 値 |
jp.stock.9202 | 値 | 値 |
jp.stock.7203 | 値 | 値 |
という様に、2015/1/1〜2017/3/31 (指定した範囲の日付全て)のデータが3次元的な形式で格納されています。
###return {}
標準テンプレートでは売買シグナルを指定しないようです。
###ctx.regist_signal("my_signal", _my_signal)
シグナルを登録します
第1引数にシグナルを登録したい名前(なんでもいい)、第2引数に先ほどの _my_signalを入れます。
#日ごとの処理部分の記述
ここからは日ごとに呼び出される関数の説明です。これは例えば100日分のデータのバックテストをやる場合、100回呼び出される事になります。
def handle_signals(ctx, date, current):
'''
current: pd.DataFrame
'''
done_syms = set([])
for (sym,val) in ctx.portfolio.positions.items():
returns = val["returns"]
if returns < -0.03:
sec = ctx.getSecurity(sym)
#sec.order(-val["amount"], comment="損切り(%f)" % returns)
done_syms.add(sym)
elif returns > 0.05:
sec = ctx.getSecurity(sym)
#sec.order(-val["amount"], comment="利益確定売(%f)" % returns)
done_syms.add(sym)
###def handle_signals(ctx, date, current):
dateにはdatetime.datetime型で対応する日付が入っています。
currentは、dateの当日のデータとシグナルを含んだ pandas.DataFrame オブジェクトですが、標準テンプレートでは売買シグナルを指定していないのでdateは以下のようなpandas.DataFrame オブジェクトになっています。
close_price | close_price_adj | |
---|---|---|
jp.stock.1305 | 値 | 値 |
jp.stock.9984 | 値 | 値 |
jp.stock.9983 | 値 | 値 |
jp.stock.7201 | 値 | 値 |
jp.stock.9201 | 値 | 値 |
jp.stock.9202 | 値 | 値 |
jp.stock.7203 | 値 | 値 |
###done_syms = set([])
後に必要になる、空のsetオブジェクトを用意しておきます。
###for (sym,val) in ctx.portfolio.positions.items():
ctx.portfolio.positionsには
毎日のポジションが,下記のような辞書型で格納されています。
{'jp.stock.1305':
{'position_ratio': 値, 'amount': 値, 'pnl': -値,
'total_buy_price': 値, 'value': 値, 'portfolio_ratio': 値,
'returns': 値, 'buy_price': 値, 'total_sell_price': 値,
'sell_price': 値, 'max_returns': 値},
'jp.stock.9984':
{'position_ratio': 値, 'amount': 値, 'pnl': 値,
'total_buy_price': 値, 'value': 値, 'portfolio_ratio': 値,
'returns': 値, 'buy_price': 値, 'total_sell_price': 値,
'sell_price': 値, 'max_returns': 値}}
....
for 文のsymは
'ctx.configureで指定した証券コード'('jp.stock.1305'など)
for文のvalは
{'position_ratio': 値, 'amount': 値, 'pnl': -値,
'total_buy_price': 値, 'value': 値, 'portfolio_ratio': 値,
'returns': 値, 'buy_price': 値, 'total_sell_price': 値,
'sell_price': 値, 'max_returns': 値}
がctx.configureで指定した証券コードの数だけ入っています。
###returns = val["returns"]
for文のvalにはctx.configureで指定した証券コードの'returns'、つまり損益率の値が入っています。
###if returns < -0.03:
return=証券コードの損益率なので、ここでは損益率が-0.03未満になった場合に行う操作が入っています。つまり3%以上下落した場合に実行されます。
###sec = ctx.getSecurity(sym)
Securityとは銘柄を表すオブジェクトであり、ctx.getSecurity() で取得できます。このオブジェクトを使用して注文を行ないます。symには前述した通りctx.configureで指定した証券コードが入っています。
###done_syms.add(sym)
for文の前に作っておいたdone_syms = set([])にsym=損益率が-0.03未満の証券コードを追加します。
###elif returns > 0.05:
同様に損益率が5%以上になった場合に行う操作を指定します。
###※注意
標準テンプレートではシグナルによる売買は行わず、損切り・利益確定売りしか指定しないためこれでコードが終わっていますが、本来ならこの後に売買シグナルによる売買の式が続きます。また、注文を行うためのコードを指定していないため全く売買が行われていません。
#感想
全くの初心者ですが何と無く簡単なアルゴリズムなら書けるんじゃないかという気になって来ました。
僕みたいに大学ではミクロとかマクロとかしかやっていない文系でも、この記事を読んで少しでもアルゴリズム開発に興味を持っていただければ幸いです。
#宣伝
毎週、SmartTrade社のオフィス(神田千代田共同ビル4階)で勉強会(水曜日18時〜もくもく会、金曜日19時〜勉強会)を行なっています。気軽に参加してみてください!
Pythonアルゴリズム勉強会HP:https://python-algo.connpass.com/
(connpassって言うイベントサイトに飛びます)