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
- Create a record named
ExampleRec
that has anX
field of type int and aY
field of type int. Create an exampleExampleRec
and assign it to a value namedr
. - Create an anonymous record that has an
X
field of type int and aY
field of type int. Create an example of the anonymous record and assign it to a value namedar
.
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} ]
-
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)
]. -
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 } ]
answerlet 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?
answerlet 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
val int: value: 'T -> int (requires member op_Explicit)
--------------------
type int = int32
--------------------
type int<'Measure> = int
[<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)
val float: value: 'T -> float (requires member op_Explicit)
--------------------
type float = Double
--------------------
type float<'Measure> = float
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 ...
<summary>Gets the year component of the date represented by this instance.</summary>
<returns>The year, between 1 and 9999.</returns>
<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 string: value: 'T -> string
--------------------
type string = String
namespace FSharp
--------------------
namespace Microsoft.FSharp
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
<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>
<summary>Provides constants and static methods for trigonometric, logarithmic, and other common mathematical functions.</summary>
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