Created
August 28, 2019 10:28
-
-
Save idler921/52815eebf37a6f73c8e781e4027816a0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
""" | |
Created on Wed Aug 28 11:30:01 2019 | |
@author: idler | |
""" | |
#import csv | |
import pandas | |
from dateutil.parser import parse | |
from dateutil.relativedelta import relativedelta | |
import datetime | |
import math | |
from scipy import optimize | |
def xnpv(rate,cashflows): | |
chron_order = sorted(cashflows, key = lambda x: x[0]) | |
t0 = chron_order[0][0] #t0 is the date of the first cash flow | |
return sum([cf/(1+rate)**((t-t0).days/365.0) for (t,cf) in chron_order]) | |
def xirr(cashflows,guess=0.1): | |
return optimize.newton(lambda r: xnpv(r,cashflows),guess) | |
def next_month(date): | |
try: | |
nextmonthdate = date.replace(month=date.month+1) | |
except ValueError: | |
if date.month == 12: | |
nextmonthdate = date.replace(year=date.year+1, month=1) | |
else: | |
nextmonthdate = date.replace(month=date.month+2,day=1) - datetime.timedelta(days=1) | |
# next month is too short to have "same date" | |
# pick your own heuristic, or re-raise the exception: | |
# raise | |
return nextmonthdate | |
code = "1888" | |
price_csv = f'C:/Users/idler/Downloads/{code}.HK.csv' | |
dividend_csv = f'C:/Users/idler/Downloads/{code}.HK (1).csv' | |
price_pd = pandas.read_csv(price_csv) | |
div = pandas.read_csv(dividend_csv) | |
start_date = parse("2011-09-01") | |
mi_amt = 5000 | |
mi_fee = 50 | |
next_buy = start_date | |
total_amt = 0 | |
total_paid = 0 | |
total_div = 0 | |
last_close = 0 | |
last_date = "" | |
cashflows = [] | |
for i, row in price_pd.iterrows(): | |
row_date = parse(row["Date"]) | |
last_close = row["Close"] | |
last_date = row_date | |
df_div = div[div["Date"] == row["Date"]]["Dividends"] | |
if len(df_div) == 0: | |
div_date = 0 | |
else: | |
div_date = df_div.sum() | |
if total_amt > 0: | |
div_given = div_date * total_amt | |
total_div += div_given | |
cashflows.append((row_date, -div_given)) | |
print(i, row_date, "DIV ", div_given) | |
if row_date > next_buy and not math.isnan(row["Open"]): | |
price = row["Open"] | |
buy_amt = math.floor((5000-50)/price) | |
total_amt += buy_amt | |
paid = buy_amt * price | |
total_paid += paid | |
cashflows.append((row_date, paid)) | |
print(i, next_buy, f"Buy {buy_amt} @ " + str(row["Open"]), f"= {round(paid,2)} Total {total_amt}" ) | |
next_buy = next_month(next_buy) | |
total_value = total_amt * last_close | |
cashflows.append((last_date,-total_value)) | |
print("\n%s\nStart date %s \nLast date %s" % (code,start_date,last_date)) | |
print("Total value %.2f \nTotal paid %.2f \nTotal Div received %.2f" % (total_value, total_paid, total_div)) | |
print("XIRR %.2f %%" %(round(xirr(cashflows)*100,2))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment