Last active
December 10, 2015 21:58
-
-
Save Jun-Dai/4498519 to your computer and use it in GitHub Desktop.
No idea how accurate this truly is as I have no training in accounting whatsoever (other than a 3rd grade class on balancing my checkbook). Anyways, I wanted to know what my overall annualised yield was for my RateSetter account where I've made some deposits on certain dates and know my current balance. I would welcome improvements or correction…
This file contains 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
# Simple script to determine what your rough overall annual yield is for something that has gained value with multiple deposits and withdrawals. | |
# | |
# Expects an input file with each line in one of the following formats: | |
# | |
# 1. amount added: | |
# yyyy-mm-dd £###.## | |
# | |
# 2. amount subtracted: | |
# yyyy-mm-dd (£###.##) | |
# | |
# 3. current value (there should only be one line like this): | |
# current value as of yyyy-mm-dd: £###.## | |
# | |
# Lines that are blank or start with # will be ignored. Lines that are none of the above will be ignored in the calculations and printed out to STDOUT. | |
# TODO: we really want to compare this to a savings account interest rate. | |
# 1. Determine the total yield | |
# 2. Determine an imaginary monthly interest payment, wherein each penny day contributes equally to that payment. This should, with compound calculation, arrive at the total yield included in the "current value". | |
# 3. Calculate the annual interest rate on the basis of that imaginary monthly payment. | |
# Note: if done correctly, this should allow for withrawals wherein the balance is > 0, but the 'paid-in principal' is < 0. | |
require 'date' | |
filename = ARGV.length > 0 ? ARGV[0] : "sums.txt" | |
class Transaction | |
attr_reader :date, :amount_in_pennies, :type | |
def initialize(date_str, amount_str, type) | |
@date = Date.parse(date_str) | |
@amount_in_pennies = amount_str.include?('.') ? amount_str.tr('.', '').to_i : "#{amount_str}00".to_i # 12.23 => 1223, 12 => 1200 | |
@type = type | |
end | |
def apply(balance_in_pennies) | |
if type == :withdrawal | |
balance_in_pennies - @amount_in_pennies | |
else | |
balance_in_pennies + @amount_in_pennies | |
end | |
end | |
def date_diff(other_transaction) | |
@date - other_transaction.date | |
end | |
end | |
transactions = [] | |
current_value_in_pennies = nil | |
current_value_as_of_date = nil | |
File.readlines(filename).each do |line| | |
if line =~ /^(\d{4}-\d{2}-\d{2}) [$\u00A3\u20AC\u00A5](\d+(\.\d\d)?)\s*$/ | |
transactions << Transaction.new($1, $2, :deposit) | |
elsif line =~ /^(\d{4}-\d{2}-\d{2}) \([$\u00A3\u20AC\u00A5](\d+(\.\d\d)?)\)\s*$/ # note the parens | |
transactions << Transaction.new($1, $2, :withdrawal) | |
elsif line =~ /^current value as of (\d{4}-\d{2}-\d{2}):\s*[$\u00A3\u20AC\u00A5](\d+(\.\d\d)?)\s*$/ | |
raise "You cannot define current value more than once in input file! It's defined at least twice: #{current_value_in_pennies} and #{$1}" unless current_value_in_pennies.nil? | |
current_value_in_pennies = $2.include?('.') ? $2.tr('.', '').to_i : "#{$2}00".to_i | |
current_value_as_of_date = Date.parse($1) | |
elsif line =~ /^\s*(#.*)?$/ | |
# ignore this line, it's empty or a comment | |
else | |
puts "Ignoring this line as I cannot parse it: #{line}" | |
end | |
end | |
sorted_transactions = transactions.sort_by &:date | |
puts "#{sorted_transactions.length} sorted transactions" | |
class PrincipalBalance | |
def initialize(amount_in_pennies, days_duration) | |
@amount_in_pennies, @days_duration = amount_in_pennies, days_duration | |
end | |
def penny_days | |
@amount_in_pennies * @days_duration | |
end | |
end | |
principal_balances = [] | |
last_balance_in_pennies = 0 | |
last_transaction = nil | |
sorted_transactions.each do |trans| | |
if last_transaction.nil? | |
last_transaction = trans | |
last_balance_in_pennies = trans.apply(0) | |
else | |
days = trans.date_diff(last_transaction) | |
principal_balances << PrincipalBalance.new(last_balance_in_pennies, days) | |
new_balance = trans.apply(last_balance_in_pennies) | |
last_transaction = trans | |
last_balance_in_pennies = new_balance | |
end | |
end | |
# final balance period | |
principal_balances << PrincipalBalance.new(last_balance_in_pennies, current_value_as_of_date - last_transaction.date) | |
profit = (current_value_in_pennies - last_balance_in_pennies).to_f | |
total_penny_days = principal_balances.inject(0) {|sum, nxt| sum + nxt.penny_days} | |
total_penny_years = total_penny_days.to_f / 365 | |
annualized_yield = (profit) / total_penny_years | |
puts "Annualized yield was: #{annualized_yield * 100}% APY" |
This file contains 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
# Example sums file to test-run this scriptlet: | |
2012-05-27 £1000 | |
2012-11-12 £1000 | |
current value as of 2013-01-10: £2030.83 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment