Skip to content

Commit

Permalink
Risk-Free Interest Rate Model (QuantConnect#7594)
Browse files Browse the repository at this point in the history
* Implement risk free interest rate as an algorithm model

* Use risk free insterest rate model in Sharpe Ratio indicator

* Address peer review

Also added python wrapper

* Take pyobject as interest rate model in Sharpe Ratio indicator

* Minor fix

* Minor fix

* Address peer review
  • Loading branch information
jhonabreul authored Nov 22, 2023
1 parent 4c034bd commit f8ca85c
Show file tree
Hide file tree
Showing 23 changed files with 543 additions and 86 deletions.
19 changes: 13 additions & 6 deletions Algorithm/QCAlgorithm.Indicators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1500,19 +1500,26 @@ public SuperTrend STR(Symbol symbol, int period, decimal multiplier, MovingAvera
}

/// <summary>
/// Creates a new RollingSharpeRatio indicator.
/// Creates a new SharpeRatio indicator.
/// </summary>
/// <param name="symbol">The symbol whose RSR we want</param>
/// <param name="sharpePeriod">Period of historical observation for sharpe ratio calculation</param>
/// <param name="riskFreeRate">Risk-free rate for sharpe ratio calculation</param>
/// <param name="riskFreeRate">
/// Risk-free rate for sharpe ratio calculation. If not specified, it will use the algorithms' <see cref="RiskFreeInterestRateModel"/>
/// </param>
/// <param name="resolution">The resolution</param>
/// <param name="selector">Selects a value from the BaseData to send into the indicator, if null defaults to the Value property of BaseData (x => x.Value)</param>
/// <returns>The RollingSharpeRatio indicator for the requested symbol over the specified period</returns>
/// <returns>The SharpeRatio indicator for the requested symbol over the specified period</returns>
[DocumentationAttribute(Indicators)]
public SharpeRatio SR(Symbol symbol, int sharpePeriod, decimal riskFreeRate = 0.0m, Resolution ? resolution = null, Func<IBaseData, decimal> selector = null)
public SharpeRatio SR(Symbol symbol, int sharpePeriod, decimal? riskFreeRate = null, Resolution? resolution = null, Func<IBaseData, decimal> selector = null)
{
var name = CreateIndicatorName(symbol, $"SR({sharpePeriod},{riskFreeRate})", resolution);
var sharpeRatio = new SharpeRatio(name, sharpePeriod, riskFreeRate);
var baseBame = riskFreeRate.HasValue ? $"SR({sharpePeriod},{riskFreeRate})" : $"SR({sharpePeriod})";
var name = CreateIndicatorName(symbol, baseBame, resolution);
IRiskFreeInterestRateModel riskFreeRateModel = riskFreeRate.HasValue
? new ConstantRiskFreeRateInterestRateModel(riskFreeRate.Value)
// Make it a function so it's lazily evaluated: SetRiskFreeInterestRateModel can be called after this method
: new FuncRiskFreeRateInterestRateModel((datetime) => RiskFreeInterestRateModel.GetInterestRate(datetime));
var sharpeRatio = new SharpeRatio(name, sharpePeriod, riskFreeRateModel);
InitializeIndicator(symbol, sharpeRatio, resolution, selector);

return sharpeRatio;
Expand Down
10 changes: 10 additions & 0 deletions Algorithm/QCAlgorithm.Python.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1148,6 +1148,16 @@ public void SetBrokerageModel(PyObject model)
SetBrokerageModel(brokerageModel);
}

/// <summary>
/// Sets the risk free interest rate model to be used in the algorithm
/// </summary>
/// <param name="model">The risk free interest rate model to use</param>
[DocumentationAttribute(Modeling)]
public void SetRiskFreeInterestRateModel(PyObject model)
{
SetRiskFreeInterestRateModel(RiskFreeInterestRateModelPythonWrapper.FromPyObject(model));
}

/// <summary>
/// Sets the security initializer function, used to initialize/configure securities after creation
/// </summary>
Expand Down
21 changes: 21 additions & 0 deletions Algorithm/QCAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ public QCAlgorithm()
SignalExport = new SignalExportManager(this);

BrokerageModel = new DefaultBrokerageModel();
RiskFreeInterestRateModel = new InterestRateProvider();
Notify = new NotificationManager(false); // Notification manager defaults to disabled.

//Initialise to unlocked:
Expand Down Expand Up @@ -337,6 +338,16 @@ public IBrokerageMessageHandler BrokerageMessageHandler
set;
}

/// <summary>
/// Gets the risk free interest rate model used to get the interest rates
/// </summary>
[DocumentationAttribute(Modeling)]
public IRiskFreeInterestRateModel RiskFreeInterestRateModel
{
get;
private set;
}

/// <summary>
/// Notification Manager for Sending Live Runtime Notifications to users about important events.
/// </summary>
Expand Down Expand Up @@ -1278,6 +1289,16 @@ public void SetBrokerageMessageHandler(IBrokerageMessageHandler handler)
BrokerageMessageHandler = handler ?? throw new ArgumentNullException(nameof(handler));
}

/// <summary>
/// Sets the risk free interest rate model to be used in the algorithm
/// </summary>
/// <param name="model">The risk free interest rate model to use</param>
[DocumentationAttribute(Modeling)]
public void SetRiskFreeInterestRateModel(IRiskFreeInterestRateModel model)
{
RiskFreeInterestRateModel = model ?? throw new ArgumentNullException(nameof(model));
}

/// <summary>
/// Sets the benchmark used for computing statistics of the algorithm to the specified symbol
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions AlgorithmFactory/Python/Wrappers/AlgorithmPythonWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ public IBrokerageMessageHandler BrokerageMessageHandler
/// </summary>
public BrokerageName BrokerageName => _baseAlgorithm.BrokerageName;

/// <summary>
/// Gets the risk free interest rate model used to get the interest rates
/// </summary>
public IRiskFreeInterestRateModel RiskFreeInterestRateModel => _baseAlgorithm.RiskFreeInterestRateModel;

/// <summary>
/// Debug messages from the strategy:
/// </summary>
Expand Down
45 changes: 45 additions & 0 deletions Common/Data/ConstantRiskFreeRateInterestRateModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;

namespace QuantConnect.Data
{
/// <summary>
/// Constant risk free rate interest rate model
/// </summary>
public class ConstantRiskFreeRateInterestRateModel : IRiskFreeInterestRateModel
{
private readonly decimal _riskFreeRate;

/// <summary>
/// Instantiates a <see cref="ConstantRiskFreeRateInterestRateModel"/> with the specified risk free rate
/// </summary>
public ConstantRiskFreeRateInterestRateModel(decimal riskFreeRate)
{
_riskFreeRate = riskFreeRate;
}

/// <summary>
/// Get interest rate by a given date
/// </summary>
/// <param name="date">The date</param>
/// <returns>Interest rate on the given date</returns>
public decimal GetInterestRate(DateTime date)
{
return _riskFreeRate;
}
}
}
45 changes: 45 additions & 0 deletions Common/Data/FuncRiskFreeRateInterestRateModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;

namespace QuantConnect.Data
{
/// <summary>
/// Constant risk free rate interest rate model
/// </summary>
public class FuncRiskFreeRateInterestRateModel : IRiskFreeInterestRateModel
{
private readonly Func<DateTime, decimal> _getInterestRateFunc;

/// <summary>
/// Create class instance of interest rate provider
/// </summary>
public FuncRiskFreeRateInterestRateModel(Func<DateTime, decimal> getInterestRateFunc)
{
_getInterestRateFunc = getInterestRateFunc;
}

/// <summary>
/// Get interest rate by a given date
/// </summary>
/// <param name="date">The date</param>
/// <returns>Interest rate on the given date</returns>
public decimal GetInterestRate(DateTime date)
{
return _getInterestRateFunc(date);
}
}
}
64 changes: 64 additions & 0 deletions Common/Data/IRiskFreeInterestRateModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;

namespace QuantConnect.Data
{
/// <summary>
/// Represents a model that provides risk free interest rate data
/// </summary>
public interface IRiskFreeInterestRateModel
{
/// <summary>
/// Get interest rate by a given date
/// </summary>
/// <param name="date">The date</param>
/// <returns>Interest rate on the given date</returns>
decimal GetInterestRate(DateTime date);
}

/// <summary>
/// Provide extension and static methods for <see cref="IRiskFreeInterestRateModel"/>
/// </summary>
public static class RiskFreeInterestRateModelExtensions
{
/// <summary>
/// Gets the average risk free annual return rate
/// </summary>
/// <param name="model">The interest rate model</param>
/// <param name="startDate">Start date to calculate the average</param>
/// <param name="endDate">End date to calculate the average</param>
public static decimal GetRiskFreeRate(this IRiskFreeInterestRateModel model, DateTime startDate, DateTime endDate)
{
return model.GetAverageRiskFreeRate(Time.EachDay(startDate, endDate));
}

/// <summary>
/// Gets the average Risk Free Rate from the interest rate of the given dates
/// </summary>
/// <param name="model">The interest rate model</param>
/// <param name="dates">
/// Collection of dates from which the interest rates will be computed and then the average of them
/// </param>
public static decimal GetAverageRiskFreeRate(this IRiskFreeInterestRateModel model, IEnumerable<DateTime> dates)
{
var interestRates = dates.Select(x => model.GetInterestRate(x)).DefaultIfEmpty(0);
return interestRates.Average();
}
}
}
54 changes: 35 additions & 19 deletions Common/Data/InterestRateProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,38 +28,54 @@ namespace QuantConnect.Data
/// <summary>
/// Fed US Primary Credit Rate at given date
/// </summary>
public class InterestRateProvider
public class InterestRateProvider : IRiskFreeInterestRateModel
{
private static readonly DateTime FirstInterestRateDate = new DateTime(1998, 1, 1);
private static readonly DateTime _firstInterestRateDate = new DateTime(1998, 1, 1);
private static DateTime _lastInterestRateDate;
private static Dictionary<DateTime, decimal> _riskFreeRateProvider;
private static readonly object _lock = new();

/// <summary>
/// Default Risk Free Rate of 1%
/// </summary>
public static decimal DefaultRiskFreeRate { get; } = 0.01m;

private DateTime _lastInterestRateDate;
private Dictionary<DateTime, decimal> _riskFreeRateProvider;
public static readonly decimal DefaultRiskFreeRate = 0.01m;

/// <summary>
/// Create class instance of interest rate provider
/// Lazily loads the interest rate provider from disk and returns it
/// </summary>
public InterestRateProvider()
private IReadOnlyDictionary<DateTime, decimal> RiskFreeRateProvider
{
LoadInterestRateProvider();
get
{
// let's not lock if the provider is already loaded
if (_riskFreeRateProvider != null)
{
return _riskFreeRateProvider;
}

lock (_lock)
{
if (_riskFreeRateProvider == null)
{
LoadInterestRateProvider();
}
return _riskFreeRateProvider;
}
}
}

/// <summary>
/// Get interest rate by a given datetime
/// Get interest rate by a given date
/// </summary>
/// <param name="dateTime"></param>
/// <returns>interest rate of the given date</returns>
public decimal GetInterestRate(DateTime dateTime)
/// <param name="date">The date</param>
/// <returns>Interest rate on the given date</returns>
public decimal GetInterestRate(DateTime date)
{
if (!_riskFreeRateProvider.TryGetValue(dateTime.Date, out var interestRate))
if (!RiskFreeRateProvider.TryGetValue(date.Date, out var interestRate))
{
return dateTime < FirstInterestRateDate
? _riskFreeRateProvider[FirstInterestRateDate]
: _riskFreeRateProvider[_lastInterestRateDate];
return date < _firstInterestRateDate
? RiskFreeRateProvider[_firstInterestRateDate]
: RiskFreeRateProvider[_lastInterestRateDate];
}

return interestRate;
Expand All @@ -77,7 +93,7 @@ protected void LoadInterestRateProvider()
_lastInterestRateDate = DateTime.UtcNow.Date;

// Sparse the discrete data points into continuous credit rate data for every day
for (var date = FirstInterestRateDate; date <= _lastInterestRateDate; date = date.AddDays(1))
for (var date = _firstInterestRateDate; date <= _lastInterestRateDate; date = date.AddDays(1))
{
if (!_riskFreeRateProvider.TryGetValue(date, out var currentRate))
{
Expand Down Expand Up @@ -151,7 +167,7 @@ public static bool TryParse(string csvLine, out DateTime date, out decimal inter
return false;
}

// Unit conversion from %
// Unit conversion from %
interestRate /= 100;
return true;
}
Expand Down
8 changes: 8 additions & 0 deletions Common/Interfaces/IAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,14 @@ BrokerageName BrokerageName
get;
}

/// <summary>
/// Gets the risk free interest rate model used to get the interest rates
/// </summary>
IRiskFreeInterestRateModel RiskFreeInterestRateModel
{
get;
}

/// <summary>
/// Gets the brokerage message handler used to decide what to do
/// with each message sent from the brokerage
Expand Down
Loading

0 comments on commit f8ca85c

Please sign in to comment.