Header menu logo Teaching

BinderScriptNotebook

Some good things to reference

Anonymous Records. You can read the above link for details, but the point of these is quite simple.

Records have been our main type for holding data for an observation. We've typically defined these ahead of time with a name before using them. This is good for important types that you will use frequently.

If you're using a particular record in only a few lines of code, then it can feel cumbersome to define the type beforehand. Anonymous records are a good solution in these circumstances. They are records that you can essentially use like regular records that we've been using, but you don't have to define the name of the record ahead of time.

I rarely use anonymous records, but you might find them useful for exploratory data manipulation. They're also kind of nice for these short problems because I don't need to define a record for each problem.

Anonymous records

Question 1

  1. Create a record named ExampleRec that has an X field of type int and a Y field of type int. Create an example ExampleRec and assign it to a value named r.
  2. Create an anonymous record that has an X field of type int and a Y field of type int. Create an example of the anonymous record and assign it to a value named ar.

answer

// a regular named record
type ExampleRec = { X : int; Y : int }
let r = { X = 1; Y = 2}
// an anonymous record. The difference is
// 1. We did not define the type ahead of time.
// 2. We put the pipe symbole "|" inside the curly braces.
let ar = {| X = 1; Y = 2|}
// Note that they are not the same type, so if you
// compare them they will be different even though
// the X and Y fields have the same values.
// For example, running `r = ar` 
// will give a compiler error
type ExampleRec =
  {
    X: int
    Y: int
  }
val r: ExampleRec = { X = 1
                      Y = 2 }
val ar: {| X: int; Y: int |} = { X = 1
                                 Y = 2 }

Question 2

Imagine you have this list

open System
type ArExample = { Date : DateTime; Value: float}
let arr = [ { Date = DateTime(1990,1,1); Value = 1.25}
            { Date = DateTime(1990,1,2); Value = 2.25}
            { Date = DateTime(1991,1,1); Value = 3.25} ]
  1. Group the observations by a tuple of (year,month) and find the minimum value for each group. Report the result as a tuple of the group and the minimum value [so it will be ((year, month), minValue)].
  2. Now, the same thing with anonymous records. Group the observations by an Anonymous Record {| Year = year; Month= month|} and find the minimum value for each group. Report the result as an Anonymous record with a Group field for the group and a value field for the minimum value [so it will be {| Group = {| Year = year; Month= month|}; Value = minValue |}].

answer

// 1.

// Option 1: here I will explicitly put year and month in the final result
arr 
|> List.groupBy(fun x -> x.Date.Year, x.Date.Month)
|> List.map(fun ((year, month), xs) ->
    let minValue = 
        xs 
        |> List.map(fun x -> x.Value)
        |> List.min
    (year, month), minValue) // explicitly put it in the result

// or Option 1:
// since I'm just returning the grouping variable, there's really
// no need to deconstruct it into year, month at any point.
arr 
|> List.groupBy(fun x -> x.Date.Year, x.Date.Month)
|> List.map(fun (group, xs) -> // match group to (year,month) together
    let minValue = 
        xs 
        |> List.map(fun x -> x.Value)
        |> List.min
    group, minValue)

// 2. Now using anonymous records
// This is where anonymous records can be useful.
// For example, sometimes grouping by many things, 
// using anonymous records like this make it more clear what the different
// grouping variables are because they have names.
// It's like a middle ground between tuples with no clear naming structure
// and regular named records that are very explicit.

arr 
|> List.groupBy(fun x -> {| Year = x.Date.Year; Month = x.Date.Month |})
|> List.map(fun (group, xs) -> 
    let minValue = 
        xs 
        |> List.map(fun x -> x.Value)
        |> List.min
    {| Group = group; Value = minValue |})
val it: {| Group: {| Month: int; Year: int |}; Value: float |} list =
  [{ Group = { Month = 1
               Year = 1990 }
     Value = 1.25 }; { Group = { Month = 1
                                 Year = 1991 }
                       Value = 3.25 }]

Portfolio Returns

Question 1

Imagine that you have the following positions in your portfolio. For each position you have a weight and a return. What is the return of the entire portfolio?

type PortReturnPos = { Id: string;  Weight: float; Return: float}
let stockPos = { Id = "stock"; Weight = 0.25; Return = 0.1 }
let bondPos = { Id = "bond"; Weight = 0.75; Return = 0.05}

answer

// Remember that portfolio returns are a weighted average
// of the returns of the stocks in the portfolio. The weights
// are the position weights.

// Option 1:
let stockAndBondPort = 
    stockPos.Weight*stockPos.Return + bondPos.Weight*bondPos.Return

// Option 2: or, doing the multiplication and summation with collections
let weightXreturn =
    [ for pos in [ stockPos; bondPos ] do pos.Weight * pos.Return ]
// look at it
weightXreturn
// now sum
let stockAndBondPort2 = weightXreturn |> List.sum
// check
stockAndBondPort = stockAndBondPort2 // evaluates to true
val stockAndBondPort: float = 0.0625
val weightXreturn: float list = [0.025; 0.0375]
val stockAndBondPort2: float = 0.0625
val it: bool = true

Question 2

Imagine that you have the following positions in your portfolio. For each position you have a weight and a return. What is the return of the entire portfolio?

let positions =
    [ { Id = "stock"; Weight = 0.25; Return = 0.12 }
      { Id = "bond"; Weight = 0.25; Return = 0.22 }
      { Id = "real-estate"; Weight = 0.5; Return = -0.15 } ]

answer

// Option 1:
let threeAssetPortfolioReturn =
    positions
    |> List.map(fun pos -> pos.Weight*pos.Return)
    |> List.sum

// Option 2:
let threeAssetPortfolioReturn2 =
    positions
    |> List.sumBy(fun pos -> pos.Weight*pos.Return)

// Option 3:
let threeAssetPortfolioReturn3 =
    [ for pos in positions do pos.Weight * pos.Return ]
    |> List.sum
val threeAssetPortfolioReturn: float = 0.01
val threeAssetPortfolioReturn2: float = 0.01
val threeAssetPortfolioReturn3: float = 0.01

Question 3

Imagine that you have the following positions in your portfolio. For each position you have a weight and a return. What is the return of the entire portfolio?

let positionsWithShort =
    [ { Id = "stock"; Weight = 0.25; Return = 0.12 }
      { Id = "bond"; Weight = -0.25; Return = 0.22 }
      { Id = "real-estate"; Weight = 1.0; Return = -0.15 } ]

answer

let positionsWithShortReturn =
    positionsWithShort
    |> List.map(fun pos -> pos.Weight*pos.Return)
    |> List.sum
val positionsWithShortReturn: float = -0.175

Sharpe Ratios

Question 1

Imagine that you have the following array of annual returns in excess of the risk-free rate. What is the annualized Sharpe ratio?

let rets = [ 0.1; -0.4; 0.2; 0.15; -0.03 ]
//Note that the units are such that 0.1 is 10%.

answer

#r "nuget: FSharp.Stats, 0.5.0"
open FSharp.Stats

let retsAvg = rets |> List.average
// we get stDev from FSharp.Stats
let retsStdDev = rets |> stDev 
let retsSharpeRatio = retsAvg / retsStdDev
[Loading C:\Users\runneradmin\.packagemanagement\nuget\Cache\ec63001b4149383af67dc5083312da4ecf34b844b655d861ca5bad281c016ede.fsx]
module FSI_0051.
       Ec63001b4149383af67dc5083312da4ecf34b844b655d861ca5bad281c016ede

val retsAvg: float = 0.004
val retsStdDev: float = 0.241516045
val retsSharpeRatio: float = 0.01656204663

Question 2

Imagine that you have the following array of monthly returns in excess of the risk-free rate. What is the annualized Sharpe ratio?

let rets = [ 0.1; -0.4; 0.2; 0.15; -0.03 ]
//Note that the units are such that 0.1 is 10%.

answer

// remember that to annualize an arithmetic return,
// we do return * (# compounding periods per year)
// to annualize a standard deviation, 
// we do sd * sqrt(# compounding periods per year)

let monthlyRetsAnnualizedAvg = 12.0*(rets |> List.average)
// or
let monthlyRetsAnnualizedAvg2 = 
    rets 
    |> List.average 
    // now we're going to use a lambda expression.
    // this is the same idea as when we do Array.map(fun x -> ...)
    // except now we're only piping a float, not an array so
    // we're leaving off the "Array.map" 
    |> (fun avg -> 12.0 * avg) 
// or, in two steps
let monthlyRetsAvg = rets |> List.average
let monthlyRetsAnnualizedAvg3 = 12.0*monthlyRetsAvg

// now the standard deviation
let monthlyRetsAnnualizedSd = 
    rets 
    |> stDev
    |> fun monthlySd -> sqrt(12.0) * monthlySd
//or, in two steps
let monthlyRetsSd = rets |> stDev
let monthlyRetsAnnualizedSd2 = sqrt(12.0)*monthlyRetsSd

// SharpeRatio
let annualizedSharpeFromMonthly =
    monthlyRetsAnnualizedAvg / monthlyRetsAnnualizedSd

// or, since 12.0/sqrt(12.0) = sqrt(12.0) then
// (monthlyRetsAvg *12.0)/(monthlyRetsSd*sqrt(12.0)) = 
//      sqrt(12.0)*(monthlyRetsAvg/monthlyRetsSd)
let annualizedSharpeFromMonthly2 =
    sqrt(12.0) * (monthlyRetsAvg / monthlyRetsSd)

// check
// we have to round because floating point math gives us slightly different #'s
// recall from the fundamentals lecture how floating point math is inexact.
Math.Round(annualizedSharpeFromMonthly,6) = Math.Round(annualizedSharpeFromMonthly2,6) // true
val monthlyRetsAnnualizedAvg: float = 0.048
val monthlyRetsAnnualizedAvg2: float = 0.048
val monthlyRetsAvg: float = 0.004
val monthlyRetsAnnualizedAvg3: float = 0.048
val monthlyRetsAnnualizedSd: float = 0.8366361216
val monthlyRetsSd: float = 0.241516045
val monthlyRetsAnnualizedSd2: float = 0.8366361216
val annualizedSharpeFromMonthly: float = 0.05737261249
val annualizedSharpeFromMonthly2: float = 0.05737261249
val it: bool = true

Question 3

Imagine that you have the following array of daily returns in excess of the risk-free rate. What is the annualized Sharpe ratio?

let rets = [ 0.1; -0.4; 0.2; 0.15; -0.03 ]
//Note that the units are such that 0.1 is 10%.

answer

// Convention for daily is 252 trading days per year.
// so annualize daily by multiplying by sqrt(252.0)
let annualizedSharpeFromDaily =
    let avgRet = rets |> List.average
    let stdevRet = rets |> stDev
    sqrt(252.0) * (avgRet/stdevRet)
// or in multiple steps
let dailyAvgRet = rets |> List.average
let dailyStDevRet = rets |> stDev
let annualizedSharpeFromDaily2 =
    sqrt(252.0) * (dailyAvgRet/dailyStDevRet)
val annualizedSharpeFromDaily: float = 0.2629143395
val dailyAvgRet: float = 0.004
val dailyStDevRet: float = 0.241516045
val annualizedSharpeFromDaily2: float = 0.2629143395

Risky weights

Question 1

Assume that you are a mean-variance investor where your utility function has the form

\[U = \mu - \frac{\gamma}{2}\sigma^2\]

You plan to allocate only between a risky asset and the risk-free asset. Your risk aversion parameter \(\gamma=3\). For the risky asset, \(\mu=0.1\) and \(\sigma=0.2\). What is your optimal weight in the risky asset?

answer

let mu = 0.1
let sigma = 0.2
let gamma = 3.0
let riskyWeight = mu / (gamma * sigma ** 2.0)
val mu: float = 0.1
val sigma: float = 0.2
val gamma: float = 3.0
val riskyWeight: float = 0.8333333333

type ExampleRec = { X: int Y: int }
ExampleRec.X: int
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
ExampleRec.Y: int
val r: ExampleRec
val ar: {| X: int; Y: int |}
anonymous record field X: int
anonymous record field Y: int
namespace System
type ArExample = { Date: DateTime Value: float }
Multiple items
[<Struct>] type DateTime = new: year: int * month: int * day: int -> unit + 16 overloads member Add: value: TimeSpan -> DateTime member AddDays: value: float -> DateTime member AddHours: value: float -> DateTime member AddMicroseconds: value: float -> DateTime member AddMilliseconds: value: float -> DateTime member AddMinutes: value: float -> DateTime member AddMonths: months: int -> DateTime member AddSeconds: value: float -> DateTime member AddTicks: value: int64 -> DateTime ...
<summary>Represents an instant in time, typically expressed as a date and time of day.</summary>

--------------------
DateTime ()
   (+0 other overloads)
DateTime(ticks: int64) : DateTime
   (+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(date: DateOnly, time: TimeOnly) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int) : DateTime
   (+0 other overloads)
DateTime(date: DateOnly, time: TimeOnly, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : DateTime
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : DateTime
   (+0 other overloads)
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

--------------------
type float = Double

--------------------
type float<'Measure> = float
val arr: ArExample list
Multiple items
module List from Microsoft.FSharp.Collections

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...
val groupBy: projection: ('T -> 'Key) -> list: 'T list -> ('Key * 'T list) list (requires equality)
val x: ArExample
ArExample.Date: DateTime
property DateTime.Year: int with get
<summary>Gets the year component of the date represented by this instance.</summary>
<returns>The year, between 1 and 9999.</returns>
property DateTime.Month: int with get
<summary>Gets the month component of the date represented by this instance.</summary>
<returns>The month component, expressed as a value between 1 and 12.</returns>
val map: mapping: ('T -> 'U) -> list: 'T list -> 'U list
val year: int
val month: int
val xs: ArExample list
val minValue: float
ArExample.Value: float
val min: list: 'T list -> 'T (requires comparison)
val group: int * int
val group: {| Month: int; Year: int |}
type PortReturnPos = { Id: string Weight: float Return: float }
Multiple items
val string: value: 'T -> string

--------------------
type string = String
val stockPos: PortReturnPos
val bondPos: PortReturnPos
val stockAndBondPort: float
PortReturnPos.Weight: float
PortReturnPos.Return: float
val weightXreturn: float list
val pos: PortReturnPos
val stockAndBondPort2: float
val sum: list: 'T list -> 'T (requires member (+) and member Zero)
val positions: PortReturnPos list
val threeAssetPortfolioReturn: float
val threeAssetPortfolioReturn2: float
val sumBy: projection: ('T -> 'U) -> list: 'T list -> 'U (requires member (+) and member Zero)
val threeAssetPortfolioReturn3: float
val positionsWithShort: PortReturnPos list
val positionsWithShortReturn: float
val rets: float list
Multiple items
namespace FSharp

--------------------
namespace Microsoft.FSharp
namespace FSharp.Stats
val retsAvg: float
Multiple items
module List from FSharp.Stats
<summary> Module to compute common statistical measure on list </summary>

--------------------
module List from Microsoft.FSharp.Collections

--------------------
type List = new: unit -> List static member geomspace: start: float * stop: float * num: int * ?IncludeEndpoint: bool -> float list static member linspace: start: float * stop: float * num: int * ?IncludeEndpoint: bool -> float list

--------------------
type List<'T> = | op_Nil | op_ColonColon of Head: 'T * Tail: 'T list interface IReadOnlyList<'T> interface IReadOnlyCollection<'T> interface IEnumerable interface IEnumerable<'T> member GetReverseIndex: rank: int * offset: int -> int member GetSlice: startIndex: int option * endIndex: int option -> 'T list static member Cons: head: 'T * tail: 'T list -> 'T list member Head: 'T member IsEmpty: bool member Item: index: int -> 'T with get ...

--------------------
new: unit -> List
val average: list: 'T list -> 'T (requires member (+) and member DivideByInt and member Zero)
val retsStdDev: float
val stDev: items: 'T seq -> 'U (requires member (-) and member Zero and member DivideByInt and member (+) and member ( * ) and member (+) and member (/) and member Sqrt)
<summary> Computes the sample standard deviation </summary>
<param name="items">The input sequence.</param>
<remarks>Returns NaN if data is empty or if any entry is NaN.</remarks>
<returns>standard deviation of a sample (Bessel's correction by N-1)</returns>
val retsSharpeRatio: float
val monthlyRetsAnnualizedAvg: float
val monthlyRetsAnnualizedAvg2: float
val avg: float
val monthlyRetsAvg: float
val monthlyRetsAnnualizedAvg3: float
val monthlyRetsAnnualizedSd: float
val monthlySd: float
val sqrt: value: 'T -> 'U (requires member Sqrt)
val monthlyRetsSd: float
val monthlyRetsAnnualizedSd2: float
val annualizedSharpeFromMonthly: float
val annualizedSharpeFromMonthly2: float
type Math = static member Abs: value: decimal -> decimal + 7 overloads static member Acos: d: float -> float static member Acosh: d: float -> float static member Asin: d: float -> float static member Asinh: d: float -> float static member Atan: d: float -> float static member Atan2: y: float * x: float -> float static member Atanh: d: float -> float static member BigMul: a: int * b: int -> int64 + 2 overloads static member BitDecrement: x: float -> float ...
<summary>Provides constants and static methods for trigonometric, logarithmic, and other common mathematical functions.</summary>
Math.Round(a: float) : float
Math.Round(d: decimal) : decimal
Math.Round(value: float, mode: MidpointRounding) : float
Math.Round(value: float, digits: int) : float
Math.Round(d: decimal, mode: MidpointRounding) : decimal
Math.Round(d: decimal, decimals: int) : decimal
Math.Round(value: float, digits: int, mode: MidpointRounding) : float
Math.Round(d: decimal, decimals: int, mode: MidpointRounding) : decimal
val annualizedSharpeFromDaily: float
val avgRet: float
val stdevRet: float
val dailyAvgRet: float
val dailyStDevRet: float
val annualizedSharpeFromDaily2: float
val mu: float
val sigma: float
val gamma: float
val riskyWeight: float

Type something to start searching.