Python3
fintech
Quantopian
SmartTrade
Quantx

Quantopianとは

Quantopianとは、QuantXと同様のクラウドベースのトレーディングプラットフォームであり、ユーザーはPythonとあらかじめ用意されているAPIを使用して、自分の取引アルゴリズムを作成、公開することができます。日本株中心のQuantXと異なり、QuantopianではUS株を使用することができ、また、APIの豊富さによるアルゴリズムの自由度や、ユーザーコミュニティの活発さ、アルゴリズムの評価方法などの観点から、非常に優れたサービスであると言えます。ただ、いまだに日本語のリファレンスが少なく、当然のように公式ドキュメントは英語であるため、日本ユーザーにとっては取っ付きづらい印象があるかもしれません。以下では、QuantXで用意されている移動平均アルゴリズムをQuantopianでどのように表現するかを解説していきます。

アルゴリズムを作る

Quantopianにアクセスし、Research > Algorithms > New Algorithm > Createから、アルゴリズム作成ページに飛びましょう。

今回作ったコード

今回作ったコードの全文です。

my_algorithm
import quantopian.algorithm as algo
from quantopian.pipeline import Pipeline
from quantopian.algorithm import attach_pipeline, pipeline_output
from quantopian.pipeline.data.builtin import USEquityPricing
from quantopian.pipeline.factors import SimpleMovingAverage, AverageDollarVolume


def initialize(context):

    # 10日間移動平均の情報を取得
    sma = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=10)


    # 30日出来高平均のデータ取得
    dollar_volume = AverageDollarVolume(window_length=30)


    # 出来高上位2%の銘柄のみを抽出する
    high_dollar_volume = dollar_volume.percentile_between(98, 100)

    # フィルターを作成
    pipe_screen = ((sma > 1.0) & high_dollar_volume)

    # データフレームに格納するカラムを指定する
    pipe_columns = {'dollar_volume':dollar_volume, 'sma':sma}

    # Pipeline関数を使って、pipelineを作成し、「my_pipeline」という名前で適用する
    pipe = Pipeline(columns=pipe_columns,screen=pipe_screen)
    attach_pipeline(pipe, 'my_pipeline')

    #損切り注文を実行する
    schedule_function(stop_loss, date_rules.every_day(), time_rules.market_open())

    #リバランスと指標の可視化を定期的に実行する
    schedule_function(rebalance, date_rules.every_day(), time_rules.market_open(hours=1))
    schedule_function(record_vars, date_rules.every_day(), time_rules.market_close())

def before_trading_start(context, data):
    # Pipeline_output関数を使って、データフレームを作成
    output = pipeline_output('my_pipeline')

    # Universeを更新
    context.my_securities = output.sort('sma', ascending=False).iloc[:50]
    print len(context.my_securities)

    #取り扱う銘柄リストを作成
    context.security_list = context.my_securities.index

    log.info("\n" + str(context.my_securities.head(5)))

def rebalance(context, data):
    for stock in context.security_list:
        #25日移動平均と75日移動平均を計算して比率を算出する
        m25 = data.history(stock, 'price', 25,'1d').mean()
        m75 = data.history(stock, 'price', 75,'1d').mean()
        ratio = m25 / m75

        #トレード可能かどうかを判断して、可能な場合のみ注文を実行する
        if data.can_trade(stock):
            if (ratio > 1.02) & (ratio < 1.05):
                order_target_percent(stock, 1.0)
            elif ratio < 0.95:
                order_target_percent(stock, 0.0)

    #損切り注文を実行する
    stop_loss(context, data)

def record_vars(context, data):
    record(capital_used=context.portfolio.capital_used,
           cash=context.portfolio.cash)

def stop_loss(context, data):  
    for stock in context.portfolio.positions:  
        position = context.portfolio.positions[stock].amount  
        price = data[stock].price

        #if position <= 0:  
        #    order_target_percent(stock, 0, style=StopOrder(price + (price*0.005)))

        if position >= 0:  
            order_target_percent(stock, 0, style=StopOrder(price - (price*0.005)))

なお、今回比較するQuantXのコードはこちら

解説

initialize関数

一番最初に読み込まれるため、後に必要なインスタンス等はここで作成する。

データを指定して、インスタンスを作成する。例えば、Amazonのデータを使用する時は以下のようになる。ちなみに、sid()内で企業名を入力すれば、自動でコード番号を教えてくれる。

context.amzn = sid(16841)

今回は、Quantopian独自の機能であるpipelineというスクリーニング機能を用いて、銘柄選定を行う。

# 10日間移動平均の情報を取得
sma = SimpleMovingAverage(inputs=[USEquityPricing.close], window_length=10)


# 30日出来高平均のデータ取得
dollar_volume = AverageDollarVolume(window_length=30)


# 出来高上位2%の銘柄のみを抽出する
high_dollar_volume = dollar_volume.percentile_between(98, 100)

# フィルターを作成
pipe_screen = ((sma > 1.0) & high_dollar_volume)

# データフレームに格納するカラムを指定する
pipe_columns = {'dollar_volume':dollar_volume, 'sma':sma}

# Pipeline関数を使って、pipelineを作成し、「my_pipeline」という名前で適用する
pipe = Pipeline(columns=pipe_columns,screen=pipe_screen)
attach_pipeline(pipe, 'my_pipeline')

毎日のトレードの前に、smaの上位50銘柄を抽出し、取引を行う。

def before_trading_start(context, data):
    # Pipeline_output関数を使って、データフレームを作成
    output = pipeline_output('my_pipeline')

    # Universeを更新
    context.my_securities = output.sort('sma', ascending=False).iloc[:50]
    print len(context.my_securities)

    #取り扱う銘柄リストを作成
    context.security_list = context.my_securities.index

    log.info("\n" + str(context.my_securities.head(5)))

損切り注文、リバランス注文を実行するスケジュールを設定する。
ここでは、損切り注文を毎日市場オープン時に条件を満たしていれば実行し、リバランスを毎日市場オープン時間の1時間後に条件を満たしていれば実行するように指定している。
date_rulesで実行する周期、time_rulesで実行する時刻を指定できる。

#損切り注文を実行するスケジュールを設定
schedule_function(handle_data, date_rules.every_day(), time_rules.market_open())

#リバランスと指標の可視化を実行するスケジュールを設定
schedule_function(rebalance, date_rules.every_day(), time_rules.market_open(hours=1))
schedule_function(record_vars, date_rules.every_day(), time_rules.market_close())

rebalance関数

売買シグナルをどこで出すか、また、どのような注文を実行するかを指定する。

data.historyで、銘柄、データ種類、期間、取り出すスパンを指定して、株価データを取り出す。ここでは、Amazon株の終値を過去25日間分1日ごとに抽出し、それを平均して25日移動平均を算出している。

#25日移動平均と75日移動平均を計算して比率を算出する
for stock in context.security_list:
  m25 = data.history(stock, 'price', 25,'1d').mean()
  m75 = data.history(stock, 'price', 75,'1d').mean()
  ratio = m25 / m75

data.can_trade()は、引数で指定した銘柄がトレード可能である場合Trueを返す。QuantXでは、銘柄が非上場であった場合、Errorを返すのでこの機能は非常にありがたい。
order_target_percentでratioが1.2を上回る時に自己資金の100%になるまでロングし、0.95を下回る時に自己資金の0%になるまでショートを実行する。

#トレード可能かどうかを判断して、可能な場合のみ注文を実行する
if data.can_trade(stock):
  if (ratio > 1.02) & (ratio < 1.05):
    order_target_percent(stock, 1.0)
  elif ratio < 0.95:
    order_target_percent(stock, 0.0)

ポジション変更後に再度損切り注文を実行する。

#損切り注文を実行する
handle_data(context, data)

record_vars関数

5つまで表示したい指標を指定できる。

record関数内で、表示したい指標を指定することで、バックテスト時に指標の変化を確認できる。

record(capital_used=context.portfolio.capital_used,
       cash=context.portfolio.cash)

損切り

portfolio_positionsで自分がその時点で持っているポートフォリオの情報を取得することができます。
ここでは、個別銘柄ごとの保有量と株価を取得し、株価が0.5%下落した時に全て売ります。

def stop_loss(context, data):  
    for stock in context.portfolio.positions:  
        position = context.portfolio.positions[stock].amount  
        price = data[stock].price

        #if position <= 0:  
        #    order_target_percent(stock, 0, style=StopOrder(price + (price*0.005)))

        if position >= 0:  
            order_target_percent(stock, 0, style=StopOrder(price - (price*0.005)))

バックテスト

様々な指標でアルゴリズムの評価結果を確認することができます。
特に、Quantopianのコンテストに参加するにはかなりの評価基準を満たす必要があり(今回の結果ではま全く満たせていませんね)、見かけのreturnが大きいだけではダメであることがよく分かります。
スクリーンショット 2019-01-18 16.00.37.png

Quantopianが独自に用意しているこのリスク評価モデルに関しても、Quantopian側でホワイトペーパーが用意されているので、後ほど記事を上げていこうかと思います。

まとめ

Quantopianは機能があまりに充実しているのですが、使いこなすにはかなりの時間と鍛錬が必要っぽい。