Life is Like a Boat

忘備録や経済、投資、プログラミングに関するメモやtipsなど

Herokuまわりの勉強

半日中、Herokuまわりの勉強をしていました。

以下忘備録かねてまとめたものです。

PostgreSQLのHobby Basicプラン

HerokuではPostgreSQLのAddonが無料から使えます。しかし、無料プラン(Hobby Dev)は最大10,000行まで。日々のプライスデータなら3日もあれば一杯になってしまいます。 その次のHobby Basicプランだと$9/月で最大10,000,000行まで使えます。

これなら年単位で大丈夫そうです。

LocalからHeroku Postgresにdbをdumpしたい

  1. 公式ではLocal分をdump
  2. dumpファイルをS3に載せる
  3. pg:backups:restoreでHeroku Postgres側に取り込む

の方法を推奨しているようです。

S3にupしたファイルを一時的に公開アクセス状態にするのがベストプラクティス的にどうかと思います。あと、S3使ってない人はイラっとするでしょう。 私はこれくらいで別の方法を探すような時間の無駄をしたくないので公式ガイドに沿ったやり方にしました。
Importing and Exporting Heroku Postgres Databases with PG Backups | Heroku Dev Center

Herokuアプリ間でHeroku Postgresを共有する

1千万行もあれば、複数アプリ間で一つのdbを共有したくなります。オフィシャルのドキュメントを読んでいるとそれが可能だとわかりました。

heroku addons:attach Heroku Postgresアドオンが乗ってるApp名::DATABASE --app 共有したいApp名

例えば、App名がmisoでここにHeroku Postgresのアドオンを載っけているとします。新たに作ったApp、sushiで共有したい場合
heroku addons:attach miso::DATABASE --app sushi
となります。

これでアプリsushiの環境変数にDATABASE_URLが追加されます。

https://devcenter.heroku.com/articles/heroku-postgresql#sharing-heroku-postgres-between-applications

pip freezeがイラっとする

requirements.txtというアプリに必要なライブラリをまとめたものをherokuにデプロイしないといけません。 $ pip freeze > requirements.txt するとrequirements.txtに書き出されるのですが、いままでpip installした全てのものが書き出されるようです。仕方がないので、アプリでimportしているものだけをマニュアルで記述するようにしました。
すると案の定、実行時にこれが足りないだのエラーが出て都度都度パッケージ名を書く羽目になると。
他にいい方法があればどなたか教えてください (><)。

日経平均の月足データをPandasのDataframeにする

Monthlyのパフォーマンスを日経と比較して見たい時や指標と日経の相関見てみたいなという時に月次の225のデータがあると何かと便利です。

どこかのサイトでデータ用意してくれないかなと調べていたところ、ありがたいことに、本家のサイトからcsv形式でダウンロードできるじゃないですか。

HighとLowも用意してあるのでありがたいです。

https://indexes.nikkei.co.jp/nkave/historical/nikkei_stock_average_monthly_jp.csv

以下、ダウンロードからDataframe化するまでのソースです。

import pandas as pd
import io
import requests
from datetime import datetime
import re

NIKKEI_MONTHLY_DATA_URL = 'https://indexes.nikkei.co.jp/nkave/historical/nikkei_stock_average_monthly_jp.csv'


data = requests.get(NIKKEI_MONTHLY_DATA_URL).content.decode('shift-jis')
nikkei_monthly_df = pd.read_csv(io.StringIO(data))
nikkei_monthly_df

#rename columns
nikkei_monthly_df = nikkei_monthly_df.rename(columns={
    'データ日付': 'date',
    '終値': 'Close', '始値': 'Open', '高値': 'High', '安値': 'Low'})

#データ日付欄(rename後はdate)をdatetimeにする
pattern = re.compile(r'([12]\d{3}/(0[1-9]|1[0-2])/(0[1-9]|[12]\d|3[01]))')
nikkei_monthly_df.date = list(map(lambda x : datetime.strptime(x, '%Y/%m/%d') if pattern.match(x) else x, nikkei_monthly_df.date))
nikkei_monthly_df = nikkei_monthly_df.dropna(thresh=3)

nikkei_monthly_df = nikkei_monthly_df.set_index('date')
nikkei_monthly_df

f:id:nerimplo:20180707221901p:plain

Plotlyでチャートを描画するとこんな感じになります。PlotlyのCandleStickチャートはOpen, High, Low, Closeを指定するだけでいいので直感的です。

f:id:nerimplo:20180707232651p:plain

from plotly.offline import init_notebook_mode, iplot
import plotly.graph_objs as go

init_notebook_mode(connected=True)

trace = go.Candlestick(x=nikkei_monthly_df.index,
                       open=nikkei_monthly_df.Open,
                       high=nikkei_monthly_df.High,
                       low=nikkei_monthly_df.Low,
                       close=nikkei_monthly_df.Close)

layout = go.Layout(
    xaxis = dict(
        rangeslider = dict(
            visible = False
        )
    ),
    yaxis = dict(exponentformat="none")
)

data = [trace]
fig = go.Figure(data=data,layout=layout)
iplot(fig, filename='simple_candlestick')

デフォルトだとy軸が20kと表示されてしまうのでyaxis = dict(exponentformat="none")をセットしてあげます。

TradingViewのチャートにエントリーポイントを加える方法

個別銘柄チャートに自分が売買した日付の印をつけたいなと思うことがあります。 売買タイミングの振り返りにも役立つと思います。

米国株のPF管理にTradingViewを使い出したので、チャート機能が充実しているTradingViewならエントリーとイグジットポイントをプロットする方法あるはず。

そこでネットであれこれ探しながらいい方法はないものか自分なりに解決策を探して見ました。

以下ソースコードです。

//@version=3
study("My Script", overlay=true)

f(x) => x * 1000

unix_time_start = input(1521644400.0)
unix_time_end = input(1521730800.0)

condition = time >= f(unix_time_start) and time <= f(unix_time_end)
plotshape(condition ? close : na, color=blue, style=shape.triangledown, location=location.abovebar, text="Buy")

TradingViewではPine Scriptという言語を使っています。
condition = time >= f(unix_time_start) and time <= f(unix_time_end)で描画する条件を指定してあげます。
この場合unix time 1521644400.0 (3月22日) と 1521730800.0 (3月23日)の間に描画するという条件です。
startとendはunix timeで1日分差ないと描画されないみたいです。

f:id:nerimplo:20180706180752p:plain
closeがPriceのデータセット?のようです。color, style, locationを変えることで印の形や位置、色を変えることができます。

ソースはPine Editorパネルに記述し、Save, Add to Chartをクリックすると矢印がチャートにoverlayされるはずです。

少々見にくいですね。文字の大きさは変えられないようです.... 他にいいやり方ご存知の方、ぜひシェアをお願いします! f:id:nerimplo:20180706180501p:plain

KABU+を使ってみた。csvのダウンロードからPandasで利用するまで。

駄犬さんの紹介でKABU+さんのスタンダードプランに申し込んでみました。

KABU+は株価データ・投資指標データ等の株式投資関連データを提供する情報サービスです。

KABU+からCSVをダウンロードする

KABU+サービスを申し込むとPaypalで決済したのちIDとパスワードが送られてきます。 Basic認証なのでプログラム的にダウンロードするにはヘッダーにIDとpasswordをbase64でエンコードしてやつをヘッダー名Authorizationの値としてつけて送らないといけません。 requestsHTTPBasicAuthを使うとその辺よしなにやってくれます。

def retrieve_csv_file(url):
    res = requests.get(url, auth=HTTPBasicAuth('YOUR_ID', 'YOUR_PASSWORD'))
    data = res.content.decode('shift-jis')
    return data

この戻り値をcsvにします。

def to_csv(data, file=None):
    if file:
        with open(file, 'w', encoding='utf-8') as f:
            writer = csv.writer(f)
            reader = csv.reader(data.splitlines())
            for row in reader:
                writer.writerow(row)

あとはこれらのメソッドを実行するだけ。カレントディレクトリにtest.csvが作成されているはずです。

if __name__ == '__main__':
    URL = 'https://csvex.com/kabu.plus/csv/japan-all-stock-prices-2/daily/japan-all-stock-prices-2_20180104.csv'
    data = retrieve_csv_file(URL)
    to_csv(data, './test.csv')

Pandasで利用する

KABU+からダウンロードしたcsvのヘッダーは日本語なので後々の処理も考えてdataframeのカラム変換用のマッピングを作ります。

mapping = {'SC': 'Code',
           '名称': 'Name',
           '市場': 'Market',
           '業種': 'Industry',
           '日時': 'Timestamp',
           '株価': 'Price',
           '前日比': 'Change',
           '前日比(%)': 'ChangeInPercent',
           '前日終値': 'PreviousClosePx',
           '始値': 'Open',
           '高値': 'High',
           '安値': 'Low',
           'VWAP': 'VWAP',
           '出来高': 'Volume',
           '出来高率': 'VolumeInPercent',
           '売買代金(千円)': 'TradingVolume',
           '時価総額(百万円)': 'MarketCap',
           '値幅下限': 'LowerRange',
           '値幅上限': 'UpperRange',
           '高値日付': 'HighDate',
           '年初来高値': 'YTDHigh',
           '年初来高値乖離率': 'DeviationFromYTDHigh',
           '安値日付': 'LowDate',
           '年初来安値': 'YTDLow',
           '年初来安値乖離率': 'DeviationFromYTDLow'}

あと後処理しやすいよう、データ型をそろえてあげます。

    non_double_precisions = ['Code', 'Name', 'Market', 'Timestamp', 'Industry', 'HighDate', 'LowDate']

    df = pd.read_csv(file)
    df = df.rename(columns=mapping)

    df['Timestamp'] = list(
        map(lambda x: datetime.strptime(x, '%Y/%m/%d %H:%M') if type(x) == str and x != '-' else x, df['Timestamp']))

    df['HighDate'] = list(
        map(lambda x: datetime.strptime(x, '%Y/%m/%d') if type(x) == str and x != '-' else x, df['HighDate']))

    df['LowDate'] = list(
        map(lambda x: datetime.strptime(x, '%Y/%m/%d') if type(x) == str and x != '-' else x, df['LowDate']))

    df = df.replace('-', np.nan)

    for column_type in mapping.values():
        if column_type not in non_double_precisions:
            df[column_type] = list(map(lambda x: float(x), df[column_type]))

    df = df.set_index('Code')

これでdataframeが出来上がったので、いろいろ加工できます。

データベースに突っ込む

ここから先、時系列のデータをDBに突っ込んで必要な時にDBからPandasで読み込むのがスマートなやり方ではないでしょうか。

その方法としていくつかあると思います。考えつくのが

  • PostgreSQLのCOPYコマンドを使ってKABU+からダウンロードしたcsvを突っ込む

  • PythonのORMライブラリを使ってcsvを読み込み、dbにinsert

  • Pandasでcsvを読み込んだものをそのまま、to_sqlする

かと思います。私は3番目のやり方にしました。

コードとTimestampの組み合わせでUniqueなIDをつくるとTimestampが欠損しているケースがあり、こんな感じでDBから怒られます。

ERROR: could not create unique index "ix_stockpx2_id" Detail: Key (id)=(5602540528120821380) is duplicated.

原因はこのレコードのせいでした。

f:id:nerimplo:20180705195611p:plain

7857 セキおそらく出合いがなくプライスが付かなかったのが4日分あったのでしょう。

対処としてやったのが、Timestampの欠損時にはKABU+のCSVのファイル名から日付を取得。このDatetimeを充てる、です。

これでIdをuniqueなprimary keyにすることができました。

SELECT * from stockpx2 where "Code" = 1333 order by "Timestamp"で結果を確認

f:id:nerimplo:20180705201859p:plain

これで準備は整った感じがあります。
Localにサーバー立ててlocalhost/service/api/history?code=1333みたいにRestfulなAPIを作って、適宜Pandas側からプライスを呼び出せるようにしてもいいかもしれません。

景気ウォッチャー調査投資法なるものがあるそうです

9/9アップデート

Herokuで景気ウォッチャー調査投資法を紹介するサイトを作りました。 economy-watcher.herokuapp.com

景気ウォッチャー調査は市井の景気動向を知るのに役立つ指標と言われています。というのも、調査対象がタクシー運ちゃんや小売店の店員さん、採用担当者や中小企業の社員と、ビジネスのフロントラインの人々に対して景気への見方を尋ねているからです。

例えば東北地方の高級レストランの経営者は客の様子について

デパートなども物が売れていない。経済が全然回っていないのではないか。

とコメントし、家計動向の見通しを悪い(最低ランク)としています。

また東京の都市型ホテルのスタッフは5月に

ゴールデンウィークという特需期間により、来客数は3か月前より大幅に増加している。比較的温暖な気候であったことも要因といえる。

とのコメント。見通しは良くなっている(最高ランク)の評価としています。

景気ウォッチャー調査では、これらの評価の構成比分と5段階評価に対応する点数をかけてDIを算出します。

こちらが先行きDIと現状DIの推移。

f:id:nerimplo:20180703174715j:plain

本題のこの統計を使った投資法です。

三井住友AMの宅森氏によると

前月比マイナスが続いた現状判断DIがプラスに転じ、なおかつ1.0以上改善したら「買い」、プラスからマイナスに転じ、1.0以上悪化したら「売り」という売買シグナルを設定

いう売買ルールを設定、発表美の引け値で売買したところ、2000年以降のデータで33勝22敗になり、累計の利益が25,370円になったとのこと。

勝率6割の「景気ウォッチャー投資法」とは? | マネーポストWEB - Part 2

このルールを年初に適応してみると、

2月8日発表分(調査が行われたのは2018年1月)の現状DIが53.9 -> 49.9に-4.0ポイント悪化しています。これで売りシグナルが点灯。
しかし、下記のチャートでもわかるように、日経平均はすでに下落を始めています(年初の急落は2月2日からでした)

このルールに忠実に従えば、実際日経を売り建てるタイミングとしては2/8で終値21,890.86です。3/26が年初来最安値20,766.1でして、この時点でもまだ売りシグナルは点灯中。ちなみに6月にかけても売りシグナル点灯中です。2/8から日経の売りポジションをずっと持っていると仮定すると現時点ではほぼトントンくらいでしょうか。

f:id:nerimplo:20180703174426p:plain

年利10%を稼ぐ[景気ウォッチャー]投資とは? | ハーバービジネスオンライン

景気ウォッチャー投資法とは?手堅い投資が可能か。

追記

シグナルに先行DIを加味した結果、パフォーマンスが2割ほど改善しました。詳しくはこちらのブログ記事をご覧ください。

nerimplo.hatenablog.com

景気ウォッチャー調査のxlsファイルをpandasで加工してみる

官公庁が出している統計データの大半はpdf, csv, xlsです。pdfはさておき、pandasはcsvやxlsをわりかし楽にdataframeに取り込めるのでありがたいです。

元データ

統計表(Excel表形式):景気ウォッチャー調査 - 内閣府 季節調整値の全国の分野・業種別、地域別DIの推移(Excel形式)を使います。

PandasでImport

df = pd.read_excel(XLS_FILE_PATH, sheet_name=sheet_type, skiprows=5)

excelファイルを取り込むメソッドはread_excelです。sheet_nameを指定してあげるとそのシートをdataframeにしてくれます。

skiprowsで不要な行をスキップします。

官公庁のデータ見てると、例えば年月のカラムがこの様に表現されているケースをよく見かけます。   f:id:nerimplo:20180702155531p:plain 2002年1月、2002年2月とするはずが、年を省略してあります。
dataframeとして取り込むと、この年の部分が欠損値になってしまい、時系列で分析できなくなります。

欠損値を埋める

Pandasの素晴らしい点は、欠損値を他の値で埋めてくれるメソッドを用意してくれているところです。

df['ABC']=df['ABC'].fillna(method='ffill')

fillnaでmethod='ffill'とすると前の値で置き換えられます。この場合は矢印の部分が2002年になりますね。
下記参考にしたブログ記事です。 pandasで欠損値NaNを除外(削除)・置換(穴埋め)・抽出 | note.nkmk.me

下記はシート名、分野別(現状)、分野別(先行き)をdataframe化する際のサンプルです。

import pandas as pd
from datetime import datetime
import numpy as np

XLS_FILE_PATH = 'EXCEL_FILE_PATH'

def combine_year_and_month(year,month):
    y = year.replace('年', '').replace(' ','')
    m = month
    return datetime(year=int(y), month=int(m), day=1)

def load_sheets_by_category():
    dfs = []
    for sheet_type in ['分野別(現状)', '分野別(先行き)']:
        df = pd.read_excel(XLS_FILE_PATH, sheet_name=sheet_type, skiprows=5)
        df = df.drop('Unnamed: 0',axis=1)
        df['Unnamed: 1']=list(map(lambda x : x.replace('\u3000',' ') if type(x) == str else x, df['Unnamed: 1']))
        df = df.replace(r'\s+', np.nan, regex=True)

        df['Unnamed: 1']=df['Unnamed: 1'].fillna(method='ffill')

        df['YearMonth']=list(map(combine_year_and_month, df['Unnamed: 1'], df['Unnamed: 2']))

        #YearMonth欄を作ったので1,2は不要
        df = df.drop('Unnamed: 1',axis=1)
        df = df.drop('Unnamed: 2',axis=1)

        NAMING_RULE = {
                        'Unnamed: 3':'合計',
            'Unnamed: 5':'小売関連',
            'Unnamed: 6':'飲食関連',
            'Unnamed: 7':'サービス関連',
            'Unnamed: 8':'住宅関連',
            'Unnamed: 4':'家計動向',
            'Unnamed: 9':'企業動向',
            'Unnamed: 10':'製造業',
            'Unnamed: 11':'非製造業',
            'Unnamed: 12':'雇用関連',
                      }

        df.rename(columns=NAMING_RULE, inplace=True)
        dfs.append(df)
    return dfs

df_current, df_future = load_sheets_by_category()

フィデリティのFM、Joel氏のインタビュー動画

ろくすけさんのブログで紹介されていたフィデリティのファンドマネージャーJoel Tillinghast氏。

Fidelity Series Intrinsic Opportunities Fund(FDMLX) - ろくすけの長期投資の旅

氏が運用に携わるのが、FLPSX、四季報の大株主検索だとBBHフィデリティイントリンシックオポチュニティズでヒットします。

Youtubeの閲覧履歴を見ていたら、以前見たJoel氏のBBGとのインタビュー(昨年秋)を見つけたので、忘備録代わりに以下ポイントを書き出してみました。
www.youtube.com

  • 自分自身は小売企業をバーゲンハンティングできるエリアと考えている。しかしどの小売が生き残るかわからない。Jeff Bezosがカンファレンスコールでどのリテールが死ぬか教えてくれるだろう(ジョーク混じりw。Bezosが他社のマージンがAmazonのチャンスと言っているので、素晴らしいビジネスモデルを有している様に見える企業はAmazonのターゲットリストになっている。幾つかのリテール企業の株式を保有しているが自信はない。

  • 株式市場で取引される企業の数が半分になってしまった。バーゲンの機会が減って非常に腹立たしい。アメリカのビジネスや社会はWinner-takes-allの性格が顕著。独占や寡占が進んでいる。この方向性が変わるのは難しいのでは。

  • バリュー投資は難しくなっているが、有効なアプローチだと思う。価値をどの様に判断するか、企業が強靭なビジネスを持っているか。有能な経営陣に率いられているか。バリュー投資家は、将来が予想したよりも明るい時や、まあまあいいだろうと思って掴んでいたものが素晴らしいものになった時、に報われるだろうが、それは以前より難しくなっていると思う。

  • たくさんのスモールキャップの日本株を買っている。フィデリティ日本の中小型リサーチチームは素晴らしい。リサーチの対象になっていない企業も見つけ出してくれる。

  • パッシブ運用は「Idiotsが経営陣にいても、その一部をインデックスのなかに含める」ようなもの。パッシブ運用の台頭でマーケットはより非効率になったと思う。

  • フィデリティはピーターリンチのような素晴らしいファンドマネージャー陣がいたのでインデックス運用のブームに乗り遅れたのは認める。

  • AIが素晴らしい経営陣や強靭なビジネスを有している企業を教えてくれれば嬉しいがLet's see.

  • 28年間マーケットに携わってきたキャリアの中で、驚くことに何が一番変わって”いない”かといえば、それは人間の感情。人々は今日うまく行っていることについて過度に熱狂的になり、今日悪くなっていることには過度に悲観的になる。人間の行動は変わらない。