Skip to content

Instantly share code, notes, and snippets.

@Jun-Dai
Last active December 10, 2015 21:58
Show Gist options
  • Save Jun-Dai/4498519 to your computer and use it in GitHub Desktop.
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…
# 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"
# 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