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.

Talks

Blogs

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.

results matching ""

    No results matching ""