Header menu logo Teaching

BinderScriptNotebook

On moodle, there is a set of investment lecture notes called "Finance Review". Please review the Lecture-08-APT.pdf document, including the appendix.

In particular,

Imagine you have the below returns for IBM and SPY. IBM's factor beta on SPY and the (constant) risk-free rate are given below too.

type ReturnOb = { Time: int; Return : float }
let ibm =
    [| 
        { Time = 0; Return = 0.15 }
        { Time = 1; Return = 0.05 }
        { Time = 2; Return = 0.01 }
    |]

let spy =
    [| 
        { Time = 0; Return = 0.1 }
        { Time = 1; Return = 0.05 }
        { Time = 2; Return = -0.02 }
    |]    

let riskFreeRate = 0.001
let ibmBetaOnSpy = 1.2    

Question 1

What are the weights on the risk-free asset and SPY in the portfolio that hedges IBM's exposure to SPY?

  1. Report the weight on the risk-free asset as a value named wRf of type float.
  2. Report the weight on the SPY portfolio as a value named wSpy of type float.

answer

let wSpy = ibmBetaOnSpy
let wRf = 1.0-wSpy
val wSpy: float = 1.2
val wRf: float = -0.2

Question 2

What are the returns for Times [0;1;2] on the portfolio that hedges IBM's factor exposure to SPY?

  1. Report results as a value named hedgePortReturns of type ReturnOb array.

answer

let hedgePortReturns =
    spy
    |> Array.map(fun spy ->
        { Time = spy.Time 
          Return = wSpy*spy.Return + wRf*riskFreeRate })
val hedgePortReturns: ReturnOb array =
  [|{ Time = 0
      Return = 0.1198 }; { Time = 1
                           Return = 0.0598 }; { Time = 2
                                                Return = -0.0242 }|]

Question 3

Call the portfolio that hedges IBM's factor exposure to SPY the hedge portfolio. What is the hedge portfolio's factor beta on SPY?

  1. Report the answer as a value named hedgePortBetaOnSpy of type float.

answer

// 3. Hedge portfolio's factor beta on SPY
// See the lecture notes for a fuller explanation:
//
// From the portfolio return equation, the
// hedge portfolio return is
// r_hedgePort = wRf * rf + wSpy * Spy
// =>
// r_hedgePort - rf = (wSpy + wRf - 1) * rf + wSpy*(Spy -rf)
// r_hedgePort - rf = wSpy*(Spy - rf)
// So the beta is
let hedgePortBetaOnSpy = ibmBetaOnSpy 
val hedgePortBetaOnSpy: float = 1.2

Question 4

What are the returns for Times [0;1;2] on the portfolio that is long IBM and short the portfolio that hedges IBM's factor exposre to SPY?

  1. Report results as a value named longShortPortReturns of type ReturnOb array.

answer

let hedgePortMap =
    // using map because it's efficient for lookups.
    // you could use filter with such a small collection,
    // but we'll use a Map collection so that we
    // practice like it's the real thing.
    hedgePortReturns
    |> Array.map(fun x -> x.Time, x)
    |> Map.ofArray

let longShortPortReturns =
    ibm
    |> Array.choose(fun ibmOb ->
        // We're using Array.choose instead of Array.map
        // because we plan to return Some hedgeOb or None, and we
        // want to throw away all the None cases and return just
        // an array of the valid hedgeObs. 
        let matchingHedgeReturn = Map.tryFind ibmOb.Time hedgePortMap
        // in this example matchingHedgeReturn will always be Some hedgeOb.
        // But, we're using Map.tryFind and dealing with the possibility
        // that None could happen because it's good to practice
        // as if this were real code where bad things could happen.
        match matchingHedgeReturn with
        | None -> None // if there's no hedge return for ibmOb.Time return None
        | Some hedgeOb -> 
            // if there is a matching hedge return for ibmOb.Time return Some ReturnOb
            let longShort =  ibmOb.Return - hedgeOb.Return
            Some { Time = ibmOb.Time; Return = longShort })
val hedgePortMap: Map<int,ReturnOb> =
  map
    [(0, { Time = 0
           Return = 0.1198 }); (1, { Time = 1
                                     Return = 0.0598 });
     (2, { Time = 2
           Return = -0.0242 })]
val longShortPortReturns: ReturnOb array =
  [|{ Time = 0
      Return = 0.0302 }; { Time = 1
                           Return = -0.0098 }; { Time = 2
                                                 Return = 0.0342 }|]

Question 5

What is the alpha of IBM from the perspective of a factor model that uses SPY as the only risk factor?

  1. Report the result as a value named alpha of type float.

answer

let alpha = 
    longShortPortReturns 
    |> Array.averageBy(fun x -> x.Return)
val alpha: float = 0.0182

Question 6

What is the information ratio of IBM from the perspective of a factor model that uses SPY as the only risk factor?

  1. Report the result as a value named io of type float.

answer

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

let sdHedgeReturns =
    longShortPortReturns
    |> Array.map(fun x -> x.Return )
    |> Seq.stDev

// Intuitively, you can think of it similar
// to the sharpe ratio of the portfolio after
// hedging out the factor risk.

let io = alpha / sdHedgeReturns
[Loading C:\Users\runneradmin\.packagemanagement\nuget\Cache\ec63001b4149383af67dc5083312da4ecf34b844b655d861ca5bad281c016ede.fsx]
module FSI_0035.
       Ec63001b4149383af67dc5083312da4ecf34b844b655d861ca5bad281c016ede

val sdHedgeReturns: float = 0.02433105012
val io: float = 0.7480153922

type ReturnOb = { Time: int Return: float }
Multiple items
val int: value: 'T -> int (requires member op_Explicit)

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

--------------------
type int<'Measure> = int
Multiple items
val float: value: 'T -> float (requires member op_Explicit)

--------------------
type float = System.Double

--------------------
type float<'Measure> = float
val ibm: ReturnOb array
val spy: ReturnOb array
val riskFreeRate: float
val ibmBetaOnSpy: float
val wSpy: float
val wRf: float
val hedgePortReturns: ReturnOb array
module Array from Microsoft.FSharp.Collections
val map: mapping: ('T -> 'U) -> array: 'T array -> 'U array
val spy: ReturnOb
ReturnOb.Time: int
ReturnOb.Return: float
val hedgePortBetaOnSpy: float
val hedgePortMap: Map<int,ReturnOb>
val x: ReturnOb
Multiple items
module Map from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> = interface IReadOnlyDictionary<'Key,'Value> interface IReadOnlyCollection<KeyValuePair<'Key,'Value>> interface IEnumerable interface IStructuralEquatable interface IComparable interface IEnumerable<KeyValuePair<'Key,'Value>> interface ICollection<KeyValuePair<'Key,'Value>> interface IDictionary<'Key,'Value> new: elements: ('Key * 'Value) seq -> Map<'Key,'Value> member Add: key: 'Key * value: 'Value -> Map<'Key,'Value> ...

--------------------
new: elements: ('Key * 'Value) seq -> Map<'Key,'Value>
val ofArray: elements: ('Key * 'T) array -> Map<'Key,'T> (requires comparison)
val longShortPortReturns: ReturnOb array
val choose: chooser: ('T -> 'U option) -> array: 'T array -> 'U array
val ibmOb: ReturnOb
val matchingHedgeReturn: ReturnOb option
val tryFind: key: 'Key -> table: Map<'Key,'T> -> 'T option (requires comparison)
union case Option.None: Option<'T>
union case Option.Some: Value: 'T -> Option<'T>
val hedgeOb: ReturnOb
val longShort: float
val alpha: float
val averageBy: projection: ('T -> 'U) -> array: 'T array -> 'U (requires member (+) and member DivideByInt and member Zero)
Multiple items
namespace FSharp

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

--------------------
module Array from Microsoft.FSharp.Collections

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

--------------------
new: unit -> Array
Multiple items
module Seq from FSharp.Stats
<summary> Module to compute common statistical measure </summary>

--------------------
module Seq from Microsoft.FSharp.Collections

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

--------------------
new: unit -> Seq
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 io: float

Type something to start searching.