Build Yourself a Robo-Advisor in F#. Part I : Domain Modelling
All text and code copyright (c) 2016 by Doug Bruce. Used with permission.
Original post dated 2016-12-09 available at http://dougbruce.blogspot.com.by/2016/12/build-yourself-robo-advisor-in-f-part-i.html
By Doug Bruce
This article is based on the first part of the 'Build yourself a Robo-Advisor in F#' workshop, as presented at the 2016 F# Progressive F# Tutorials conference in London.
Introduction
Most of us aren't saving enough for our future. This isn't something that's going to go away - we're living longer and many of us are underestimating how much money we need for a comfortable retirement. How can you solve this problem? By harnessing the power of F# to deliver clear and easy-to-understand advice and recommendations that takes you on a journey from confused to confident.
Over the last couple of years, robo-advisors have emerged as a platform for automating this advice as one part of the Fintech revolution. In this short series of posts we will build a web-based robo advisor that will show you how much money you might have in retirement.
Along the way you will discover how to:
- Understand a domain and model it with the F# type system
- Leverage F#'s data capabilities and make a first program that can chart our projected savings
- Take it to the next level by building a web-based robo-advisor using one of F#'s fantastic open-source web app frameworks
Part 1 - Domain Modelling
A good first step towards building a new piece of software is to model the domain: what exactly is it we're getting the software to do? What are the real-life equivalents of the code we want to write?
What is our domain
We boil our domain down to one example question:
How much money will I have when I retire?
More generally, we want to take someone and calculate how much their pension pot will be worth when they retire.
The bold sections give us our basic domain:
- People
- Money
- Time
How can F# help us model it?
Algebraic Data Types
Discriminated Unions allow us to express real-world concepts almost directly in code.
Let's take a really simple one with only two options, the boolean. How to we write this in F#?
type Bool = True | False
What if we want to say that something can either have a value or not?
type Option<'a> =
| Some of 'a
None
What about if something can fail?
type Result<'TSuccess, 'TFailure> =
| Succcess of 'TSuccess
| Failure of 'TFailure
And what if we want to distinguish one type of string from another?
type EmailAddress = EmailAddress of string
That's all pretty useful! We can combine them with Records. Immutable records have a concise syntax and structural equality which make them really powerful for modelling. Here's an example:
type Car = {
Engine : Engine
Wheels : Wheel list
Seats : ... }
Pattern matching
Once we have our unions, we have a type-safe switch
alternative in the form of pattern matching.
These give us a one-step match & decompose workflow, and for extra benefit the compiler catches non-exhaustive match so that we are warned if we've added a case and not properly handled it.
Here's an example using a Result
:
let handleResult = function
| Success result -> "Woop! It all worked, the answer is: " + result
| Failure error -> "Uh-oh. Something bad happened: " + error
Units of measure
The question we want to answer here is:
How do you represent a currency in code?
One way to do so is with Units of Measure.
With these we can make sure something has the right unit, as well as creating derives measures ( e.g kg m / s^2
)
We can also reduce errors in conversions & calculations. How bad can this really be? Take a look here!
What did we come up with?
Using what we've just discussed, here's a minimal domain model for figuring out how much money someone is likely to have in retirement.
module Domain =
open System
type Gender = Male | Female
type EmailAddress = EmailAddress of string
type Person = {
Name : string
Gender : Gender
DateOfBirth : DateTime
EmailAddress : EmailAddress option }
type ContributionFrequency = Monthly | Annual
type ContributionSchedule<[<Measure>] 'ccy> = {
Amount : decimal<'ccy>
Frequency : ContributionFrequency }
type PensionPot<[<Measure>] 'ccy> = {
Value : decimal<'ccy>;
ValuationDate : DateTime;
ContributionSchedule: ContributionSchedule<'ccy> }
[<Measure>] type GBP
[<Measure>] type USD
[<Measure>] type EUR
let valuePensionPot<[<Measure>] 'ccy> (pensionPot : PensionPot<'ccy>) (valuationDate: DateTime) =
match pensionPot.ContributionSchedule.Frequency with
| Monthly ->
let monthsInFuture = (valuationDate.Year - pensionPot.ValuationDate.Year) * 12 + valuationDate.Month - pensionPot.ValuationDate.Month
pensionPot.Value + decimal monthsInFuture * pensionPot.ContributionSchedule.Amount
| Annual ->
let yearsInFuture = valuationDate.Year - pensionPot.ValuationDate.Year
pensionPot.Value + decimal yearsInFuture * pensionPot.ContributionSchedule.Amount
I think this code is pretty short and easy to understand given the complexity of the domain. We've used a wide variety of features that helped us along the way.
Useful Links
Talks
- Scott Wlaschin NDC: https://vimeo.com/97507575
- Tomas Petricek Channel 9: https://www.youtube.com/watch?v=Sa6bntNslvY
- Mark Seeman NDC: https://vimeo.com/131631483
Blogs
- http://fsharpforfunandprofit.com/ddd/
- http://tomasp.net/blog/type-first-development.aspx/
- http://gorodinski.com/blog/2013/02/17/domain-driven-design-with-fsharp-and-eventstore/
Wrapping up
In this part you've seen how we take take a real-life domain and come up with a concise and accurate model for it using F#.
We've got some great language features to model domains in F#! If you are thinking about how to represent things in code, the ideas in this post are a great starting point.
Next Time
Charts and data! You'll see how to take the domain we've just created and chart it in a Desktop app with FSharp.Charting. We'll also look at accessing data via type providers.