Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flexible trading days per year property #7690

Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c96bb0c
feat: new `TradingDaysPerYear` prop in IAlgorithmSettings
Romazes Jan 10, 2024
99c3384
feat: getTradingDayPerYear by BrokerageModel
Romazes Jan 10, 2024
fcf21c6
remove: hardcoded value:252 in statistics
Romazes Jan 10, 2024
7ce7fbb
test: `algorithm.Settings.TradingDaysPerYear` for different brokerage
Romazes Jan 10, 2024
892a100
fix: Report generator by tradingDayPerYear
Romazes Jan 10, 2024
73a5b83
fix: hardcoded value in PortfolioStatisticsTests
Romazes Jan 10, 2024
b7d6853
fix: tradingDayPerYear in AnnualPerformanceTest
Romazes Jan 11, 2024
b2b96ef
feat: backwards compatibility for TradingDaysPerYear
Romazes Jan 11, 2024
287afbb
feat: several UTest for AlgoSetting's TradingDaysPerYear prop
Romazes Jan 11, 2024
af9f9ce
fix: existed algo + test
Romazes Jan 11, 2024
0507875
feat: regression algo with checking of tradingDayPerYear in OnEndOfAl…
Romazes Jan 11, 2024
e2f43a3
refactor: GetBrokerageTradingDayPerYear -> SetBrokerage... in BaseSet…
Romazes Jan 12, 2024
4f7c57e
remove: default valuine in test param
Romazes Jan 15, 2024
4a3a352
fix: missed init TradingDaysPerYear in report.portfolioAlgo
Romazes Jan 16, 2024
edd96c8
fix: nullable TradingDaysPerYear in AlgorithmConfiguration
Romazes Jan 16, 2024
0abf315
feat: test PortfolioStatistics with different tradingDaysPerYear
Romazes Jan 16, 2024
aea54e3
revert: PortfolioLooper with TradingDaysPerYear implementation
Romazes Jan 16, 2024
f6016c6
revert: nullable TradingDaysPerYear in AlgoConfiguration
Romazes Jan 16, 2024
11a7951
style: remove empty row
Romazes Jan 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions Algorithm.CSharp/CoinbaseCryptoYearMarketTradingRegressionAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* 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 QuantConnect.Data;
using QuantConnect.Brokerages;
using QuantConnect.Interfaces;
using System.Collections.Generic;
using QuantConnect.Securities.Crypto;

namespace QuantConnect.Algorithm.CSharp
{
public class CoinbaseCryptoYearMarketTradingRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
/// <summary>
/// The Average amount of day in year
/// </summary>
/// <remarks>Regardless of calendar</remarks>
private const int _daysInYear = 365;

/// <summary>
/// Flag prevents same order <see cref="Orders.OrderDirection"/>
/// </summary>
private bool _isBuy;

/// <summary>
/// Trading security
/// </summary>
private Crypto _BTCUSD;

/// <summary>
/// Initialize the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
/// </summary>
/// <remarks>In fact, you can assign custom value for <see cref="IAlgorithmSettings.TradingDaysPerYear"/></remarks>
public override void Initialize()
{
SetStartDate(2015, 01, 13);
SetEndDate(2016, 02, 03);

SetCash(100000);

// Setup brokerage for current algorithm
SetBrokerageModel(BrokerageName.Coinbase, AccountType.Cash);

_BTCUSD = AddCrypto("BTCUSD", Resolution.Daily, Market.Coinbase);
}

/// <summary>
/// Data Event Handler: receiving all subscription data in a single event
/// </summary>
/// <param name="slice">The current slice of data keyed by symbol string</param>
public override void OnData(Slice slice)
{
if (!_isBuy)
{
MarketOrder(_BTCUSD, 1);
_isBuy = true;
}
else
{
Liquidate();
_isBuy = false;
}
}

/// <summary>
/// End of algorithm run event handler. This method is called at the end of a backtest or live trading operation. Intended for closing out logs.
/// </summary>
public override void OnEndOfAlgorithm()
{
if (Settings.TradingDaysPerYear != _daysInYear)
{
throw new Exception("The Algorithm was using invalid `TradingDaysPerYear` for this brokerage. The ExpectedStatistics is wrong.");
}
}

/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally => true;

/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public Language[] Languages { get; } = { Language.CSharp };

/// <summary>
/// Data Points count of all time slices of algorithm
/// </summary>
public long DataPoints => 673;

/// <summary>
/// Data Points count of the algorithm history
/// </summary>
public int AlgorithmHistoryDataPoints => 43;

/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Trades", "388"},
{"Average Win", "0.01%"},
{"Average Loss", "-0.01%"},
{"Compounding Annual Return", "-0.597%"},
{"Drawdown", "0.700%"},
{"Expectancy", "-0.400"},
{"Net Profit", "-0.634%"},
{"Sharpe Ratio", "-7.126"},
{"Sortino Ratio", "-7.337"},
{"Probabilistic Sharpe Ratio", "0.000%"},
{"Loss Rate", "66%"},
{"Win Rate", "34%"},
{"Profit-Loss Ratio", "0.79"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0.002"},
{"Annual Variance", "0"},
{"Information Ratio", "-3.086"},
{"Tracking Error", "0.002"},
{"Treynor Ratio", "0"},
{"Total Fees", "$331.31"},
{"Estimated Strategy Capacity", "$71000.00"},
{"Lowest Capacity Asset", "BTCUSD 2XR"},
{"Portfolio Turnover", "0.29%"},
{"OrderListHash", "3833cb6d62095f129d078c8ccd57a0a6"}
};
}
}
14 changes: 7 additions & 7 deletions Algorithm.CSharp/FractionalQuantityRegressionAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,18 +115,18 @@ private void DataConsolidated(object sender, TradeBar e)
{"Drawdown", "5.500%"},
{"Expectancy", "1.339"},
{"Net Profit", "13.775%"},
{"Sharpe Ratio", "3.289"},
{"Sortino Ratio", "7.697"},
{"Probabilistic Sharpe Ratio", "61.758%"},
{"Sharpe Ratio", "4.906"},
{"Sortino Ratio", "11.482"},
{"Probabilistic Sharpe Ratio", "63.428%"},
{"Loss Rate", "33%"},
{"Win Rate", "67%"},
{"Profit-Loss Ratio", "2.51"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0.379"},
{"Annual Variance", "0.144"},
{"Information Ratio", "3.309"},
{"Tracking Error", "0.379"},
{"Annual Standard Deviation", "0.456"},
{"Annual Variance", "0.208"},
{"Information Ratio", "4.922"},
{"Tracking Error", "0.456"},
{"Treynor Ratio", "0"},
{"Total Fees", "$2650.41"},
{"Estimated Strategy Capacity", "$30000.00"},
Expand Down
13 changes: 11 additions & 2 deletions Common/AlgorithmConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,20 @@ public class AlgorithmConfiguration
[JsonConverter(typeof(DateTimeJsonConverter), DateFormat.UI)]
public DateTime EndDate;

/// <summary>
/// Number of trading days per year for Algorithm's portfolio statistics.
/// </summary>
[JsonProperty(PropertyName = "TradingDaysPerYear")]
public int TradingDaysPerYear;

/// <summary>
/// Initializes a new instance of the <see cref="AlgorithmConfiguration"/> class
/// </summary>
public AlgorithmConfiguration(string accountCurrency, BrokerageName brokerageName, AccountType accountType, IReadOnlyDictionary<string, string> parameters,
DateTime startDate, DateTime endDate, DateTime? outOfSampleMaxEndDate, int outOfSampleDays = 0)
DateTime startDate, DateTime endDate, DateTime? outOfSampleMaxEndDate, int outOfSampleDays = 0, int tradingDaysPerYear = 0)
{
OutOfSampleMaxEndDate = outOfSampleMaxEndDate;
TradingDaysPerYear = tradingDaysPerYear;
OutOfSampleDays = outOfSampleDays;
AccountCurrency = accountCurrency;
BrokerageName = brokerageName;
Expand Down Expand Up @@ -120,7 +127,9 @@ public static AlgorithmConfiguration Create(IAlgorithm algorithm, BacktestNodePa
algorithm.StartDate,
algorithm.EndDate,
backtestNodePacket?.OutOfSampleMaxEndDate,
backtestNodePacket?.OutOfSampleDays ?? 0);
backtestNodePacket?.OutOfSampleDays ?? 0,
// use value = 252 like default for backwards compatibility
algorithm?.Settings?.TradingDaysPerYear ?? 252);
}
}
}
13 changes: 13 additions & 0 deletions Common/AlgorithmSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,19 @@ public Resolution? WarmUpResolution
}
}

/// <summary>
/// Number of trading days per year for this Algorithm's portfolio statistics.
/// </summary>
/// <remarks>Effect on
/// <see cref="Statistics.PortfolioStatistics.AnnualVariance"/>,
/// <seealso cref="Statistics.PortfolioStatistics.AnnualStandardDeviation"/>,
/// <seealso cref="Statistics.PortfolioStatistics.SharpeRatio"/>,
/// <seealso cref="Statistics.PortfolioStatistics.SortinoRatio"/>,
/// <seealso cref="Statistics.PortfolioStatistics.TrackingError"/>,
/// <seealso cref="Statistics.PortfolioStatistics.InformationRatio"/>.
/// </remarks>
public int? TradingDaysPerYear { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="AlgorithmSettings"/> class
/// </summary>
Expand Down
13 changes: 13 additions & 0 deletions Common/Interfaces/IAlgorithmSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,18 @@ public interface IAlgorithmSettings
/// </summary>
/// <remarks>This allows improving the warmup speed by setting it to a lower resolution than the one added in the algorithm</remarks>
Resolution? WarmupResolution { get; set; }

/// <summary>
/// Number of trading days per year for this Algorithm's portfolio statistics.
/// </summary>
/// <remarks>Effect on
/// <see cref="Statistics.PortfolioStatistics.AnnualVariance"/>,
/// <seealso cref="Statistics.PortfolioStatistics.AnnualStandardDeviation"/>,
/// <seealso cref="Statistics.PortfolioStatistics.SharpeRatio"/>,
/// <seealso cref="Statistics.PortfolioStatistics.SortinoRatio"/>,
/// <seealso cref="Statistics.PortfolioStatistics.TrackingError"/>,
/// <seealso cref="Statistics.PortfolioStatistics.InformationRatio"/>.
/// </remarks>
int? TradingDaysPerYear { get; set; }
}
}
6 changes: 4 additions & 2 deletions Common/Statistics/AlgorithmPerformance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ public class AlgorithmPerformance
/// <param name="winningTransactions">Number of winning transactions</param>
/// <param name="losingTransactions">Number of losing transactions</param>
/// <param name="riskFreeInterestRateModel">The risk free interest rate model to use</param>
/// <param name="tradingDaysPerYear">The number of trading days per year</param>
public AlgorithmPerformance(
List<Trade> trades,
SortedDictionary<DateTime, decimal> profitLoss,
Expand All @@ -62,12 +63,13 @@ public AlgorithmPerformance(
decimal startingCapital,
int winningTransactions,
int losingTransactions,
IRiskFreeInterestRateModel riskFreeInterestRateModel)
IRiskFreeInterestRateModel riskFreeInterestRateModel,
int tradingDaysPerYear)
{

TradeStatistics = new TradeStatistics(trades);
PortfolioStatistics = new PortfolioStatistics(profitLoss, equity, portfolioTurnover, listPerformance, listBenchmark, startingCapital,
riskFreeInterestRateModel, winCount: winningTransactions, lossCount: losingTransactions);
riskFreeInterestRateModel, tradingDaysPerYear, winningTransactions, losingTransactions);
ClosedTrades = trades;
}

Expand Down
6 changes: 3 additions & 3 deletions Common/Statistics/PortfolioStatistics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public PortfolioStatistics(
List<double> listBenchmark,
decimal startingCapital,
IRiskFreeInterestRateModel riskFreeInterestRateModel,
int tradingDaysPerYear = 252,
int tradingDaysPerYear,
int? winCount = null,
int? lossCount = null)
{
Expand Down Expand Up @@ -273,7 +273,7 @@ public PortfolioStatistics(
TreynorRatio = Beta == 0 ? 0 : (annualPerformance - riskFreeRate) / Beta;

// deannualize a 1 sharpe ratio
var benchmarkSharpeRatio = 1.0d / Math.Sqrt(252);
var benchmarkSharpeRatio = 1.0d / Math.Sqrt(tradingDaysPerYear);
ProbabilisticSharpeRatio = Statistics.ProbabilisticSharpeRatio(listPerformance, benchmarkSharpeRatio).SafeDecimalCast();
}

Expand Down Expand Up @@ -313,7 +313,7 @@ private static decimal DrawdownPercent(SortedDictionary<DateTime, decimal> equit
/// <param name="tradingDaysPerYear">Trading days per year for the assets in portfolio</param>
/// <remarks>May be inaccurate for forex algorithms with more trading days in a year</remarks>
/// <returns>Double annual performance percentage</returns>
private static decimal GetAnnualPerformance(List<double> performance, int tradingDaysPerYear = 252)
private static decimal GetAnnualPerformance(List<double> performance, int tradingDaysPerYear)
{
return Statistics.AnnualPerformance(performance, tradingDaysPerYear).SafeDecimalCast();
}
Expand Down
16 changes: 8 additions & 8 deletions Common/Statistics/Statistics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public static decimal CompoundingAnnualPerformance(decimal startingCapital, deci
/// <param name="tradingDaysPerYear">Trading days per year for the assets in portfolio</param>
/// <remarks>May be unaccurate for forex algorithms with more trading days in a year</remarks>
/// <returns>Double annual performance percentage</returns>
public static double AnnualPerformance(List<double> performance, double tradingDaysPerYear = 252)
public static double AnnualPerformance(List<double> performance, double tradingDaysPerYear)
{
return Math.Pow((performance.Average() + 1), tradingDaysPerYear) - 1;
}
Expand All @@ -96,7 +96,7 @@ public static double AnnualPerformance(List<double> performance, double tradingD
/// <param name="tradingDaysPerYear"></param>
/// <remarks>Invokes the variance extension in the MathNet Statistics class</remarks>
/// <returns>Annual variance value</returns>
public static double AnnualVariance(List<double> performance, double tradingDaysPerYear = 252)
public static double AnnualVariance(List<double> performance, double tradingDaysPerYear)
{
var variance = performance.Variance();
return variance.IsNaNOrZero() ? 0 : variance * tradingDaysPerYear;
Expand All @@ -112,7 +112,7 @@ public static double AnnualVariance(List<double> performance, double tradingDays
/// Feasibly the trading days per year can be fetched from the dictionary of performance which includes the date-times to get the range; if is more than 1 year data.
/// </remarks>
/// <returns>Value for annual standard deviation</returns>
public static double AnnualStandardDeviation(List<double> performance, double tradingDaysPerYear = 252)
public static double AnnualStandardDeviation(List<double> performance, double tradingDaysPerYear)
{
return Math.Sqrt(AnnualVariance(performance, tradingDaysPerYear));
}
Expand All @@ -125,7 +125,7 @@ public static double AnnualStandardDeviation(List<double> performance, double tr
/// <param name="minimumAcceptableReturn">Minimum acceptable return</param>
/// <remarks>Invokes the variance extension in the MathNet Statistics class</remarks>
/// <returns>Annual variance value</returns>
public static double AnnualDownsideVariance(List<double> performance, double tradingDaysPerYear = 252, double minimumAcceptableReturn = 0)
public static double AnnualDownsideVariance(List<double> performance, double tradingDaysPerYear, double minimumAcceptableReturn = 0)
{
return AnnualVariance(performance.Where(ret => ret < minimumAcceptableReturn).ToList(), tradingDaysPerYear);
}
Expand All @@ -137,7 +137,7 @@ public static double AnnualDownsideVariance(List<double> performance, double tra
/// <param name="tradingDaysPerYear">Number of trading days for the assets in portfolio to get annualize standard deviation.</param>
/// <param name="minimumAcceptableReturn">Minimum acceptable return</param>
/// <returns>Value for annual downside standard deviation</returns>
public static double AnnualDownsideStandardDeviation(List<double> performance, double tradingDaysPerYear = 252, double minimumAcceptableReturn = 0)
public static double AnnualDownsideStandardDeviation(List<double> performance, double tradingDaysPerYear, double minimumAcceptableReturn = 0)
{
return Math.Sqrt(AnnualDownsideVariance(performance, tradingDaysPerYear, minimumAcceptableReturn));
}
Expand All @@ -150,7 +150,7 @@ public static double AnnualDownsideStandardDeviation(List<double> performance, d
/// <param name="benchmarkPerformance">Double collection of benchmark daily performance values</param>
/// <param name="tradingDaysPerYear">Number of trading days per year</param>
/// <returns>Value for tracking error</returns>
public static double TrackingError(List<double> algoPerformance, List<double> benchmarkPerformance, double tradingDaysPerYear = 252)
public static double TrackingError(List<double> algoPerformance, List<double> benchmarkPerformance, double tradingDaysPerYear)
{
// Un-equal lengths will blow up other statistics, but this will handle the case here
if (algoPerformance.Count() != benchmarkPerformance.Count())
Expand Down Expand Up @@ -201,7 +201,7 @@ public static decimal SharpeRatio(decimal averagePerformance, decimal standardDe
/// <param name="riskFreeRate">The risk free rate</param>
/// <param name="tradingDaysPerYear">Trading days per year for the assets in portfolio</param>
/// <returns>Value for sharpe ratio</returns>
public static double SharpeRatio(List<double> algoPerformance, double riskFreeRate, double tradingDaysPerYear=252)
public static double SharpeRatio(List<double> algoPerformance, double riskFreeRate, double tradingDaysPerYear)
{
return SharpeRatio(AnnualPerformance(algoPerformance, tradingDaysPerYear), AnnualStandardDeviation(algoPerformance, tradingDaysPerYear), riskFreeRate);
}
Expand All @@ -215,7 +215,7 @@ public static double SharpeRatio(List<double> algoPerformance, double riskFreeRa
/// <param name="tradingDaysPerYear">Trading days per year for the assets in portfolio</param>
/// <param name="minimumAcceptableReturn">Minimum acceptable return for Sortino ratio calculation</param>
/// <returns>Value for Sortino ratio</returns>
public static double SortinoRatio(List<double> algoPerformance, double riskFreeRate, double tradingDaysPerYear = 252, double minimumAcceptableReturn = 0)
public static double SortinoRatio(List<double> algoPerformance, double riskFreeRate, double tradingDaysPerYear, double minimumAcceptableReturn = 0)
{
return SharpeRatio(AnnualPerformance(algoPerformance, tradingDaysPerYear), AnnualDownsideStandardDeviation(algoPerformance, tradingDaysPerYear, minimumAcceptableReturn), riskFreeRate);
}
Expand Down
Loading
Loading