見出し画像

米株プログラミング[2-6](銘柄分析編)

こんにちわ!トミィ(@toushi_tommy)です!今回は別のyahoo finance モジュールを導入して、銘柄の情報取得をやってみたいと思います。yahoo finance のホームページを見れば情報は集まるのですが、プログラミングで好きな情報だけを集めて出力させることで、日頃の作業の効率化ができると思います。それではやってみます。(GoogleColabを使う為に、必ずモジュール導入編 [0-1], [0-2] を実施してください。)

サークルは無料で運営しております。記事内容も無料です。トミィにジュースでもおごってあげようと思った方は投げ銭いただけると、今後の運営の励みになります。

yahoo finance モジュールインストール

インストールする新しいモジュールはこちらです。

yfinanceですが、利点としてはコンパクトにまとまっている事と、複数銘柄のデータを同時に取ってこれる点になります。既に導入方法はこちらのモジュール導入編 [0-2] に記載済みです。

pip install yfinance -t /content/drive/MyDrive/module

画像1

これで、モジュール導入完了です。

yahoo finance 情報確認

まず、こちらのコードを実行してみてください。

import sys
sys.path.append('/content/drive/MyDrive/module')
import yfinance as yf
import pandas as pd
import plotly.graph_objects as go

ticker = 'AAPL'
data = yf.Ticker(ticker)

df = pd.DataFrame.from_dict(data.info, orient='index').rename(columns={0:'Data'})
fig = go.Figure(data=[go.Table(
 columnwidth = [8, 20],
 header = dict(values=['Name','Data'], align='center', font_size=16, height=30,
             line_color='black', fill_color='white'
             ),
 cells = dict(values=[df.index, df['Data']],
            line_color='black',align=['center', 'left'],font_size=16, height=30,
            fill_color=[['rgb(235, 235, 235)', 'white']*(int(len(df)/2)+1)],
            )
 )],
)
fig.show()
print(data.info['sector'])

このように出力されます。

画像2

下にスクロールすることで、取得できる内容がそれぞれ確認できます。また、コードの最後に、それぞれのデータの取得方法を書いておりますが、このような形で取り出せます。

print(data.info['sector'])

 出力はこのようになります。

Technology

その他、以下のコードで様々なデータを取得できます。

配当金、株式分割データ取得

import sys
sys.path.append('/content/drive/MyDrive/module')
import yfinance as yf
ticker = 'AAPL'
data = yf.Ticker(ticker)

# show actions (dividends, splits)
print(data.actions)
# show dividends
print(data.dividends)
# show splits
print(data.splits)

data.actionsで両方同時取得、data.dividendsは配当金、data.splitsが株式分割のデータになります。出力は以下の通り。

両方の場合

画像3

配当金

画像4

株式分割

画像5

ファイナンスデータ取得

Yahooファイナンスページで取れるデータが以下のコードで取得できます。ファイナンスで取れるデータは、

Income Statement(損益計算書)

Yahooファイナンスでのページはこちらになります。

ちなみに、年次、四半期データの切り替えはリンク先のこちらのボタンで切り替えができます。

画像10

年次損益計算書データ

import sys
sys.path.append('/content/drive/MyDrive/module')
import yfinance as yf

ticker = 'AAPL'
data = yf.Ticker(ticker)

data.financials

画像7

四半期損益計算書データ

import sys
sys.path.append('/content/drive/MyDrive/module')
import yfinance as yf

ticker = 'AAPL'
data = yf.Ticker(ticker)

data.quarterly_financials

画像6

同様にバランスシート(貸借対照表)

data.balance_sheet
data.quarterly_balance_sheet

キャッシュフロー

data.cashflow
data.quarterly_cashflow

で取得できます。

課題

好きな銘柄でバランスシート、キャッシュフローのデータを出力してみましょう。(年次データ、四半期データを出力してみます)

収益データ

四半期ごとの決算発表に使われるデータが四半期収益データになります。こちらのモジュールでも収益データが、年次、四半期を所得することができます。内容はこちらのページの右側の内容になります。

画像11

Revenue : 総収入(Income Statement の Total Revenue)
Earnings : 純利益(Cash FlowのNet Income from Continuing Operations)

コードは以下の通りです。

年次収益データ

import sys
sys.path.append('/content/drive/MyDrive/module')
import yfinance as yf

ticker = 'AAPL'
data = yf.Ticker(ticker)

data.earnings

画像8

四半期収益データ

import sys
sys.path.append('/content/drive/MyDrive/module')
import yfinance as yf

ticker = 'AAPL'
data = yf.Ticker(ticker)

data.quarterly_earnings

画像9

ちなみに、決算で出てくる売上は四半期データのTotal Revenueもしくは、四半期収益のRevenueになります。以下、両方とも同じデータが取れると思います。

total_revenue = data.quarterly_financials.loc['Total Revenue'][0]
total_revenue = data.quarterly_earnings['Revenue'][-1]

またYoY(前年比)はinfoの中のrevenueGrowthにありますので、このようなコードで確認できます。(売り上げは出力フォーマットを変更してます。)

import sys
sys.path.append('/content/drive/MyDrive/module')
import yfinance as yf

ticker = 'AAPL'
data = yf.Ticker(ticker)

#total_revenue = data.quarterly_financials.loc['Total Revenue'][0]
total_revenue = data.quarterly_earnings['Revenue'][-1]
total_revenue = (lambda x: '$%.2f'%(x/float('1E'+str(3*'{:,}'.format(x).count(','))))+['','K','M','B','T']['{:,}'.format(x).count(',')])(total_revenue)
revenue_growth = '{:.2%}'.format(data.info['revenueGrowth'])
print('売上:',total_revenue,' YoY:',revenue_growth)

売上: $81.43B , YoY: 36.40%

その他データ

その他取得できるデータを紹介します。

株主情報

data.major_holders

画像12

data.institutional_holders

画像13

サステイナビリティ(ESG格付け)持続可能性

data.sustainability

画像14

アップグレード・ダウングレード情報

data.recommendations

画像15

イベントカレンダー

今日以降のイベントの情報を出力します。次回の決算情報が見えます。

data.calendar

画像16

ISINコード(国際証券コード)

data.isin

US0378331005

分析資料作成

最後にこれらのデータを使って、ツイッター用の縦長まとめ資料を作ってみます。以下のコードを実行してみてください。(8/28修正:分析結果の出力をアップデートしました)

module_dir = '/content/drive/MyDrive/module/'
output_dir = '/content/drive/MyDrive/output/'
import sys
sys.path.append(module_dir)
import yfinance as yf
from PIL import Image, ImageDraw, ImageFont
import os
import pytz
import IPython
import requests
import datetime as datetime
import mplfinance as mpf
import matplotlib.pyplot as plt
import japanize_matplotlib 
import matplotlib.dates as mdates
import pandas as pd
import numpy as np
from bs4 import BeautifulSoup
import re
import requests
today = datetime.datetime.now(pytz.timezone('Asia/Tokyo'))
if not os.path.isdir(output_dir): os.makedirs(output_dir)
tmp_file = output_dir+'tmp.png'
tmp2_file = output_dir+'tmp2.png'

jap_font = '/content/drive/MyDrive/fonts/meiryo.ttc'
if not os.path.exists(jap_font): jap_font = module_dir+'japanize_matplotlib/fonts/ipaexg.ttf'

# 初期設定(ここを変えてください)###################################
ticker = 'MSFT'
signiture = 'トミィ @toushi_tommy'
#####################################################################

#####################################################################
# フォント
font_jap18=ImageFont.truetype(jap_font, 18)
font_jap40=ImageFont.truetype(jap_font, 40)
font_jap20=ImageFont.truetype(jap_font, 20)
font_jap14=ImageFont.truetype(jap_font, 14)
#####################################################################

data = yf.Ticker(ticker)
im = Image.new('RGB', (960, 1280), (255,255,255))
draw = ImageDraw.Draw(im)

# アイコン ##########################################################
open(tmp2_file, 'wb').write(requests.get(data.info['logo_url']).content) #if  os.path.getsize(tmp_file)>1: im.paste(Image.open(tmp_file).copy(), (30, 30))
if os.path.getsize(tmp2_file)>1:
 image = Image.open(tmp2_file)
 image.load()
 background = Image.new("RGB", image.size, (255, 255, 255))
 if len(image.split())>3: background.paste(image, mask=image.split()[3])
 else: background.paste(image)
 background.save(tmp2_file,quality=95)
 im.paste(Image.open(tmp2_file).copy(), (30, 95-int(image.size[1]/2)))

#####################################################################
# 銘柄説明文取得
japDetail=''
try:
 site = requests.get("https://us.kabutan.jp/stocks/"+ticker)
 sdat = BeautifulSoup(site.text,'html.parser')
 da = sdat.find_all("dd", class_="text-left pt-1 pl-4")
 jname = [re.sub('.*>(.*)<.*', r'\1', s) for s in [s.replace('&amp;', '&') for s in [str(i) for i in da]]]
 da = sdat.find_all("dd", class_="text-left pt-1 pl-4 text-md")
 detail = [re.sub('.*>(.*)<.*', r'\1', s) for s in [s.replace('\n', '').replace('&amp;', '&') for s in [str(i) for i in da]]]
 japDetail=jname[0]+':'+detail[0]
except:
 pass

#####################################################################
str_dat = ticker+'の銘柄情報'
draw.line((310,100, 330+draw.textsize(str_dat, font_jap40)[0],100), fill='yellow', width=30)
draw.text((320, 70), str_dat, 'black', font=font_jap40)
draw.text((680, 20), signiture, 'blue', font=font_jap20)
draw.text((680, 150), "更新日時:"+today.strftime("%Y/%m/%d"),'blue',font=font_jap20)
draw.rectangle([(10, 10), (950, 1270)], outline='black', width=5)
draw.line((20,180, 940,180), fill='black', width=1)

#####################################################################
draw.text((30, 200), '社名:'+data.info['longName']+', セクター:'+data.info['sector']+', 業界:'+data.info['industry'],
         'black', font=font_jap20)
draw.text((50, 235), japDetail[:48], 'black', font=font_jap18)
draw.text((50, 255), japDetail[48:96], 'black', font=font_jap18)
draw.text((50, 275), japDetail[96:144], 'black', font=font_jap18)
draw.text((50, 295), japDetail[144:192], 'black', font=font_jap18)
draw.text((30, 320), 'Webサイト:'+data.info['website'], 'black', font=font_jap20)

# 株価 #####################################################################
draw.line((20,360, 940,360), fill='black', width=1)
draw.text((30, 370), 'アナリスト株価ターゲット:'+str(data.info['numberOfAnalystOpinions'])+'人', 'black', font=font_jap20)
xpos = 100
ypos = 430
length = 650
if(data.info['numberOfAnalystOpinions'] is not None):
 lo_price = data.info['currentPrice'] if data.info['currentPrice']<data.info['targetLowPrice'] else data.info['targetLowPrice']
 hi_price = data.info['currentPrice'] if data.info['currentPrice']>data.info['targetHighPrice'] else data.info['targetHighPrice']
 avg_per = (data.info['targetMeanPrice'] - lo_price)/(hi_price - lo_price)
 lo_per = (data.info['targetLowPrice'] - lo_price)/(hi_price - lo_price)
 hi_per = (data.info['targetHighPrice'] - lo_price)/(hi_price - lo_price)
 cur_per = (data.info['currentPrice'] - lo_price)/(hi_price - lo_price)
 draw.line((xpos, ypos, xpos+length, ypos), fill='black', width=2)
 draw.ellipse((xpos+length*lo_per-5, ypos-5, xpos+length*lo_per+5, ypos+5), fill=(0, 0, 0))
 draw.ellipse((xpos+length*hi_per-5, ypos-5, xpos+length*hi_per+5, ypos+5), fill=(0, 0, 0))
 draw.ellipse((xpos+length*avg_per-5, ypos-5, xpos+length*avg_per+5, ypos+5), fill='red')
 draw.ellipse((xpos+length*cur_per-5, ypos-5, xpos+length*cur_per+5, ypos+5), fill='blue')
 draw.text((xpos+length*lo_per, ypos+10), '下限:'+str(data.info['targetLowPrice']), 'black', font=font_jap20)
 draw.text((xpos+length*hi_per, ypos+10), '上限:'+str(data.info['targetHighPrice']), 'black', font=font_jap20)
 draw.text((xpos+length*avg_per, ypos-35), '平均:'+str(data.info['targetMeanPrice']), 'red', font=font_jap20)
 draw.text((xpos+length*cur_per, ypos+35), '現在値:'+str(data.info['currentPrice']), 'blue', font=font_jap20)

# 決算情報 #####################################################################
draw.line((20,500, 940,500), fill='black', width=1)
conv_str = lambda x: '$%.2f'%(x/float('1E'+str(3*'{:,}'.format(x).count(','))))+['','K','M','B','T']['{:,}'.format(x).count(',')]
total_revenue = conv_str(data.info['totalRevenue']) if (data.info['totalRevenue'] is not None) else str(data.info['totalRevenue'])
quart_revenue = conv_str(data.quarterly_earnings['Revenue'][-1])
market_cap = conv_str(data.info['marketCap'])
per = '%.2f'%(data.info['trailingPE']) if 'trailingPE' in data.info else 'N/A'
psr = '%.2f'%(data.info['marketCap']/4/data.quarterly_earnings['Revenue'][-1]) if(data.quarterly_earnings['Revenue'][-1]>0) else 'N/A'
yoy = '{:.2%}'.format(data.info['revenueGrowth']) if (data.info['revenueGrowth'] is not None) else str(data.info['revenueGrowth'])

draw.text((30, 510), '売上(直近1年):'+total_revenue+', 売上('+data.quarterly_earnings.index[-1]+
'):'+quart_revenue+', YoY:'+yoy, 'black', font=font_jap20)
draw.text((30, 550), '時価総額:'+market_cap+', PER:'+per+', PSR:'+psr, 'black', font=font_jap20)
draw.text((30, 590), '発行済株式数:'+"{:,}".format(data.info['sharesOutstanding']), 'black', font=font_jap20)
draw.text((30, 630), '52週高値:'+'$%.2f'%(data.info['fiftyTwoWeekHigh'])+', 52週安値:'+'$%.2f'%(data.info['fiftyTwoWeekLow']),
         'black', font=font_jap20)

# チャート ##########################################################
chart = data.history(period="3mo")
chart['Percent'] = chart['Close'].pct_change()
chart['MACD'] = chart['Close'].ewm(span=12, adjust=False).mean() - chart['Close'].ewm(span=26, adjust=False).mean()
chart['Signal'] = chart['MACD'].rolling(window=9).mean()
chart['Hist'] = chart['MACD'] - chart['Signal']
df_diff = chart['Close'].diff()
df_up, df_down = df_diff.copy(), df_diff.copy()
df_up[df_up < 0] = 0
df_down[df_down > 0] = 0
df_down = df_down * -1
sim14_up = df_up.rolling(window=14).mean()
sim14_down = df_down.rolling(window=14).mean()
chart['RSI'] = sim14_up / (sim14_up + sim14_down) * 100
chart['RSI_hl'] = 70
chart['RSI_ll'] = 30
add_plot=[
mpf.make_addplot(chart['MACD'], color='m', panel=1, secondary_y=False),
mpf.make_addplot(chart['Signal'], color='c', panel=1, secondary_y=False),
mpf.make_addplot(chart['Hist'], type='bar', color='g', panel=1, secondary_y=True),
mpf.make_addplot(chart['RSI'], panel=2),
mpf.make_addplot(chart['RSI_hl'], color='b', panel=2, secondary_y=False,linestyle='-.', width=1),
mpf.make_addplot(chart['RSI_ll'], color='r', panel=2, secondary_y=False,linestyle='-.', width=1)
]
mpf.plot(chart,volume=True, figscale = 0.7, figratio=(4,2),volume_panel=3,type='candle',mav=(5,25),addplot=add_plot
,datetime_format='%Y/%m/%d',savefig=tmp_file)

im.paste(Image.open(tmp_file).copy(), (140, 850))
#####################################################################
# 一週間の値動き
df = yf.download(ticker, interval = "1m", period = "5d")
str_tmp=""
day_split=[]
for i in range(len(df)):
 str_data = df.index[i].strftime("%Y-%m-%d")
 if str_data != str_tmp: day_split.append(df.index[i])
 str_tmp= str_data

day_split.append(df.index[-1])
mpf.plot(df, figscale = 0.31, figratio=(4.5,1),
     vlines=dict(vlines=day_split,colors='r',linestyle='-.',linewidths=1),
     type='line',datetime_format='', xrotation=0,savefig=tmp_file)

im.paste(Image.open(tmp_file).copy(), (140, 680))
wk = ['月','火','水','木','金','土','日']
draw.text((500, 670), '過去1週間の値動き', 'black', font=font_jap18)
for i in range(0,5):
 draw.text((325+i*105, 830), day_split[i].strftime('%m/%d')+'('+wk[day_split[i].weekday()]+')', 'black', font=font_jap14)
 draw.text((325+i*105, 845),'['+'{:.2%}'.format(chart['Percent'][-5+i])+']' , ('blue' if chart['Percent'][-5+i] > 0 else 'red'), font=font_jap14)

#####################################################################
draw.text((500, 860), '過去3か月チャート', 'black', font=font_jap18)
draw.line((20,660, 940,660), fill='black', width=1)
draw.text((30, 670), 'チャート:'+chart.index[-1].strftime('%Y/%m/%d'), 'black', font=font_jap20)
draw.text((40, 720), '株価:'+'$%.2f'%(chart['Close'][-1]), 'black', font=font_jap20)
draw.text((40, 750), '始値:'+'$%.2f'%(chart['Open'][-1]), 'black', font=font_jap20)
draw.text((40, 780), '高値:'+'$%.2f'%(chart['High'][-1]), 'black', font=font_jap20)
draw.text((40, 810), '安値:'+'$%.2f'%(chart['Low'][-1]), 'black', font=font_jap20)
draw.text((40, 870), '前日比:'+'{:.2%}'.format(chart['Percent'][-1]), 'black', font=font_jap20)
draw.text((40, 900), 'RSI:'+'{:.2%}'.format(chart['RSI'][-1]/100), 'black', font=font_jap20)
draw.text((40, 930), '52週高値下落:', 'black', font=font_jap20)
draw.text((50, 960), '{:.2%}'.format((chart['Close'][-1]-data.info['fiftyTwoWeekHigh'])/data.info['fiftyTwoWeekHigh']), 'black', font=font_jap20)
draw.text((40, 1070), '出来高:', 'black', font=font_jap20)
draw.text((50, 1100), "{:,}".format(chart['Volume'][-1]), 'black', font=font_jap20)
draw.text((40, 1140), '出来高上昇率:', 'black', font=font_jap20)
draw.text((50, 1170), '%.2f'%(chart['Volume'][-1]/data.info['averageVolume10days'])+'倍', 'black', font=font_jap20)
#####################################################################

outfile = output_dir+ticker+'_'+today.strftime("%Y%m%d_%H%M%S")+'.png'
im.save(outfile)
IPython.display.Image(outfile)

出力は以下のような感じになります。

画像17

好きな情報を出して、自分なりの縦長まとめ情報を作ってください。

課題

銘柄を変えて実行してみてください。また、可能であれば、出力情報を増やしたり変更してみてください。

本日は以上です。

ここから先は

0字

¥ 150

サポートいただけますと、うれしいです。より良い記事を書く励みになります!