Life is Like a Boat

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

1月雑感

あけましておめでとうございます。実はこの投稿が2020年初です。

ブログ書くペース落ちたなと昨年からの投稿数/月を見ているんですが、今年は月1は維持したいなと思ってます。

 

昨年末から振り返ると、年末年始は帰省先で過ごしていました。

 

その間やったアイテムをあげると

山登り、買い物、釣り、墓掃除、庭掃除、子供と庭で遊ぶ(バトミントン、野球)、BBQと焼き芋、餅付き

で、餅付きなど普段なかなか出来ないことをやっているうちにあっという間に1日過ぎていく感じでした。

 

子供と外遊びすると、やっぱり庭付きの戸建てがある環境はいいなと自分が育った家ですが改めて思います。

 

去年から住む場所の話を家族でしています。

親戚が多い九州に引っ越すか、それならスタートアップが熱いと聞く福岡、英語は問題なく使えるので海外住めるなどと言われますが、やはり日本国の社会保障の素晴らしさは外に出て分かるもので、その選択肢は現実的じゃないかなと思っています。いろんな要素が絡んでいるので結局現状維持になりそうな予感がしています。

 

だいたいにおいて選択肢が複数ある場合、一つを選ぶと他の道を選んだ場合の何々になれたかもしれない自分を消している事になると思います。その選択の連続で生きてきて、2020年になりました。そう考えると、次の10年どこで何している、何していたいと聞かれたところで、正直全然見通せないです。

 

 

 

 

投資面では、昨年は日本株よりも米株が良い成績でした。

 

その中でもTSLAの去年8月からのリバウンド、その後の新高値更新が1番のサプライズです。この会社の株式は同社のロケットが火星に行くくらい先の未来まで長期的に保有するつもりでいます。


今年も引き続き「堀」の性質を持つビジネスを展開する企業を保有し、新たに探していきたいと思います。その中でも、業界特化型SaaSは好みの分野です。例えば、昨年8月以降のパフォーマンスはパッとしなかったですが、VEEVの様なタイプです。

 

 

連日新型肺炎のニュースですが、自分はこういう時にどうも楽観視する癖があると思っています。

 

2009年の豚インフルの時を思いだすと最初は大騒ぎしていたが、後で収束していきました。豚インフルかかった友人と鎮静化して数ヶ月後がお茶した時、あぁそういえば最初のころ留学帰りの高校生が感染してその学校の校長が叩かれてたよねぇ、と話したのを覚えています。

 

過去の事例を参照値として見たがる癖なんだと思ってます。

 

重症化率や致死率を過去と比較してこれだったから今回こうなるだろうと推測するわけですが、中国のサプライチェーンに与える影響度合いはより高まっているはずです。

 

特に製造業の調達担当者がどう行動するか、もともと春節はスケジュールに組み込まれていたはずで、それが伸びたことで様子見るか、あるいは他国の調達先を探すか。

速報性があるじぶん銀行製造業PMI(https://www.markiteconomics.com/public?language=ja)や来月の景気ウォッチャー調査のコメントを見てみたいところです。

 

 

Django x Salesforce

この記事はDjango Advent Calendar 2019の記事です。

Djangoは2019年にちょっと時間割いて勉強しておこうと思ったPythonのWebフレームワークです。

きっかけはFinpyに参加されていたakiyokoさんのLTで、氏が著した下記3本やチュートリアルをみながら学習していった感じです。

現場で使える Django の教科書《基礎編》

現場で使える Django の教科書《基礎編》

現場で使える Django の教科書《実践編》

現場で使える Django の教科書《実践編》

Advent Calenderは初参加で何か書くぞと宣言したはいいもの、このネタを思いついたのが実は昨日ですw

Djangoと何かを掛け合わせて相乗効果がでるようなネタがいいなと、自分のSalesforce開発に関わった経験とDjangoを繋ぐためのライブラリを探したところ、見つけたのがdjango-salesforceです。

このライブラリのメリットを一言で表すと、「Django ORMと統合することでSalesforce上のレコードをPostgreSQLやMySQLをBackendに使う場合の様に操作できる点」だと思います。

github.com

セットアップ

基本公式のREADMEに全て書いてあります。

django-salesforce/README.rst at master · django-salesforce/django-salesforce · GitHub

セットアップの肝としては、Django側とSalesforce側のやり取りはConnected Appを通して行われます。Connected AppはOAuth認証を使用して外部アプリとSalesforce APIを統合するためのものです。この辺の解説が詳しいです。

Help | Training | Salesforce

Connected Appの具体的な使用事例としては、外部のBIツールからこれを介してSalesforce内のCRMデータにアクセスし、BIツール側でレポートを作るなどがあります。

Model

DjangoでのModelがSalesforceでのオプジェクトになります。ドキュメントによれば全てのModelはsalesforce.models.Modelを継承していなければなりません。 また、Salesforce上に既にオブジェクトが存在している必要があります。例えば、Djangoチュートリアルで使われるPollアプリの質問や選択肢がSalesforce上にカスタムオブジェクトとして(Questionc, Choicec)として存在することが期待されます。これはdjango-salesforceがメタデータの作成までサポートしていないからだと思います。githubのissuesをざっくり眺めたのですが、この辺り将来的なロードマップへの言及は見つけられませんでした。

Salesforce側にModelが既にあれば、Django既存のデータベースをスキャンしてModelを自動作成してくれるinspectdbコマンドを使うことでmodels.pyへの記述が楽になります。ドキュメントによると、このコマンドを使ってmodels.pyを記述していく方法が推奨されている様です。 例: > python manage.py inspectdb Opportunity --database=salesforce

ここではDjangoチュートリアルのPollアプリを基に考えます。

Pollアプリ - Question

DjangoチュートリアルのPollアプリはQuestionとChoiceから構成されます。

先にSalesforce側でQuestion__cというカスタムオブジェクトを作成します。

f:id:nerimplo:20191216105612p:plain

f:id:nerimplo:20191216105617p:plain

次にinspectdbコマンドでQuestion__cのModelを自動生成します。

python manage.py inspectdb Question__c --database=salesforce

class Question(models.Model):
    name = models.CharField(max_length=80, verbose_name='質問名', default=models.DEFAULTED_ON_CREATE, blank=True, null=True)
    question_text = models.CharField(custom=True, db_column='question_text__c', max_length=200, verbose_name='question_text', blank=True, null=True)
    pub_date = models.DateTimeField(custom=True, db_column='pub_date__c', verbose_name='pub_date', blank=True, null=True)
    class Meta(models.Model.Meta):
        db_table = 'Question__c'
        verbose_name = '質問'
        verbose_name_plural = '質問'
        # keyPrefix = 'a07'

コマンド実行後のアウトプットは標準項目も含んでいるのですが、好きな様に簡略化しろとドキュメントにはあるので、この様にしました。models.pyにはname, question_text, pub_dateのみの項目とします。なお、inspectdbコマンドでカスタムオブジェクトのモデルを作成するとCが必ず最後につく様です。これは削除するようにしました。

ここまで、確認を兼ねてCRUDができるかやってみました。

Pollアプリ - CRUD

q = Question(name=4, question_text='どこにいきたい?', pub_date=datetime.today())
q.save()

f:id:nerimplo:20191216100746p:plain

ちゃんと保存されています!所有者は接続アプリのログインユーザーになっています。

> q = Question.objects.filter(name=2)
> q
<SalesforceQuerySet [<Question: Question object (a072v00001AWH9XAAX)>]>
> q[0].name
'2'
> q[0].question_text
'夏休み旅行の予算はいくらですか'

UpdateやDeleteも通常のDjangoモデルのそれと同じに扱える様です。deleteメソッドの場合、Salesforce側では論理削除になっています(削除済みのフラグが付けられ、ごみ箱入り)

Pollアプリ - Choice

Djangoチュートリアルも参考に選択肢(Choice)をmodels.pyに加えてみます。

class Choice(models.SalesforceModel):
    name = models.CharField(max_length=80, verbose_name='選択肢名', default=models.DEFAULTED_ON_CREATE, blank=True, null=True)
    question = models.ForeignKey('Question', models.DO_NOTHING, custom=True, blank=True, null=True, db_column='Question__c')
    choice_text = models.CharField(custom=True, db_column='choice_text__c', max_length=20, verbose_name='choice_text', blank=True, null=True)
    votes = models.DecimalField(custom=True, db_column='votes__c', max_digits=18, decimal_places=0, verbose_name='votes', blank=True, null=True)
    class Meta(models.Model.Meta):
        db_table = 'Choice__c'
        verbose_name = '選択肢'
        verbose_name_plural = '選択肢'

質問レコードを選んで、選択肢を加えてみます。

> q = Question.objects.get(name=1)
> q.id
'a072v00001AWH9SAAX'
> c = Choice(question_id=q.id, name='A', choice_text='A', votes=10)
> c.save()
> c = Choice(question_id=q.id, name='B', choice_text='B', votes=1)
> c.save()
> c = Choice(question_id=q.id, name='C', choice_text='C', votes=1)
> c.save()

f:id:nerimplo:20191216130238p:plain

関連リストにちゃんと選択肢が表示されています。

選択肢nameにAを含む質問

> Question.objects.filter(choice__name='A')
<SalesforceQuerySet [<Question: Question object (a072v00001AWH9SAAX)>]>

選択肢のvotesが5以上の質問

> Question.objects.filter(choice__votes__gt=5)
<salesforce.backend.models_sql_query.SalesforceQuery object at 0x10b8a9d90>
>>> q = Question.objects.get(name=1)
>>> q
<Question: Question object (a072v00001AWH9SAAX)>
>>> q.choice_set.all()
<SalesforceQuerySet [<Choice: Choice object (a082v00002pwq0hAAA)>, <Choice: Choice object (a082v00002pwq2YAAQ)>, <Choice: Choice object (a082v00002pwq2OAAQ)>]>

_setを使ったクエリもDjangoライクにできます。ライブラリ側でORM->SOQLクエリにしている様です。

活用案

ざっくりdjango-salesforceを使ってみた感じですが、このライブラリの活用案を考えてみたところ、

  1. 既にDjangoベースのtoC向けのシステムがあって、CRM側のデータを使ってフロントで見せたい。
  2. Django側にCRM側のデータを引っ張り出してユーザー認証後の画面で編集
  3. ライセンスをケチる必要がある(爆

があると思います。 このライブラリを使わなくても解決はできそうですが、必要は発明の母というので私が認識していない活用事例があるはずだ思っています。思いついて語りたい方、Twitterまで連絡ください!

勝ち馬に乗る

よく勝ち馬に乗るというアナロジーでモメンタム投資が語られます。モメンタム戦略では中期(3-12ヶ月)の株価パフォーマンスに基づいてポートフォリオを構築します。

今回は定義を変えて昨年の騰落率がプラスでかつ今年の騰落率がそれを上回っている「勝ち馬であり続ける勝ち馬」をみてみました。

まず2018年をプラスで引けた銘柄は525銘柄しかありません。全上場企業がだいたい3700社として、14%程度です。

そのうち、年初来で18年の騰落率をも上回っている企業は182社あります。

そのうちのトップ10は下記のテーブルの通りです。

コード セクター 社名 2018年騰落率 年初来騰落率
7748 製造用機械・電気機械 ホロン 127.24 134.43
3849 システム・ソフトウエア 日本テクノ・ラボ 102.93 186.4
3983 広告 オロ 89.99 128.2
7564 衣料品・服飾品小売 ワークマン 86.48 152.11
6067 企業向け専門サービス メディアフラッグ 81.23 108.46
9698 システム・ソフトウエア クレオ 60.51 76.57
6544 建設資材・設備 JESHD 52.56 69.55
4734 システム・ソフトウエア ビーイング 50.98 56.74
2492 システム・ソフトウエア インフォマート 48.23 88.05
3038 食品卸 神戸物産 47.67 101.48

1位のホロンはで半導体電子ビーム測定・検査装置専業の研究開発型企業(四季報より)だそうです。調べるまで聞いた事ない会社でした。

上記182社からさらにセクター別に銘柄数をカウントしてみると、システム・ソフトウェア、バイオ・医薬品、飲食店という順になりました。よく日経新聞に載っている産業天気図で好調さが伺えるセクターから高いパフォーマンスを出す企業が出ている事がわかります。だいたいこのセクターはアメリカ株でもパフォーマンスがよく、先月のNBRでも出演者のFMが似た様な話をしていました。

セクター 銘柄数 年初からの騰落率
システム・ソフトウエア 37 67.4
バイオ・医薬品関連 10 46.7
飲食店 10 15.8
コンテンツ制作・配信 9 67.2
企業向け専門サービス 8 113.9
製造用機械・電気機械 7 65.3
不動産・住宅 7 48.9
建設・土木 7 21.2
陸運 7 17.4
広告 6 67.8

帝王の投資哲学を読み返す

最近読み返している1999年初版の帝王の投資哲学という投資本があります。

アメリカの公共放送PBSで制作されたマネー番組を書籍化したものです。(原題はBeyond Wall Street)

マークモビアスやピーターバーンスタインなど著名な投資家のスタイル、手法の概要が取材されています。

それだけでなく、長くマーケットで生き抜いてきた知恵や人生観、マーケットの歴史などをそれぞれの投資家の半生を通じて読める部分が面白いです。

Amazonでは中古が13円から出ており、これはお買い得すぎる値だと思います。

帝王の投資哲学

帝王の投資哲学

  • 作者: スティーブンミンツ,トーマスウィルソン,ダナデイキン,Steven L. Mintz,Thomas Willison,Dana Dakin,石井吉文
  • 出版社/メーカー: 日本経済新聞社
  • 発売日: 1999/03
  • メディア: 単行本
  • この商品を含むブログ (1件) を見る

NBRの10月29日放送回より

S&P500が新高値をつけたわけですが、Nightly Business ReportでCIOのRaymond James氏が1. リセッションはこない、2. 予想より良い利益の伸び、3.季節性を理由にこのトレンドは持続すると解説しています。

特に3.の季節性については、4Qが歴史的にパフォーマンスがよい四半期であること、大統領選の前年であることをあげています。ちなみに過去60年間で行われた16回の大統領選のうち、S&P500がプラスになった年は14回あります。

同氏が推奨するのは米大型株、特にIT、通信サービス、ヘルスケアセクターです。 f:id:nerimplo:20191030162455p:plain

f:id:nerimplo:20191030162458p:plain

番組の動画はこちらから。 www.youtube.com

Pandasを使って営業日ベースで騰落率を調べる

例えば、トヨタ、ソフトバンク、任天堂、村田製作所、日本たばこ産業で構成されるポートフォリオがあるとします。ここからそれぞれの企業の四半期決算発表日前後の株価の騰落率を調べてみようと思います。

Pandasでこのようなデータフレームを作ります。

txt = """7974,任天堂,20190731
7203,トヨタ自動車,20190802
9984,ソフトバンクグループ,20190807
6981,村田製作所,20190731
2914,日本たばこ産業,20190731"""


companies = []
for row in txt.split('\n'):
    d = {}
    r = row.split(',')
    code, name = r[0], r[1]
    d['code']=r[0]
    d['name']=r[1]
    d['t']=r[2]
    companies.append(d)

df = pd.DataFrame(companies).set_index('code')

このようなデータフレームになります。日付はSBI証券から7~8月にかけての決算発表スケジュール(国内株式)を参照しました。

f:id:nerimplo:20190926121509p:plain

さらに決算発表日の2営業日前後の日付を取得します。

例えばGW期間直前や月曜日に決算発表をする場合、営業日ベースで考えると土日祝日を考慮する必要があります。

特に今年は譲位に伴う特別な祝日がGW期間中にありましたし、昨年まで休日だった上皇陛下の誕生日(12月23日)は休日ではなくなります。

私の知る限り、Pythonのdatetime周りのライブラリーで最新の日本の祝日を考慮してくれるものはないと思います。(あったら教えてください!)

date.weekday()で土日判定は可能なので、休日判定のためには総務省の祝日csvファイルを使うのがいいと思います。 このcsvは2020年までの休日が含まれているので、来年のどこかの時点で2021年分を取得して休日リストをアップデートしてあげる必要があります。

国民の祝日について - 内閣府

いろいろ試してみて結局、営業日APIを自作してHeroku Appにしてみました。 2019年9月26日の15営業日後を知りたい場合、クエリパラメータでfromとnを指定してあげます。 https://aio-business-days.herokuapp.com/biz?from=20190926&n=15

レスポンスはこのようにしてます。2019年9月26日の10営業日後の日付は10月18日。

{"date": "20191018", "from": "20190926", "n": "15"}

話をデータフレームに戻すと決算発表日のtがわかっているので前後2営業日の日付を取得します。 上記APIを叩くための関数を作り、それを呼び出した戻り値をデータフレームのセルの値にしてあげます。

df['t-2']=''
df['t+2']=''

BASE_URL = 'https://aio-business-days.herokuapp.com/biz?from={0}&n={1}'

def get_date(t,n):
    res = requests.get(BASE_URL.format(t,n))
    return res.json()

for idx, row in df.iterrows():
    value = get_date(row['t'],-2)
    df.at[idx,'t-2']=value['date']

for idx, row in df.iterrows():
    value = get_date(row['t'],2)
    df.at[idx,'t+2']=value['date']

f:id:nerimplo:20190926131305p:plain

同じ要領で二期間の騰落率(終値ベースで)を計算するAPIを使い(以前自作していました)、セルの値として入れてみるとこのようなデータフレームになりました。

f:id:nerimplo:20191028100834p:plain

決算発表後の騰落率がプラスであれば、ポジティブサプライズ、そうでなければネガティブサプライズと定義してパターン分けできそうです。

- +ve -ve
2営業日前 村田 任天堂、トヨタ、SBG、JT
2営業日後 任天堂 村田、トヨタ、SBG、JT

Pandasデータフレームのセルに色をつけたい

PandasのデータフレームをJupyter Notebook上でテーブル表示する際、セルに色をつけたいケースがあります。

具体的にはこんな風にしたい時です。

f:id:nerimplo:20190911105839p:plain

このテーブルは TOPIXコア30採用銘柄の毎月のベストとワーストパフォーマーを色付けしたものです。 例えば、今年の4月の騰落率でみると、任天堂が+17.10%でコア30銘柄中のベストパフォーマー、武田薬が-8.74とワーストになっています。

セルに色をつける事ができるとは聞いた事がありましたが、実際にどうするのか知らなかったので調べてみました。

まず、Pandas公式にはStylingを使うとあります。

pandas.pydata.org

よく読むと、やりたいことはカラム毎(毎月)に最大値、最小値を見つける関数を適用するのようです。

コードにすると下記のようになります。

  • データフレームの1つのカラムを指すデータ構造であるSeriesに対して、series.max()で最大(小)値を見つけ、最大(小)値でなければtrueで色付け(background-color)、そうでなければ何もしないという関数を作る。
def highlight_highest_on_each_month(series):
    is_max = series == series.max()
    return ['background-color: #FC6C4D' if v else '' for v in is_max]

def highlight_lowest_on_each_month(series):
    is_min = series == series.min()
    return ['background-color: #5B60F8' if v else '' for v in is_min]

is_max = series == series.max()の部分が直感的にわからない感じがしますが、

is_max = series == series.max()
print(is_max)

でboolのSeriesを生成しています。

  • メソッドチェーンを使ってそれぞれの関数をapplyしてあげます。
df = df.style.apply(highlight_highest_on_each_month).apply(highlight_lowest_on_each_month)
  • htmlファイルとして書き出します。
with open("df.html","w", encoding='utf-8') as f:
    f.write(df.render())

書き出したあとは、cssを調整するなどして見栄えを整えてあげればいいと思います。

参考記事

stackoverflow.com

PythonユーザのためのJupyter[実践]入門

PythonユーザのためのJupyter[実践]入門