One ambition of mine is to retire relatively early (around 55) and in the period prior to that to take jobs without consideration for their financial compensation. To achieve that, I pay attention to how my net worth, savings rate, and investments track with my goals using the coast FIRE calculator.
I have been concerned for some while that the coast FIRE calculator is insufficient for doing financial planning so I have paid attention to alternatives like ProjectionLab. I'm not a huge fan of using a GUI to express these things though so I would strongly prefer to have something that allows me to specify a financial situation in code and see the result of that.
Which lead me to the idea of creating Fingine which was meant to be an engine for financial simulations. It was supposed to be analogous to something like Unity or Unreal in the sense that there would be an engine under the hood that would handle all the little details for you and you would just write some script on top that brings in a lot of financial matters such as accounts, taxes, income events, expense events, assert growth, asset declines and others into 1 convenient package.
The mistake I made in my initial attempt to build this is that my ambitions exceeded my ability. For one, I didn't know enough about finance to engage in this project. Two, I tried to do this in a language I was insufficiently familiar with (Rust) and that hobbled my ability to make progress. So, I ended up getting frustrated and giving up but not before learning a lot of Rust (which was definitely good for me) and learning plenty about personal finance (also great for me).
I would like to give this a go again though but to make sure I do that correctly, I need to think about what went wrong last time. I think for me, a side project needs to follow as many of these rules as possible for it to be achievable: * I should have someone I respect working with me to hold me to account * The project should be scoped down enough that I can build something that I can talk about within 4 weekends of dedicated work * I should be pretty familiar with my chosen tools and I need to constrain my use of anything novel * I should have clearly defined goals I don't think the first one is achievable here but I can do the other 3 reasonably well. To do that, I have the following goals for a revival of Fingine: * It should allow for users to create financial simulations using rules that roughly match what someone would have to do to model a financial situation that could occur in Canada * Instead of trying to cover tax credits and tax deductions in depth, I'll let users specify what's relevant to their situation and provide the ability to run custom code to specify these things. This same principle applies to a lot of other financial primitives such as income, expense, asset growth, asset decline, and others. * Instead of being modelled as an engine in written in a high performance language with a scripting language that can be written on top. It will be a domain specific language implemented using some sort of language that has support for metaprogramming. * It should support monte carlo simulations, that would be a huge improvement over my current methods for financial planning.
Furthermore, I have the following non-goals for any revival of Fingine. * It should not be tailored to anyone's financial situation. Last time, I had a lot of trouble trying to cover every possible situation. I should focus on my own needs first. * I don't care about adoption, this is for me, not for other folks. * While ideally it is extensible enough that it works for other financial systems, I will only focus on Canada since that's what I'm familiar with. * I'm not going to concern myself too much with performance, as long as it works for my needs, that's enough
With that in mind, I'm mindful that last time I did get bogged down in a lack of domain knowledge and skill with my chosen language. I've read enough and learned enough over the past few years that I'm pretty comfortable with personal finance in a way I wasn't years ago. I'm also not interested in pursuing Rust as the language for this revival. Instead, I'm considering Elixir. Admittedly, I'm not familiar enough with Elixir right now but I plan on fixing that both for the purposes of this project and to align with my broader goal of learning distributed systems well by doing the following: * Reading Elixir in Action * Going through Exercism's learning exercises for Elixir * Going through the Gossip Glomers in Elixir
Once I've gone through that I'll hopefully have enough context that I won't struggle with it the way I struggled with Rust. In terms of design principles, I've already stated my general principle of only specifying financial primitives and not getting too deep into the weeds of those principles but on top of that, I really like the idea of conceptualizing the simulation as a bunch of matrix manipulations.
Imagine we have the following set of primitives: - Accounts (this is where assets are domiciled, they can be bank accounts, retirement accounts, and more) - Liabilities (this is where we account for what is owed, taxes are ) - Income events (any income) - Asset value change events (growth or decline) - Liability value change events (growth or decline) - Expense events (any expense) - Transfer events (to represent the transfer between one account to another) - Tax credits - Tax deductions - Income tax system - Sales tax system - Wealth tax system (note: this incorporates land, and property taxes which are common in Canada but should be generic so it can cover other assets)
We can conceptualize each account's (or perhaps the value of each asset in each account) as a matrix. The events are modifications to this matrix and the tax system is a modification of each event. Here's an example, imagine I have $2,000 in my bank account and $10,000 in an index fund in my retirement account. We can represent this in a matrix as the following:
[2_000, 10_000]
Let's say we have an income event of $5,000 which becomes $3,500 after accounting for taxes. This event becomes a modification like the following:
[2_000, 10_000] + [3_500, 0]
With Elixir Nx this should be
Nx.tensor([2000.0, 10000.0]) + Nx.tensor([3500.0, 0])
Admittedly, I think there's some more thought I need to put into this specifically in the context of applying double entry accounting here. So I'll probably add learning hledger
on the list of things to do prior to undertaking this all over again. Regardless though, it's good to get some of these ideas out there.