yfinance API를 이용하여 만든 자산 관리 프로그램
해외주식, 국내주식, 연금저축펀드, 비트코인, 현금, 부채, 투자원금 목록은 수동으로 입력해주어야 한다.
Google Collaboratory, jupyter notebook에서 작성하였으며, 구글 드라이브와 연동하여 업데이트된 자산현황을 읽어온다.
서로 다른 계좌의 자산을 통합하여 현재 어떤 비중으로 투자하고 있는지 볼 수 있고, 1월 1일부터 자산의 변화를 볼 수 있다.
매일의 주가, 비트코인 가격 및 환율을 yfinance API를 통해 입력받아, 종가 기준으로 자산평가액을 계산한다.
현재 연금 저축 펀드를 통해 투자하고 있는 ETF 두 종목은 yfinance에서 가장 최근 거래일의 종가만을 제공하고 있기 때문에, 프로그램을 실행할 때마다 각 종목의 주가를 저장 및 업데이트 하도록 구현했다.
그래프에 최저/최고/현재 자산과 최저/최고/현재 수익을 표시했다.
과거의 거래 기록을 완벽하게 복원하지 못하여 1월의 기록에는 상당한 오류가 있는데, 시간이 널널할 때 수정할 계획.
INVESTMENT_STATUS.PNG
SUMMARY.PNG
Asset.png
#code
!pip install yfinance
from pandas_datareader import data as pdr
import yfinance as yf
import datetime
import matplotlib.pyplot as plt
from PIL import Image, ImageDraw, ImageFont
import os
from google.colab import drive
drive.mount('/content/drive')
os.system('cp /content/drive/MyDrive/Stock_Record/Stock_Own_Record.txt /content/drive/MyDrive/Stock_Record/Stock_Own_Record_backup.txt')
os.system('cp /content/drive/MyDrive/Stock_Record/Stock_Price_Record.txt /content/drive/MyDrive/Stock_Record/Stock_Price_Record_backup.txt')
now = datetime.datetime.now()
start_date = datetime.date(2021, 1, 1)
now_date = now.strftime('%Y-%m-%d')
f = open('/content/drive/MyDrive/Stock_Record/Money_Debt_Record.txt', 'r')
money = {}
housing = {}
debt = {}
global_deposit = {}
korea_deposit = {}
pension_deposit = {}
principal = {}
i = -1
while True:
line = f.readline()
if not line:
break
if line[0] == '#':
i += 1
continue
if i == 0:
money[line.split()[0]] = float(line.split()[1])
if i == 1:
housing[line.split()[0]] = float(line.split()[1])
if i == 2:
debt[line.split()[0]] = float(line.split()[1])
if i == 3:
global_deposit[line.split()[0]] = float(line.split()[1])
if i == 4:
korea_deposit[line.split()[0]] = float(line.split()[1])
if i == 5:
pension_deposit[line.split()[0]] = float(line.split()[1])
if i == 6:
principal[line.split()[0]] = float(line.split()[1])
f.close()
f = open('/content/drive/MyDrive/Stock_Record/Stock_Own_Record.txt', 'r')
ticker = []
stock_type = []
stock_own = []
i = -1
while True:
line = f.readline()
if not line:
break
if line[0] == '#':
ticker.append(line.split('#')[1])
stock_type.append(int(line.split('#')[2].split('\n')[0]))
i += 1
stock_own.append({})
continue
stock_own[i][line.split()[0]] = float(line.split()[1])
f.close()
f = open('/content/drive/MyDrive/Stock_Record/Stock_Price_Record.txt', 'r')
stock_price = []
for i in range(len(ticker)):
stock_price.append({})
i = -1
while True:
line = f.readline()
if not line:
break
if line[0] == '#':
i += 1
continue
stock_price[i][line.split()[0]] = float(line.split()[1])
f.close()
yf.pdr_override()
new_stock_price = []
for i in ticker:
new_stock_price.append(pdr.get_data_yahoo(i, start=start_date, end=now_date))
for i in range(len(ticker)):
for j in new_stock_price[i].index:
stock_price[i][j.strftime('%Y-%m-%d')] = float('{:.2f}'.format(new_stock_price[i].at[j,'Close']))
stock_price[16]['2020-12-31'] = 25.11
f = open('/content/drive/MyDrive/Stock_Record/Stock_Price_Record.txt', 'w')
for i in range(len(ticker)):
f.write('#'+ticker[i]+'\n')
for j in stock_price[i].keys():
f.write(str(j) + ' ')
f.write(str('{:.2f}'.format(stock_price[i][j])))
f.write('\n')
f.close()
dollar_price = yf.download(['USDKRW=X'],start=start_date, end=now_date)
dollar_price_record = {}
for i in dollar_price.index:
dollar_price_record[i.strftime('%Y-%m-%d')] = float('{:.2f}'.format(dollar_price.at[i,'Close']))
print()
dollarperwon = dollar_price.at[dollar_price.index[-1], 'Close']
amount = []
amount_by_type = [0, 0, 0, 0]
total_amount = 0
for i in range(len(ticker)):
temp = stock_price[i][list(stock_price[i].keys())[-1]] * stock_own[i][list(stock_own[i].keys())[-1]]
if stock_type[i] == 1 or stock_type[i] == 2:
temp /= dollarperwon
amount.append(temp)
amount_by_type[stock_type[i]] += temp
total_amount += temp
amount_by_type[0] += global_deposit[list(global_deposit.keys())[-1]]
amount_by_type[1] += korea_deposit[list(korea_deposit.keys())[-1]]/dollarperwon
amount_by_type[2] += pension_deposit[list(pension_deposit.keys())[-1]]/dollarperwon
total_amount_plus_deposit = total_amount + global_deposit[list(global_deposit.keys())[-1]] + korea_deposit[list(korea_deposit.keys())[-1]]/dollarperwon + pension_deposit[list(pension_deposit.keys())[-1]]/dollarperwon
sorted_amount_index = list(reversed(sorted(range(len(amount)), key=lambda k: amount[k])))
print('**INVESTMENT STATUS**')
for i in sorted_amount_index:
if stock_own[i][list(stock_own[i].keys())[-1]] == 0:
break
if stock_type[i] == 0:
print('{}{:10.1f}%{:10.1f}{:15.0f} {:10.2f}'.format('{0: <10}'.format(ticker[i]), amount[i]/total_amount*100, amount[i], stock_own[i][list(stock_own[i].keys())[-1]], stock_price[i][list(stock_price[i].keys())[-1]]))
if stock_type[i] == 1 or stock_type == 2:
print('{}{:10.1f}%{:10.1f}{:15.0f} {:10.0f}'.format('{0: <10}'.format(ticker[i]), amount[i]/total_amount*100, amount[i], stock_own[i][list(stock_own[i].keys())[-1]], stock_price[i][list(stock_price[i].keys())[-1]]))
if stock_type[i] == 3:
print('{}{:10.1f}%{:10.1f}{:15.8f} {:10.2f}'.format('{0: <10}'.format(ticker[i]), amount[i]/total_amount*100, amount[i], stock_own[i][list(stock_own[i].keys())[-1]], stock_price[i][list(stock_price[i].keys())[-1]]))
print()
print('**SUMMARY**')
print('GLOBAL STOCK : {:.1f}%\t${:.1f}\t₩{}'.format(amount_by_type[0]/total_amount_plus_deposit*100, amount_by_type[0], '{:,}'.format(int(amount_by_type[0]*dollarperwon))))
print('KOREA STOCK : {:.1f}% \t${:.1f} \t₩{}'.format(amount_by_type[1]/total_amount_plus_deposit*100, amount_by_type[1], '{:,}'.format(int(amount_by_type[1]*dollarperwon))))
print('PENSION S. FUND : {:.1f}% \t${:.1f} \t₩{}'.format(amount_by_type[2]/total_amount_plus_deposit*100, amount_by_type[2], '{:,}'.format(int(amount_by_type[2]*dollarperwon))))
print('CRYPTOCURRENCY : {:.1f}% \t${:.1f} \t₩{}'.format(amount_by_type[3]/total_amount_plus_deposit*100, amount_by_type[3], '{:,}'.format(int(amount_by_type[3]*dollarperwon))))
print('TOTAL ASSET : 100% \t${:.1f}\t₩{}'.format(total_amount_plus_deposit, '{:,}'.format(int(total_amount_plus_deposit*dollarperwon))))
cur = start_date
now = datetime.date(now.year, now.month, now.day)
cur_stock_price = []
cur_stock_own = []
for i in range(len(ticker)):
cur_stock_price.append(stock_price[i][list(stock_price[i].keys())[0]])
cur_stock_own.append(stock_own[i][list(stock_own[i].keys())[0]])
cur_money = money[list(money.keys())[0]]
cur_housing = housing[list(housing.keys())[0]]
cur_debt = debt[list(debt.keys())[0]]
cur_global_deposit = global_deposit[list(global_deposit.keys())[0]]
cur_korea_deposit = korea_deposit[list(korea_deposit.keys())[0]]
cur_pension_deposit = pension_deposit[list(pension_deposit.keys())[0]]
cur_dollarperwon = dollar_price_record[list(dollar_price_record.keys())[0]]
cur_principal = principal[list(principal.keys())[0]]
asset_axis = []
invest_axis = []
earning_axis = []
global_axis = []
korea_axis = []
pension_axis = []
btc_axis = []
date_axis = []
day_count = 0
for_xaxis = []
datetime_axis = []
earning_rate_axis = []
principal_axis = []
while cur <= now:
for i in range(len(ticker)):
if cur.strftime('%Y-%m-%d') in list(stock_price[i].keys()):
cur_stock_price[i] = stock_price[i][cur.strftime('%Y-%m-%d')]
if cur.strftime('%Y-%m-%d') in list(stock_own[i].keys()):
cur_stock_own[i] = stock_own[i][cur.strftime('%Y-%m-%d')]
if cur.strftime('%Y-%m-%d') in list(money.keys()):
cur_money = money[cur.strftime('%Y-%m-%d')]
if cur.strftime('%Y-%m-%d') in list(housing.keys()):
cur_housing = housing[cur.strftime('%Y-%m-%d')]
if cur.strftime('%Y-%m-%d') in list(debt.keys()):
cur_debt = debt[cur.strftime('%Y-%m-%d')]
if cur.strftime('%Y-%m-%d') in list(global_deposit.keys()):
cur_global_deposit = global_deposit[cur.strftime('%Y-%m-%d')]
if cur.strftime('%Y-%m-%d') in list(korea_deposit.keys()):
cur_korea_deposit = korea_deposit[cur.strftime('%Y-%m-%d')]
if cur.strftime('%Y-%m-%d') in list(pension_deposit.keys()):
cur_pension_deposit = pension_deposit[cur.strftime('%Y-%m-%d')]
if cur.strftime('%Y-%m-%d') in list(dollar_price_record.keys()):
cur_dollarperwon = dollar_price_record[cur.strftime('%Y-%m-%d')]
if cur.strftime('%Y-%m-%d') in list(principal.keys()):
cur_principal = principal[cur.strftime('%Y-%m-%d')]
temp = 0
temp_invest = 0
temp_asset = 0
temp_global = 0
temp_korea = 0
temp_pension = 0
temp_btc = 0
temp_earning = 0
for i in range(len(ticker)):
if stock_type[i] == 0:
temp_invest += cur_stock_price[i] * cur_stock_own[i] * cur_dollarperwon
temp_global += cur_stock_price[i] * cur_stock_own[i] * cur_dollarperwon
elif stock_type[i] == 1:
temp_invest += cur_stock_price[i] * cur_stock_own[i]
temp_korea += cur_stock_price[i] * cur_stock_own[i]
elif stock_type[i] == 2:
temp_invest += cur_stock_price[i] * cur_stock_own[i]
temp_pension += cur_stock_price[i] * cur_stock_own[i]
elif stock_type[i] == 3:
temp_invest += cur_stock_price[i] * cur_stock_own[i] * cur_dollarperwon
temp_btc += cur_stock_price[i] * cur_stock_own[i] * cur_dollarperwon
temp_invest += cur_global_deposit*cur_dollarperwon + cur_korea_deposit + cur_pension_deposit
temp_asset = temp_invest + cur_money + cur_housing - cur_debt
temp_earning = temp_invest - cur_principal
earning_axis.append(temp_earning)
asset_axis.append(temp_asset)
invest_axis.append(temp_invest)
global_axis.append(temp_global)
korea_axis.append(temp_korea)
pension_axis.append(temp_pension)
btc_axis.append(temp_btc)
date_axis.append(day_count)
datetime_axis.append(cur)
principal_axis.append(cur_principal)
earning_rate_axis.append(temp_earning/cur_principal*100)
if day_count%28 == 0:
for_xaxis.append(day_count)
day_count += 1
cur += datetime.timedelta(days=1)
cur = start_date
label_for_xaxis = []
while cur <= now:
label_for_xaxis.append(cur.strftime('%Y-%m-%d'))
cur += datetime.timedelta(weeks=4)
plt.figure(figsize=(20,10))
plt.yticks([0, 10000000, 20000000, 30000000, 40000000, 50000000, 60000000, 70000000, 80000000, 90000000, 100000000, 110000000, 120000000], labels=['0', '10,000,000', '20,000,000', '30,000,000', '40,000,000', '50,000,000', '60,000,000', '70,000,000', '80,000,000', '90,000,000', '100,000,000', '110,000,000', '120,000,000'])
plt.xticks(for_xaxis, labels=label_for_xaxis, rotation=60)
plt.plot(date_axis, asset_axis, label='NET ASSET', color='black')
plt.plot(date_axis , earning_axis, label='EARNING', color='red')
plt.plot(date_axis, invest_axis, label='ASSET', linestyle = 'dashed')
plt.plot(date_axis, global_axis, label='GLOBAL', linestyle = 'dashed')
plt.plot(date_axis, korea_axis, label='KOREA', linestyle = 'dashed')
plt.plot(date_axis, pension_axis, label='PENSION', linestyle = 'dashed')
plt.plot(date_axis, btc_axis, label='BTC', linestyle = 'dashed')
max_asset = max(asset_axis)
min_asset = min(asset_axis)
max_asset_index = asset_axis.index(max_asset)
min_asset_index = asset_axis.index(min_asset)
max_earning = max(earning_axis)
min_earning = min(earning_axis)
max_earning_index = earning_axis.index(max_earning)
min_earning_index = earning_axis.index(min_earning)
plt.annotate('{}\n{} {:,}'.format(datetime_axis[max_asset_index].strftime('%Y-%m-%d'), u"\u20A9", int(asset_axis[max_asset_index])), xy=(date_axis[max_asset_index], asset_axis[max_asset_index]), xytext=(-200,70), textcoords='offset points', fontsize=20, ha='center', arrowprops=dict(color='black',arrowstyle="-|>"))
plt.annotate('{}\n{} {:,}'.format(datetime_axis[min_asset_index].strftime('%Y-%m-%d'), u"\u20A9", int(asset_axis[min_asset_index])), xy=(date_axis[min_asset_index], asset_axis[min_asset_index]), xytext=(100,120), textcoords='offset points', fontsize=20, ha='center', arrowprops=dict(color='black',arrowstyle="-|>"))
plt.annotate('{}\n{} {:,}'.format(datetime_axis[-1].strftime('%Y-%m-%d'), u"\u20A9", int(asset_axis[-1])), xy=(date_axis[-1], asset_axis[-1]), xytext=(0,70), textcoords='offset points', fontsize=20, ha='center', arrowprops=dict(color='black',arrowstyle="-|>"))
plt.annotate('{}\n{} {:,}'.format(datetime_axis[max_earning_index].strftime('%Y-%m-%d'), u"\u20A9", int(earning_axis[max_earning_index])), xy=(date_axis[max_earning_index], earning_axis[max_earning_index]), xytext=(0,70), textcoords='offset points', fontsize=20, ha='center', arrowprops=dict(color='red',arrowstyle="-|>"))
plt.annotate('{}\n-{} {:,}'.format(datetime_axis[min_earning_index].strftime('%Y-%m-%d'), u"\u20A9", abs(int(earning_axis[min_earning_index]))), xy=(date_axis[min_earning_index], earning_axis[min_earning_index]), xytext=(0,70), textcoords='offset points', fontsize=20, ha='center', arrowprops=dict(color='red',arrowstyle="-|>"))
plt.annotate('{}\n{} {:,}'.format(datetime_axis[-1].strftime('%Y-%m-%d'), u"\u20A9", int(earning_axis[-1])), xy=(date_axis[-1], earning_axis[-1]), xytext=(0,70), textcoords='offset points', fontsize=20, ha='center', arrowprops=dict(color='red',arrowstyle="-|>"))
print()
print('NET ASSET : ₩{:,}'.format(int(asset_axis[-1])))
print('MAX NET ASSET : ₩{:,}'.format(int(max_asset)))
print()
print('EARNING : ₩{:,}'.format(int(earning_axis[-1])))
print('MAX EARNING : ₩{:,}'.format(int(max_earning)))
print()
print('RATE OF RETURN : {:.1f}%'.format(earning_rate_axis[-1]))
plt.legend()
plt.grid(True, axis='y')
plt.savefig('/content/drive/MyDrive/Stock_Record/Asset.png')
old_im = Image.open('/content/drive/MyDrive/Stock_Record/Asset.png')
old_size = old_im.size
new_size = (1440, 1080)
new_im = Image.new("RGB", new_size) ## luckily, this is already black!
new_im.paste(old_im, ((int)((new_size[0]-old_size[0])/2), (int)((new_size[1]-old_size[1])/2)))
new_im.show()
new_im.save('/content/drive/MyDrive/Stock_Record/Asset.png')
bg_color = 'rgb(105,105,105)'
font = ImageFont.truetype('/content/drive/MyDrive/Stock_Record/Monoid-Retina.ttf', 15)
font_color = 'rgb(255,255,255)'
w = 932
h = 699
image = Image.new('RGB', (w,h), color=bg_color)
draw = ImageDraw.Draw(image)
x_text = 165
y_text = 50
lines = []
lines.append('**INVESTMENT STATUS**')
for i in sorted_amount_index:
if stock_own[i][list(stock_own[i].keys())[-1]] == 0:
break
if stock_type[i] == 0:
lines.append('{}{:10.1f}%{:10.1f}{:15.0f} {:10.2f}'.format('{0: <10}'.format(ticker[i]), amount[i]/total_amount*100, amount[i], stock_own[i][list(stock_own[i].keys())[-1]], stock_price[i][list(stock_price[i].keys())[-1]]))
if stock_type[i] == 1 or stock_type == 2:
lines.append('{}{:10.1f}%{:10.1f}{:15.0f} {:10.0f}'.format('{0: <10}'.format(ticker[i]), amount[i]/total_amount*100, amount[i], stock_own[i][list(stock_own[i].keys())[-1]], stock_price[i][list(stock_price[i].keys())[-1]]))
if stock_type[i] == 3:
lines.append('{}{:10.1f}%{:10.1f}{:15.8f} {:10.2f}'.format('{0: <10}'.format(ticker[i]), amount[i]/total_amount*100, amount[i], stock_own[i][list(stock_own[i].keys())[-1]], stock_price[i][list(stock_price[i].keys())[-1]]))
for line in lines:
width, height = font.getsize(line)
draw.text((x_text,y_text),line,font=font,fill=font_color)
y_text += height+10
image.save('/content/drive/MyDrive/Stock_Record/Investment_Status.png')
image
w = 600
h = 450
image1 = Image.new('RGB', (w,h), color=bg_color)
draw1 = ImageDraw.Draw(image1)
x_text = 50
y_text = 50
lines = []
lines.append('**SUMMARY**')
lines.append('GLOBAL STOCK : {:.1f}% ${:.1f} {}'.format(amount_by_type[0]/total_amount_plus_deposit*100, amount_by_type[0], '{:,}'.format(int(amount_by_type[0]*dollarperwon))))
lines.append('KOREA STOCK : {:.1f}% ${:.1f} {}'.format(amount_by_type[1]/total_amount_plus_deposit*100, amount_by_type[1], '{:,}'.format(int(amount_by_type[1]*dollarperwon))))
lines.append('PENSION S. FUND : {:.1f}% ${:.1f} {}'.format(amount_by_type[2]/total_amount_plus_deposit*100, amount_by_type[2], '{:,}'.format(int(amount_by_type[2]*dollarperwon))))
lines.append('CRYPTOCURRENCY : {:.1f}% ${:.1f} {}'.format(amount_by_type[3]/total_amount_plus_deposit*100, amount_by_type[3], '{:,}'.format(int(amount_by_type[3]*dollarperwon))))
lines.append('TOTAL ASSET : 100% ${:.1f} {}'.format(total_amount_plus_deposit, '{:,}'.format(int(total_amount_plus_deposit*dollarperwon))))
lines.append('')
lines.append('NET ASSET : {:,}'.format(int(asset_axis[-1])))
lines.append('MAX NET ASSET : {:,}'.format(int(max_asset)))
lines.append('')
lines.append('EARNING : {:,}'.format(int(earning_axis[-1])))
lines.append('MAX EARNING : {:,}'.format(int(max_earning)))
lines.append('')
lines.append('RATE OF RETURN : {:.1f}%'.format(earning_rate_axis[-1]))
for line in lines:
width, height = font.getsize(line)
draw1.text((x_text,y_text),line,font=font,fill=font_color)
y_text += height+10
image1.save('/content/drive/MyDrive/Stock_Record/Summary.png')
image1