Quantopianのチュートリアルpart1をやってみた

#Quantopianについて
Quantopianは、Pythonを使って自動取引アルゴリズムをウェブ上で開発できます。
Quantopianでは、バックテストやポートフォリオ分析の仕組みをオープンソース化しており、バックテスト等の膨大なデータを扱う処理もクラウド上で完結している、完全無料のサービスです。
また、Quantpianには多数のAPIが存在しており、リサーチ環境でアルゴリズムに活用するデータを参照することが可能となっております。

#Quantopianの始め方
アカウント登録が完了したら、以下チュートリアルのぺーじに沿って進めると良いでしょう。
https://www.quantopian.com/get-funded

#Researchを活用する
###データの探索1
QuantopianのResearch機能は、アメリカの8,000以上ある株式の2002年以降の、価格やボリューム、リターンなどを自由に取り扱うことができます。
早速ですが、Appleの株価データを調査してみましょう。

```python
# Research environment functions
from quantopian.research import prices, symbols

# Pandas library: https://pandas.pydata.org/
import pandas as pd

# Query historical pricing data for AAPL
aapl_close = prices(
    assets=symbols('AAPL'),
    start='2013-01-01',
    end='2016-01-01',
)

# Compute 20 and 50 day moving averages on
# AAPL's pricing data
aapl_sma20 = aapl_close.rolling(20).mean()
aapl_sma50 = aapl_close.rolling(50).mean()

# Combine results into a pandas DataFrame and plot
pd.DataFrame({   
    'AAPL': aapl_close,
    'SMA20': aapl_sma20,
    'SMA50': aapl_sma50
}).plot(
    title='AAPL Close Price / SMA Crossover'
);
```
こちらのコードを実行すると、以下のようなAppleの株価/移動平均線20/移動平均線50のグラフが描けると思います。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/269343/a2526830-f032-63c0-e4a3-9800aa06c8af.png)

###Pipeline API
PipelineのAPIは横断的な分析に対して非常に有効です。複数のインプットされたデータを集計したり、時系列データの分析を行ったりすることが可能です。
APIを活用し、以下3つのようなことが可能です。

・フィルタリングルールに基づく株式の選定
・スコアリング関数に基づく株式のランク付け
・最適なポートフォリオの計算

それでは、さっそくパイプラインをインポートしてみましょう。

```python:quantopian.py
# Pipeline class
from quantopian.pipeline import Pipeline

def make_pipeline():
    # Create and return an empty Pipeline
    return Pipeline()
```
アウトプットをパイプラインに追加するため、データセットを参照し、実行したいデータを特定する必要があります。例えば、`close` のcolumnを利用する場合、 `USEquityPricing` のデータセットを参照します。そして、以下のように最新の株価をこのカラムから定義することが可能となっています。

```python

# Import Pipeline class and USEquityPricing dataset
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data import USEquityPricing

def make_pipeline():
    # Get latest closing price
    close_price = USEquityPricing.close.latest

    # Return Pipeline containing latest closing price
    return Pipeline(
        columns={
            'close_price': close_price,
        }
    )
```
Pipeline APIには、いくつかの組み込み計算も用意されています。例えば次のコードは `Psychsignal` の `stocktwits` データセットをインポートし、出力をその `bull_minus_bear` 列の3日間の移動平均として定義します。

```python
# Import Pipeline class and datasets
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data import USEquityPricing
from quantopian.pipeline.data.psychsignal import stocktwits

# Import built-in moving average calculation
from quantopian.pipeline.factors import SimpleMovingAverage


def make_pipeline():
    # Get latest closing price
    close_price = USEquityPricing.close.latest

    # Calculate 3 day average of bull_minus_bear scores
    sentiment_score = SimpleMovingAverage(
        inputs=[stocktwits.bull_minus_bear],
        window_length=3,
    )

    # Return Pipeline containing close_price
    # and sentiment_score
    return Pipeline(
        columns={
            'close_price': close_price,
            'sentiment_score': sentiment_score,
        }
    )
```

戦略を構築する上で大切なことは、ポートフォリオに組み込む株式を設定することです。一方でQuantopianではできる限り流動性の高い株式が適切だと考えています。

Quantopianの `QTradableStocksUS` 　はこの特徴付を行っています。

```python
# Import Pipeline class and datasets
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data import USEquityPricing
from quantopian.pipeline.data.psychsignal import stocktwits

# Import built-in moving average calculation
from quantopian.pipeline.factors import SimpleMovingAverage

# Import built-in trading universe
from quantopian.pipeline.experimental import QTradableStocksUS


def make_pipeline():
    # Create a reference to our trading universe
    base_universe = QTradableStocksUS()

    # Get latest closing price
    close_price = USEquityPricing.close.latest

    # Calculate 3 day average of bull_minus_bear scores
    sentiment_score = SimpleMovingAverage(
        inputs=[stocktwits.bull_minus_bear],
        window_length=3,
    )

    # Return Pipeline containing close_price and
    # sentiment_score that has our trading universe as screen
    return Pipeline(
        columns={
            'close_price': close_price,
            'sentiment_score': sentiment_score,
        },
        screen=base_universe,
    )
```
これでPipelineの定義は完全に完了し、`run_pipeline` を利用すれば特定の期間のデータを実行することができます。
アウトプットは日付と株ごとのpandasのDataFrameとなります。


```python

# Import run_pipeline method
from quantopian.research import run_pipeline

# Execute pipeline created by make_pipeline
# between start_date and end_date
pipeline_output = run_pipeline(
    make_pipeline(),
    start_date='2013-01-01',
    end_date='2013-12-31'
)

# Display last 10 rows
pipeline_output.tail(10)
```

###戦略の定義
ここまでのチュートリアルでどのようにデータにアクセスするかは理解頂けたと思います。
ここからは、ロング・ショート戦略を構築しましょう。
一般に、ロング・ショート戦略は、試算の相対的価値をモデル化を行い、値上がりが期待できる割安な銘柄（過小評価された銘柄）を買い、同時に値下がりが予想される割高な銘柄（過大評価された銘柄）を空売りします。
*戦略* ：過去3日間のセンチメントスコアが高いものは高い株価とみなし、低いものは低い株価とみなします。

###戦略分析
先程行ったものと同様に、 `SimpleMovingAverage` と `stocktwits`　と `bull_minus_bear`のデータを定義できます。

```python
# Pipeline imports
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.psychsignal import stocktwits
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline.experimental import QTradableStocksUS


# Pipeline definition
def  make_pipeline():

    base_universe = QTradableStocksUS()

    sentiment_score = SimpleMovingAverage(
        inputs=[stocktwits.bull_minus_bear],
        window_length=3,
    )

    return Pipeline(
        columns={
            'sentiment_score': sentiment_score,
        },
        screen=base_universe
    ) 
```

シンプルに、 `sentiment_score` トップ350と下位350のみを分析します。
`top` と `bottom` のメソッドくを活用することにより、パイプラインのフィルターを作成することが可能です。そして、フィルターと取引量の方針を通して他の余分な株価を取り除くことが可能です。

```python
# Pipeline imports
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.psychsignal import stocktwits
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline.experimental import QTradableStocksUS

# Pipeline definition
def  make_pipeline():

    base_universe = QTradableStocksUS()

    sentiment_score = SimpleMovingAverage(
        inputs=[stocktwits.bull_minus_bear],
        window_length=3,
    )

    # Create filter for top 350 and bottom 350
    # assets based on their sentiment scores
    top_bottom_scores = (
        sentiment_score.top(350) | sentiment_score.bottom(350)
    )

    return Pipeline(
        columns={
            'sentiment_score': sentiment_score,
        },
        # Set screen as the intersection between our filter
        # and trading universe
        screen=(
            base_universe
            & top_bottom_scores
        )
    )
```

次に、私達が分析を行いたい期間のデータを実行します。
ここでは2018/1/1~2018/12/31までのデータとしております。

```python

# Import run_pipeline method
from quantopian.research import run_pipeline

# Specify a time range to evaluate
period_start = '2018-01-01'
period_end = '2018-12-31'

# Execute pipeline over evaluation period
pipeline_output = run_pipeline(
    make_pipeline(),
    start_date=period_start,
    end_date=period_end
)```


Pipelineのデータに加え、株価データが必要となりますので。これは容易に出力することが可能で、 `prices`を参照します。

```python
# Import prices function
from quantopian.research import prices

# Get list of unique assets from the pipeline output
asset_list = pipeline_output.index.levels[1].unique()

# Query pricing data for all assets present during
# evaluation period
asset_prices = prices(
    asset_list,
    start=period_start,
    end=period_end
)
```


戦略をテストするために、Quantopianのオープンソースである *Alphalens* を活用することができます。最初に、`get_clean_factor_and_forward_returns` を利用し、 `factor` `pricing` のデータを結合しましょう。

```python
# Import Alphalens
import alphalens as al

# Get asset forward returns and quantile classification
# based on sentiment scores
factor_data = al.utils.get_clean_factor_and_forward_returns(
    factor=pipeline_output['sentiment_score'],
    prices=asset_prices,
    quantiles=2,
    periods=(1,5,10),
)

# Display first 5 rows
factor_data.head(5)
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/269343/97033817-d5a8-fe5a-e20c-9c4a8590a482.png)

このデータフォーマットを活用することにより、いくつかのAlphalensによる分析が行えます。
では、期間全体の平均リターンをみることから始めましょう。
あくまでも、目標はロング・ショート戦略を構築することです。

```python
# Calculate mean return by factor quantile
mean_return_by_q, std_err_by_q = al.performance.mean_return_by_quantile(factor_data)

# Plot mean returns by quantile and holding period
# over evaluation time range
al.plotting.plot_quantile_returns_bar(
    mean_return_by_q.apply(
        al.utils.rate_of_return,
        axis=0,
        args=('1D',)
    )
);```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/269343/2fdc63e8-57a8-0b77-0e5b-0388d25c2c7e.png)

また、次のコードを利用して、保有期間5日間ののロング・ショートポートフォリオの累積リターンをプロットすることができます。

```python
# Calculate factor-weighted long-short portfolio returns
ls_factor_returns = al.performance.factor_returns(factor_data)

# Plot cumulative returns for 5 day holding period
al.plotting.plot_cumulative_returns(ls_factor_returns['5D'], '5D');
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/269343/25bb05ae-b006-7c0f-4cd3-b3ec0bb9b4c9.png)

上のプロットは大きなドローダウン期間を示しており、この分析ではまだ取引コストや市場への影響を考慮していません。 ここから、Alphalensを活用し、より深い分析を行い、戦略のアイデアを繰り返すべきです。

###Algorithm API & Core Functions
ここからは、QuantopianのアルゴリズムAPIを活用し、基本的なトレード戦略を構築していきます。
アルゴリズムAPIは注文の決定とスケジューリング、そしてアルゴリズムのパラメーターの初期化と管理を行います。
それでは、いくつか核となる関数をみていきましょう。

####initialize(context)
`initialize` はアルゴリズムをスタートする1回と、 `context` を必要とする時のみ利用します。
`context` は、シミュレーションプロセス全体を通して状態を維持するために使用されるPython辞書であり、アルゴリズムのさまざまな部分で参照できます。
関数を呼び出す際に、永続化したい変数は、グローバル変数を使用するのではなく、 `context` に格納する必要があります。ドット記法（context.some_attribute）を使用して、コンテキスト変数にアクセスして初期化することができます。

####before_trading_start(context, data)
`before_trading_start` は、マーケットがオープンする前に1日1回呼び出され、入力としてcontextとデータを必要とします。  `context` は `initialize` からの同じ辞書への参照です。 `data` は、任意のアセットの現在または過去の価格設定と、ボリュームを調べることを可能にするいくつかのAPI関数を格納するオブジェクトです。
`before_trading_start` は、パイプラインの出力を取得する場所でもあります。また、ポートフォリオ構築に使用する前に、結果データの前処理を行います。 これについては次のチュートリアルで説明します。

####schedule_function(func, day_rule, time_rule)
Quantopianでは、アルゴリズムはNYSEの取引カレンダーに従って、通常の取引日の午前9時30分から午後4時の間に株式を取引できます。 
`schedule_function` を使用すると、指定した日時にカスタム関数を実行できます。 
たとえば、次のように、各週の初日にマーケットオープンでポートフォリオをリバランスする機能をスケジュールできます。

```python
schedule_function(
    rebalance,
    date_rule=date_rules.week_start(),
    time_rule=time_rules.market_open()
)  
```

スケジューリング関数は初期化時に実行する必要があり、このメソッドでスケジューリングされたカスタム関数は引数として、`context` と `data` を受け取る必要があります。 利用可能な `date_rules` と `time_rules` の完全なリストは、 [Quantopianのドキュメント](https://www.quantopian.com/help#ide-schedulefunction) をチェックしてください。
次に、取引アルゴリズムの骨格を作りましょう。 現状、アルゴリズムはシミュレーションで経過した日数を追跡し、日付と時刻に応じて異なるメッセージを記録します。

```
# Import Algorithm API
import quantopian.algorithm as algo


def initialize(context):
    # Initialize algorithm parameters
    context.day_count = 0
    context.daily_message = "Day {}."
    context.weekly_message = "Time to place some trades!"

    # Schedule rebalance function
    algo.schedule_function(
        rebalance,
        date_rule=algo.date_rules.week_start(),
        time_rule=algo.time_rules.market_open()
    )


def before_trading_start(context, data):
    # Execute any daily actions that need to happen
    # before the start of a trading session
    context.day_count += 1
    log.info(context.daily_message, context.day_count)


def rebalance(context, data):
    # Execute rebalance logic
    log.info(context.weekly_message)
```


ここまでで、取引あるごリズムのための基本的な構造ができました。


###アルゴリズムにおけるデータ処理
次に、`Research` で構築したデータパイプラインにアルゴリズムに統合します。
`Research` との重要な違いの1つは、バックテスト中にシミュレーションが進行するにつれてパイプラインが毎日実行されるため、 `start_date` と `end_date` 引数を含める必要がないことです。 
アルゴリズムでデータパイプラインを使用するための最初のステップは、アルゴリズムの初期化関数にそれへの参照を追加することです。 これは、2つの入力を必要とする `attach_pipeline` メソッドを使用して行われます。
これは、 `make_pipeline` を使用して構築する `Pipeline` オブジェクトへの参照と、それを識別するためのString名です。

```python
# Import Algorithm API
import quantopian.algorithm as algo


def initialize(context):
    # Attach pipeline to algorithm
    algo.attach_pipeline(
        make_pipeline(),
        'data_pipe'
    )

    # Schedule rebalance function
    algo.schedule_function(
        rebalance,
        date_rule=algo.date_rules.week_start(),
        time_rule=algo.time_rules.market_open()
    )


def before_trading_start(context, data):
    pass


def rebalance(context, data):
    pass
```

上記のように、Quantopianのパイプラインは、毎日市場が開かれる前にデータストリームを処理して出力を生成します。
初期化で指定したパイプライン名を取り、パイプラインによって生成されたPandasのDataFrameを返す、 `pipeline_output`関数を使用して、 `before_trading_start` でパイプラインの出力を取得できます。 
今のところ、リバランス機能を使用して、パイプラインの出力から上位10行をログに記録できます。

```python
# Import Algorithm API
import quantopian.algorithm as algo


def initialize(context):
    # Attach pipeline to algorithm
    algo.attach_pipeline(
        make_pipeline(),
        'data_pipe'
    )

    # Schedule rebalance function
    algo.schedule_function(
        rebalance,
        date_rule=algo.date_rules.week_start(),
        time_rule=algo.time_rules.market_open()
    )


def before_trading_start(context, data):
    # Get pipeline output and
    # store it in context
    context.pipeline_data = algo.pipeline_output(
        'data_pipe'
    )


def rebalance(context, data):
    # Display first 10 rows
    # of pipeline output
    log.info(context.pipeline_data.head(10))
```

次に、 `Research` で構築した `make_pipeline` 関数をアルゴリズムに追加しましょう。 
分析のために行ったように資産の数を制限するのではなく、アルゴリズムはそれがセンチメントスコアを持っている取引すべての資産を考慮するべきです。
これには、 `sentiment_score` 出力の `notnull` メソッドを使用してフィルタを作成し、 `＆演算子` を使用して取引可能なユニバースとの共通部分を取得します。

```python
# Import Algorithm API
import quantopian.algorithm as algo

# Pipeline imports
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.psychsignal import stocktwits
from quantopian.pipeline.factors import SimpleMovingAverage
from quantopian.pipeline.filters import QTradableStocksUS


def initialize(context):
    # Attach pipeline to algorithm
    algo.attach_pipeline(
        make_pipeline(),
        'data_pipe'
    )

    # Schedule rebalance function
    algo.schedule_function(
        rebalance,
        date_rule=algo.date_rules.week_start(),
        time_rule=algo.time_rules.market_open()
    )


def before_trading_start(context, data):
    # Get pipeline output and
    # store it in context
    context.pipeline_data = algo.pipeline_output('data_pipe')


def rebalance(context, data):
    # Display first 10 rows
    # of pipeline output
    log.info(context.pipeline_data.head(10))


# Pipeline definition
def make_pipeline():

    base_universe = QTradableStocksUS()

    sentiment_score = SimpleMovingAverage(
        inputs=[stocktwits.bull_minus_bear],
        window_length=3,
    )

    return Pipeline(
        columns={
            'sentiment_score': sentiment_score,
        },
        screen=(
            base_universe
            & sentiment_score.notnull()
        )
    )
```

これで、取引可能な資産の範囲を毎日選択し、ポートフォリオ内の資産配分を決定するために使用できるアルファスコアを生成ができました。

###Portfolio Management
ここでは、パイプラインで生成されたアルファスコアをアルゴリズムがどのように使用してポートフォリオのバランスを取り戻すかを定義します。 目標は、一連のルールや制約によって定義された特定の構造を維持しながら、アルファスコアに基づいて収益を最大化するターゲットポートフォリオを見つけることです。
これは、ポートフォリオ最適化問題と呼ばれます。 QuantopianのOptimize APIを使用すると、パイプラインの出力を目的と一連の制約に簡単に変換できます。 
その後、 `order_optimal_portfolio` を使用して、現在のポートフォリオを、仕様を満たすターゲットポートフォリオに移行できます。 最初のステップは目的を定義することです。 `MaximizeAlpha` を使用します。これは、アルファスコアに比例して資本を資産に割り当てようとします。

```python
# Import Optimize API module
import quantopian.optimize as opt


def rebalance(context, data):
  # Retrieve alpha from pipeline output
  alpha = context.pipeline_data.sentiment_score

  if not alpha.empty:
      # Create MaximizeAlpha objective
      objective = opt.MaximizeAlpha(alpha)
```

次に、ターゲットポートフォリオに満たさせたい制約のリストを指定する必要があります。
初期化でいくつかのしきい値を定義し、それらを `context` 変数に格納することから始めましょう。

```python
# Constraint parameters
context.max_leverage = 1.0
context.max_pos_size = 0.015
context.max_turnover = 0.95
```

これで、上で定義したしきい値を使用して、リバランスにおける制約を指定できます。

```python
# Import Optimize API module
import quantopian.optimize as opt


def rebalance(context, data):
  # Retrieve alpha from pipeline output
  alpha = context.pipeline_data.sentiment_score

  if not alpha.empty:
      # Create MaximizeAlpha objective
      objective = opt.MaximizeAlpha(alpha)

      # Create position size constraint
      constrain_pos_size = opt.PositionConcentration.with_equal_bounds(
          -context.max_pos_size,
          context.max_pos_size
      )

      # Constrain target portfolio's leverage
      max_leverage = opt.MaxGrossExposure(context.max_leverage)

      # Ensure long and short books
      # are roughly the same size
      dollar_neutral = opt.DollarNeutral()

      # Constrain portfolio turnover
      max_turnover = opt.MaxTurnover(context.max_turnover)
```

最後に、目的のポートフォリオと制約のリストを `order_optimal_portfolio` に渡してターゲットポートフォリオを計算し、現在のポートフォリオを最適な状態に移行するのに必要な注文を発信できます。

```python
# Import Algorithm API
import quantopian.algorithm as algo

# Import Optimize API
import quantopian.optimize as opt


def rebalance(context, data):
  # Retrieve alpha from pipeline output
  alpha = context.pipeline_data.sentiment_score

  if not alpha.empty:
      # Create MaximizeAlpha objective
      objective = opt.MaximizeAlpha(alpha)

      # Create position size constraint
      constrain_pos_size = opt.PositionConcentration.with_equal_bounds(
          -context.max_pos_size,
          context.max_pos_size
      )

      # Constrain target portfolio's leverage
      max_leverage = opt.MaxGrossExposure(context.max_leverage)

      # Ensure long and short books
      # are roughly the same size
      dollar_neutral = opt.DollarNeutral()

      # Constrain portfolio turnover
      max_turnover = opt.MaxTurnover(context.max_turnover)

      # Rebalance portfolio using objective
      # and list of constraints
      algo.order_optimal_portfolio(
          objective=objective,
          constraints=[
              constrain_pos_size,
              max_leverage,
              dollar_neutral,
              max_turnover,
          ]
      )
```

###リスク管理
目標ポートフォリオの構造に対する制約を設定することに加えて、その業績に影響を及ぼし得る共通のリスク要因に対するそのエクスポージャーを制限したいと思います。
例えば、株価指数のセンチメントデータは一時的なものであり、センチメントスコアの急上昇を利用するという意図のために、アルゴリズムは短期の逆転リスクにさらされる可能性があります。
ポートフォリオの共通リスク要因に対するエクスポージャーを管理するためにQuantopianのリスクモデルを使用します。 
リスクモデルは、11のセクター要因と5つのスタイル要因（短期の組戻しを含む）の16種類のリスク要因に対する、資産エクスポージャーを計算します。  `risk_loading_pipeline` 関数を使用すると、リスクモデルの各因子について出力列を生成するデータパイプラインを返すので、アルゴリズム内でこのデータに簡単にアクセスできます。 データパイプラインと同様に、リスクデータパイプラインをアルゴリズムに付加し、それを識別するための名前を指定する必要があります。 それから、その出力を `before_trading_start` に入れて、コンテキストに格納することができます。

```python
# Import Algorithm API
import quantopian.algorithm as algo

# Import Risk API method
from quantopian.pipeline.experimental import risk_loading_pipeline

def initialize(context):
    # Constraint parameters
    context.max_leverage = 1.0
    context.max_pos_size = 0.015
    context.max_turnover = 0.95

    # Attach data pipelines
    algo.attach_pipeline(
        make_pipeline(),
        'data_pipe'
    )
    algo.attach_pipeline(
        risk_loading_pipeline(),
        'risk_pipe'
    )

    # Schedule rebalance function
    algo.schedule_function(
        rebalance,
        algo.date_rules.week_start(),
        algo.time_rules.market_open(),
    )


def before_trading_start(context, data):
    # Get pipeline outputs and
    # store them in context
    context.pipeline_data = algo.pipeline_output(
      'data_pipe'
    )

    context.risk_factor_betas = algo.pipeline_output(
      'risk_pipe'
    )
```

次のステップは、 `RiskModelExposure` 制約をポートフォリオ最適化ロジックに追加することです。 
この制約は、リスクモデルによって生成されたデータを使用して、モデルに含まれる各要因に対するターゲットポートフォリオの全体的なエクスポージャーに制限を設定します。

```python

# Constrain target portfolio's risk exposure
# By default, max sector exposure is set at
# 0.2, and max style exposure is set at 0.4
factor_risk_constraints = opt.experimental.RiskModelExposure(
    context.risk_factor_betas,
    version=opt.Newest
)
```

最後に、以下のアルゴリズムは、戦略とポートフォリオ構築ロジックを網羅しており、バックテストの準備ができています。

```python
# Import Algorithm API
import quantopian.algorithm as algo

# Import Optimize API
import quantopian.optimize as opt

# Pipeline imports
from quantopian.pipeline import Pipeline
from quantopian.pipeline.data.psychsignal import stocktwits
from quantopian.pipeline.factors import SimpleMovingAverage

# Import built-in universe and Risk API method
from quantopian.pipeline.filters import QTradableStocksUS
from quantopian.pipeline.experimental import risk_loading_pipeline


def initialize(context):
    # Constraint parameters
    context.max_leverage = 1.0
    context.max_pos_size = 0.015
    context.max_turnover = 0.95

    # Attach data pipelines
    algo.attach_pipeline(
        make_pipeline(),
        'data_pipe'
    )
    algo.attach_pipeline(
        risk_loading_pipeline(),
        'risk_pipe'
    )

    # Schedule rebalance function
    algo.schedule_function(
        rebalance,
        algo.date_rules.week_start(),
        algo.time_rules.market_open(),
    )


def before_trading_start(context, data):
    # Get pipeline outputs and
    # store them in context
    context.pipeline_data = algo.pipeline_output('data_pipe')

    context.risk_factor_betas = algo.pipeline_output('risk_pipe')


# Pipeline definition
def make_pipeline():

    sentiment_score = SimpleMovingAverage(
        inputs=[stocktwits.bull_minus_bear],
        window_length=3,
        mask=QTradableStocksUS()
    )

    return Pipeline(
        columns={
            'sentiment_score': sentiment_score,
        },
        screen=sentiment_score.notnull()
    )


def rebalance(context, data):
    # Retrieve alpha from pipeline output
    alpha = context.pipeline_data.sentiment_score

    if not alpha.empty:
        # Create MaximizeAlpha objective
        objective = opt.MaximizeAlpha(alpha)

        # Create position size constraint
        constrain_pos_size = opt.PositionConcentration.with_equal_bounds(
            -context.max_pos_size,
            context.max_pos_size
        )

        # Constrain target portfolio's leverage
        max_leverage = opt.MaxGrossExposure(context.max_leverage)

        # Ensure long and short books
        # are roughly the same size
        dollar_neutral = opt.DollarNeutral()

        # Constrain portfolio turnover
        max_turnover = opt.MaxTurnover(context.max_turnover)

        # Constrain target portfolio's risk exposure
        # By default, max sector exposure is set at
        # 0.2, and max style exposure is set at 0.4
        factor_risk_constraints = opt.experimental.RiskModelExposure(
            context.risk_factor_betas,
            version=opt.Newest
        )

        # Rebalance portfolio using objective
        # and list of constraints
        algo.order_optimal_portfolio(
            objective=objective,
            constraints=[
                constrain_pos_size,
                max_leverage,
                dollar_neutral,
                max_turnover,
                factor_risk_constraints,
            ]
        )
```

#バックテストでの分析
バックテストが終了したら、[Notebook]タブをクリックしてください。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/269343/5c5c73a9-b8f3-b570-c6ac-0582b201e15d.png)

これにより、以下のコードを含む `Research` ノートブックが表示されます。

```python
bt = get_backtest('5a4e4faec73c4e44f218170a')
bt.create_full_tear_sheet()
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/269343/337fda64-5ca9-da11-1127-c495637f15e0.png)

もう1つの興味深い部分は、パフォーマンス属性セクションです。 
以下のプロットは、Quantopianのリスクモデルを使用して、収益のどれだけがあなたの戦略に起因しているのか、そしてそのどれだけが共通のリスク要因から生じているのかを示しています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/269343/16964fcb-b326-0448-3578-5ad020afe5a4.png)

上記のように、ポートフォリオの総収益の大部分は特定の収益から生じています。
これは、アルゴリズムのパフォーマンスが一般的なリスク要因にさらされているためではないことを示しており、これは良いことです。 
これで、チュートリアル1は終了となります。