Student Name |
Student Number |
---|---|
1: |
|
2: |
|
3: |
|
4: |
|
5: |
This is an assignment. You should work in groups. Please write your group and group member names above. You will find sections labeled Task asking you to do each piece of analysis. Please make sure that you complete all of these tasks. I included some tests to help you see if you are calculating the solution correctly, but if you cannot get the test to pass submit your best attempt and you may recieve partial credit.
All work that you submit should be your own. Make use of the course resources and example code on the course website. It should be possible to complete all the requested tasks using information given below or somewhere on the course website.
For testing
#r "nuget: FsUnit.Xunit"
#r "nuget: xunit, 2.*"
open Xunit
open FsUnit.Xunit
open FsUnitTyped
For the assignment
#r "nuget: FSharp.Data, 5.0.2"
#r "nuget: FSharp.Stats, 0.5.0"
#r "nuget: NovaSBE.Finance, 0.5.0"
#r "nuget: MathNet.Numerics"
#r "nuget: MathNet.Numerics.FSharp"
#r "nuget: Plotly.NET, 3.*"
open System
open FSharp.Data
open Plotly.NET
open FSharp.Stats
open MathNet.Numerics.Statistics
open NovaSBE.Finance.Ols
Load Data
First, make sure that you're referencing the correct files.
Here I'm assuming that you have a class folder with this notebook and a data
folder inside of it. The folder hierarchy would look like below where you
have the below files and folders accessible:
|
open NovaSBE.Finance.Portfolio
Data files
We assume the id_and_return_data.csv
file and the signal csv file are in the notebook folder.
let [<Literal>] IdAndReturnsFilePath = "id_and_return_data.csv"
let [<Literal>] MySignalFilePath = "rd_sale.csv"
let strategyName = "R&D to sales"
If my paths are correct, then this code should read the first few lines of the files. If it doesn't show the first few lines, fix the above file paths.
IO.File.ReadLines(IdAndReturnsFilePath)
|> Seq.truncate 5
|> Seq.iter (printfn "%A")
IO.File.ReadLines(MySignalFilePath)
|> Seq.truncate 5
|> Seq.iter (printfn "%A")
Ok, now assuming those paths were correct the below code will work. I will put all this prep code in one block so that it is easy to run.
let idAndReturnsCsv =
CsvProvider<IdAndReturnsFilePath,ResolutionFolder = __SOURCE_DIRECTORY__>.GetSample().Rows
|> Seq.toList
let mySignalCsv =
CsvProvider<MySignalFilePath,ResolutionFolder = __SOURCE_DIRECTORY__>.GetSample().Rows
|> Seq.toList
A list of Signal
records. The signal type is defined in the NovaSBE.Finance.Portfolio
module here.
let mySignals =
mySignalCsv
|> List.choose (fun row ->
match row.Signal with
| None -> None
| Some signal ->
let signalRecord: Signal =
{ SecurityId = Other row.Id
FormationDate = DateTime(row.Eom.Year, row.Eom.Month, 1)
Signal = signal }
Some signalRecord)
// look at a few signals
mySignals[..3]
A list of Security return records. The SecurityReturn
type is defined in the NovaSBE.Finance.Portfolio
module here
let myReturns =
idAndReturnsCsv
|> List.choose (fun row ->
match row.Ret with
| None -> None
| Some ret ->
let ret: SecurityReturn =
{ SecurityId = Other row.Id
Date = DateTime(row.Eom.Year, row.Eom.Month, 1)
Return= ret }
Some ret)
// look at a few returns
myReturns[..3]
A list of security market caps. We'll need this for value-weight portfolios. The WeightVariable
type is defined in the NovaSBE.Finance.Portfolio
module here.
let myMktCaps =
idAndReturnsCsv
|> List.choose (fun row ->
match row.MarketEquity with
| None -> None
| Some mktCap ->
let mktCap: WeightVariable =
{ SecurityId = Other row.Id
FormationDate = DateTime(row.Eom.Year, row.Eom.Month, 1)
Value = mktCap }
Some mktCap)
// look at a few market caps
myMktCaps[..3]
Forming our strategy
We're now going to use the Backtest
code to generate portfolios. It is defined in the NovaSBE.Finance.Portfolio
module here.
The Backtest
class automates some of the code we did earlier to make portfolio construction simpler.
let backtest = Backtest(returns=myReturns, signals=mySignals, nPortfolios=3, name = strategyName)
Value Weighted Portfolios
let vw = backtest.strategyValueWeighted(myMktCaps)
vw.Portfolios[..3]
vw.Returns[..3]
Long-short portfolios
We get the Fama-French 3-Factor asset pricing model data.
open NovaSBE.Finance
let ff3Lookup =
French.getFF3 French.Frequency.Monthly
|> Array.map (fun x -> DateTime(x.Date.Year, x.Date.Month, 1), x)
|> Map
Isolate some notable portfolios.
type SignalPortfolioObs =
{ Month: DateTime
Name: string
ExcessReturn: float }
let long =
vw.Returns
|> List.filter (fun row -> row.Index = 3)
|> List.map (fun row ->
let retx = row.Return - ff3Lookup[row.Month].Rf
{ Month = row.Month
Name = "Long"
ExcessReturn = retx })
let short =
vw.Returns
|> List.filter (fun row -> row.Index = 1)
|> List.map (fun row ->
let retx = row.Return - ff3Lookup[row.Month].Rf
{ Month = row.Month
Name = "Short"
ExcessReturn = retx })
let longShort =
vw.Returns
|> List.groupBy (fun x -> x.Month)
|> List.map (fun (month, xs) ->
let long = xs |> List.find (fun x -> x.Index = 3)
let short = xs |> List.find (fun x -> x.Index = 1)
{ Month = long.Month
Name = "Long-short"
ExcessReturn = long.Return - short.Return })
Start of assignment
Task: Calculate the annualized Sharpe ratios of your long, short, and long-short portfolios. Assign them to values
longSharpe
,shortSharpe
, andlongShortSharpe
, respectively.
// Solution here.
Tests
longSharpe |> should (equalWithin 1e-6) 0.2613490337
shortSharpe |> should (equalWithin 1e-6) 0.490118588
longShortSharpe |> should (equalWithin 1e-6) -0.05676492997
Task: Create a
list<RegData>
for your long-short portfolio. Assign it to a value namedlongShortRd
.
type RegData =
{ Month: DateTime
ExcessReturn: float
MktRf: float
Hml: float
Smb: float }
// Solution here.
Tests
longShortRd |> shouldHaveLength 252
longShortRd |> should be ofExactType<RegData list>
Task: Fit CAPM and Fama-French 3-factor models for your long-short portfolio. Assign them to a values named
capmModel
andff3Model
, respectively.
// Solution here.
Tests.
capmModel |> should be ofExactType<RegressionResults>
ff3Model |> should be ofExactType<RegressionResults>
CAPM model evaluation.
Task: What is the CAPM alpha for your long-short portfolio? Use code to assign the alpha to the value
capmAlpha
. Is it significantly different from zero? Use code to assign the t-statistic to the valuecapmAlphaT
.
// Solution here.
Tests
capmAlpha |> should (equalWithin 1e-6) -0.003015844638
capmAlphT |> should (equalWithin 1e-6) -1.011538156
Task: What is the CAPM beta for your long-short portfolio? Use code to assign the beta to the value
capmBeta
. Is it significantly different from zero? Use code to assign the t-statistic to the valuecapmBetaT
.
// Solution here.
Tests
capmBeta |> should (equalWithin 1e-6) 0.3874415756
capmBetaT |> should (equalWithin 1e-6) 5.957967542
Task: What is the information ratio for your long-short portfolio when using the CAPM model? Assign it to a value named
capmIR
.
// Solution here.
Tests
capmIR |> should (equalWithin 1e-6) -0.06434136117
Fama-French 3-factor model evaluation.
Task: What is the Fama-French 3-factor model alpha for your long-short portfolio. Is it significantly different from zero?
// Solution here.
Task: What are the betas on the Market, HML, and SMB factors for your long-short portfolio. Are they significantly different from zero?
// Solution here.
Task: Based on the Market, HML, and SMB factor betas for your long-short portfolio, would you say your portfolio is more like a value portfolio, more like a growth portfolio, or neither? Explain.
// Solution here.
Task: Based on the Market, HML, and SMB factor betas for your long-short portfolio, would you say your portfolio is more like a small-cap portfolio, more like a large-cap portfolio, or neither? Explain.
// Solution here.
Task: What is the information ratio for your long-short portfolio when using the Fama and French 3-factor model?
// Solution here.
namespace FSharp
--------------------
namespace Microsoft.FSharp
namespace FSharp.Data
--------------------
namespace Microsoft.FSharp.Data
type LiteralAttribute = inherit Attribute new: unit -> LiteralAttribute
--------------------
new: unit -> LiteralAttribute
<summary>Provides static methods for the creation, copying, deletion, moving, and opening of a single file, and aids in the creation of <see cref="T:System.IO.FileStream" /> objects.</summary>
IO.File.ReadLines(path: string, encoding: Text.Encoding) : Collections.Generic.IEnumerable<string>
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
<summary>Typed representation of a CSV file.</summary> <param name='Sample'>Location of a CSV sample file or a string containing a sample CSV document.</param> <param name='Separators'>Column delimiter(s). Defaults to <c>,</c>.</param> <param name='InferRows'>Number of rows to use for inference. Defaults to <c>1000</c>. If this is zero, all rows are used.</param> <param name='Schema'>Optional column types, in a comma separated list. Valid types are <c>int</c>, <c>int64</c>, <c>bool</c>, <c>float</c>, <c>decimal</c>, <c>date</c>, <c>datetimeoffset</c>, <c>timespan</c>, <c>guid</c>, <c>string</c>, <c>int?</c>, <c>int64?</c>, <c>bool?</c>, <c>float?</c>, <c>decimal?</c>, <c>date?</c>, <c>datetimeoffset?</c>, <c>timespan?</c>, <c>guid?</c>, <c>int option</c>, <c>int64 option</c>, <c>bool option</c>, <c>float option</c>, <c>decimal option</c>, <c>date option</c>, <c>datetimeoffset option</c>, <c>timespan option</c>, <c>guid option</c> and <c>string option</c>. You can also specify a unit and the name of the column like this: <c>Name (type<unit>)</c>, or you can override only the name. If you don't want to specify all the columns, you can reference the columns by name like this: <c>ColumnName=type</c>.</param> <param name='HasHeaders'>Whether the sample contains the names of the columns as its first line.</param> <param name='IgnoreErrors'>Whether to ignore rows that have the wrong number of columns or which can't be parsed using the inferred or specified schema. Otherwise an exception is thrown when these rows are encountered.</param> <param name='SkipRows'>Skips the first n rows of the CSV file.</param> <param name='AssumeMissingValues'>When set to true, the type provider will assume all columns can have missing values, even if in the provided sample all values are present. Defaults to false.</param> <param name='PreferOptionals'>When set to true, inference will prefer to use the option type instead of nullable types, <c>double.NaN</c> or <c>""</c> for missing values. Defaults to false.</param> <param name='Quote'>The quotation mark (for surrounding values containing the delimiter). Defaults to <c>"</c>.</param> <param name='MissingValues'>The set of strings recognized as missing values specified as a comma-separated string (e.g., "NA,N/A"). Defaults to <c>NaN,NA,N/A,#N/A,:,-,TBA,TBD</c>.</param> <param name='CacheRows'>Whether the rows should be caches so they can be iterated multiple times. Defaults to true. Disable for large datasets.</param> <param name='Culture'>The culture used for parsing numbers and dates. Defaults to the invariant culture.</param> <param name='Encoding'>The encoding used to read the sample. You can specify either the character set name or the codepage number. Defaults to UTF8 for files, and to ISO-8859-1 the for HTTP requests, unless <c>charset</c> is specified in the <c>Content-Type</c> response header.</param> <param name='ResolutionFolder'>A directory that is used when resolving relative file references (at design time and in hosted execution).</param> <param name='EmbeddedResource'>When specified, the type provider first attempts to load the sample from the specified resource (e.g. 'MyCompany.MyAssembly, resource_name.csv'). This is useful when exposing types generated by the type provider.</param>
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
namespace FSharp.Stats.Signal
--------------------
type Signal = { SecurityId: SecurityId FormationDate: DateTime Signal: float }
[<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)
type Backtest = new: returns: SecurityReturn seq * signals: Signal seq * nPortfolios: int * ?name: string * ?inParallel: bool -> Backtest member strategyEqualWeighted: unit -> BackTestResult member strategyValueWeighted: marketCaps: WeightVariable seq -> BackTestResult
<summary>Constructs a backtest of a trading strategy based on signals.</summary>
<param name="returns">A sequence of security returns.</param>
<param name="signals">A sequence of signals for each security and formation date.</param>
<param name="nPortfolios">The number of cross-sectionally sorted portfolios to create.</param>
<param name="name">The name of the strategy.</param>
<param name="inParallel">Whether to run the backtest in parallel.</param>
<returns>The portfolio positions each formation date and the portfolio returns.</returns>
--------------------
new: returns: SecurityReturn seq * signals: Signal seq * nPortfolios: int * ?name: string * ?inParallel: bool -> Backtest
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
<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>
module Map from FSharp.Stats
<summary> Module to strore specialised computations on maps </summary>
--------------------
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 string: value: 'T -> string
--------------------
type string = String
val float: value: 'T -> float (requires member op_Explicit)
--------------------
type float = Double
--------------------
type float<'Measure> = float
<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>
module Ols from NovaSBE.Finance
--------------------
type Ols<'Record> = new: formula: string * data: 'Record seq -> Ols<'Record> member fit: unit -> RegressionResults member df_model: int member df_resid: int member endog_names: string member exog_names: string array member k_constant: int
<summary>Create a model from a formula and collection of records</summary>
--------------------
new: formula: string * data: 'Record seq -> Ols<'Record>
type RegressionResults = new: df_model: int * df_resid: int * endog: float array * exog: float array array * endog_names: string * exog_names: string array * intercept: bool * covtype: CovType -> RegressionResults member predict: data: 'a seq -> float array member summary: ?yname: string * ?xname: string seq * ?title: string * ?alpha: float * ?slim: bool -> string member coefs: Map<string,float> member df_model: int member df_resid: int member endog_names: string member ess: float member exog_names: string array member f_pvalue: float ...
--------------------
new: df_model: int * df_resid: int * endog: float array * exog: float array array * endog_names: string * exog_names: string array * intercept: bool * covtype: CovType -> RegressionResults
<summary> The estimated model coefficients </summary>
<summary> The t-statistics of the model coefficients </summary>
<summary> The model residuals </summary>