Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a62f16833 | ||
|
|
f1c50e41a5 | ||
|
|
7445f7ec5a | ||
|
|
8febb9ac45 | ||
|
|
463044b64a | ||
|
|
9865ce9815 | ||
|
|
81d7774c80 | ||
|
|
22e04913a5 | ||
|
|
8e5893b708 | ||
|
|
99558928f0 | ||
|
|
a2efee8be9 | ||
|
|
a1d0b6cde2 | ||
|
|
8123e76e3c | ||
|
|
a28d1f2ffe | ||
|
|
f4314549c3 | ||
|
|
d416580456 | ||
|
|
93ce62536f | ||
|
|
312392d6ae | ||
|
|
01734e1f61 | ||
|
|
124063a2a4 | ||
|
|
b99da54d5a | ||
|
|
bc5d51806d | ||
|
|
f3ed5b1206 | ||
|
|
eb6c5d20f5 | ||
|
|
b70b5bd5fe | ||
|
|
5ac7d00e24 | ||
|
|
469d960f50 | ||
|
|
7eea365aa1 | ||
|
|
760071a2f7 | ||
|
|
fa1ef4f763 | ||
|
|
f7b012aa11 | ||
|
|
725ad73240 | ||
|
|
7bbf42f79b | ||
|
|
f2da037d42 | ||
|
|
5c5c527252 | ||
|
|
71d09a3567 | ||
|
|
085476a222 | ||
|
|
c78774b99a | ||
|
|
d5b2e4d8cc | ||
|
|
828d9238b8 | ||
|
|
f2100adb46 | ||
|
|
159fa5a182 | ||
|
|
8ef0a4977b | ||
|
|
ffdb615a4d | ||
|
|
8f57a8c8ff | ||
|
|
a831adc9c8 | ||
|
|
6b22254924 | ||
|
|
0e61415ce2 | ||
|
|
16c893deb6 | ||
|
|
33048149af | ||
|
|
0a2c05ab1e | ||
|
|
e16b27f089 | ||
|
|
0a9dc2c71c | ||
|
|
7c9aa858c2 | ||
|
|
8fb70b20d0 | ||
|
|
809faa24de | ||
|
|
ad5aa263c0 | ||
|
|
a1bb907e03 | ||
|
|
71540f5015 | ||
|
|
a0055a3695 | ||
|
|
0b285df496 | ||
|
|
4d37096b3f | ||
|
|
7c42ea795f | ||
|
|
f2f1d06237 | ||
|
|
86fd80a31a | ||
|
|
c556d16775 |
6
.github/workflows/virtual-environments.yml
vendored
6
.github/workflows/virtual-environments.yml
vendored
@@ -48,10 +48,10 @@ jobs:
|
||||
dotnet test ./Tests/bin/Release/QuantConnect.Tests.dll --filter "FullyQualifiedName=QuantConnect.Tests.Python.PythonPackagesTests.TensorflowProbabilityTest" --blame-hang-timeout 120seconds --blame-crash && \
|
||||
# Run Hvplot Python Package Test
|
||||
dotnet test ./Tests/bin/Release/QuantConnect.Tests.dll --filter "FullyQualifiedName=QuantConnect.Tests.Python.PythonPackagesTests.HvplotTest" --blame-hang-timeout 120seconds --blame-crash && \
|
||||
# Run Keras Python Package Test
|
||||
dotnet test ./Tests/bin/Release/QuantConnect.Tests.dll --filter "FullyQualifiedName=QuantConnect.Tests.Python.PythonPackagesTests.KerasTest" --blame-hang-timeout 120seconds --blame-crash && \
|
||||
# Run Transformers
|
||||
dotnet test ./Tests/bin/Release/QuantConnect.Tests.dll --filter "FullyQualifiedName=QuantConnect.Tests.Python.PythonPackagesTests.Transformers" --blame-hang-timeout 120seconds --blame-crash && \
|
||||
dotnet test ./Tests/bin/Release/QuantConnect.Tests.dll --filter "FullyQualifiedName=QuantConnect.Tests.Python.PythonPackagesTests.XTransformers" --blame-hang-timeout 120seconds --blame-crash && \
|
||||
# Run Shap
|
||||
dotnet test ./Tests/bin/Release/QuantConnect.Tests.dll --filter "FullyQualifiedName=QuantConnect.Tests.Python.PythonPackagesTests.ShapTest" --blame-hang-timeout 120seconds --blame-crash
|
||||
dotnet test ./Tests/bin/Release/QuantConnect.Tests.dll --filter "FullyQualifiedName=QuantConnect.Tests.Python.PythonPackagesTests.ShapTest|KerasTest|PyvinecopulibTest" --blame-hang-timeout 120seconds --blame-crash && \
|
||||
dotnet test ./Tests/bin/Release/QuantConnect.Tests.dll --filter "FullyQualifiedName=QuantConnect.Tests.Python.PythonPackagesTests.Mlforecast" --blame-hang-timeout 120seconds --blame-crash && \
|
||||
dotnet test ./Tests/bin/Release/QuantConnect.Tests.dll --filter "FullyQualifiedName=QuantConnect.Tests.Python.PythonPackagesTests.MlxtendTest|Thinc" --blame-hang-timeout 120seconds --blame-crash
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 73;
|
||||
public long DataPoints => 76;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -164,7 +164,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 311879;
|
||||
public long DataPoints => 311881;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 12169;
|
||||
public long DataPoints => 12172;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -220,7 +220,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 608377;
|
||||
public long DataPoints => 608380;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American);
|
||||
AddOptionContract(option);
|
||||
|
||||
foreach (var symbol in new[] { option.Symbol, option.Underlying.Symbol })
|
||||
foreach (var symbol in new[] { option.Symbol, option.UnderlyingSymbol })
|
||||
{
|
||||
var config = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol).ToList();
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 713369;
|
||||
public long DataPoints => 713375;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 2217299;
|
||||
public long DataPoints => 2217328;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 133945;
|
||||
public long DataPoints => 133947;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 1190;
|
||||
public long DataPoints => 1199;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -117,7 +117,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public virtual long DataPoints => 12452;
|
||||
public virtual long DataPoints => 12455;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -134,33 +134,33 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public virtual Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "32"},
|
||||
{"Total Orders", "34"},
|
||||
{"Average Win", "0.33%"},
|
||||
{"Average Loss", "-0.04%"},
|
||||
{"Compounding Annual Return", "0.110%"},
|
||||
{"Compounding Annual Return", "0.106%"},
|
||||
{"Drawdown", "0.300%"},
|
||||
{"Expectancy", "0.184"},
|
||||
{"Expectancy", "0.178"},
|
||||
{"Start Equity", "1000000"},
|
||||
{"End Equity", "1001108"},
|
||||
{"Net Profit", "0.111%"},
|
||||
{"Sharpe Ratio", "-1.688"},
|
||||
{"Sortino Ratio", "-0.772"},
|
||||
{"Probabilistic Sharpe Ratio", "14.944%"},
|
||||
{"End Equity", "1001066.2"},
|
||||
{"Net Profit", "0.107%"},
|
||||
{"Sharpe Ratio", "-1.695"},
|
||||
{"Sortino Ratio", "-0.804"},
|
||||
{"Probabilistic Sharpe Ratio", "14.797%"},
|
||||
{"Loss Rate", "88%"},
|
||||
{"Win Rate", "12%"},
|
||||
{"Profit-Loss Ratio", "8.47"},
|
||||
{"Profit-Loss Ratio", "9.01"},
|
||||
{"Alpha", "-0.007"},
|
||||
{"Beta", "0.002"},
|
||||
{"Annual Standard Deviation", "0.004"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-1.353"},
|
||||
{"Tracking Error", "0.089"},
|
||||
{"Treynor Ratio", "-4.099"},
|
||||
{"Total Fees", "$72.00"},
|
||||
{"Treynor Ratio", "-4.112"},
|
||||
{"Total Fees", "$76.30"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", "ES VRJST036ZY0X"},
|
||||
{"Portfolio Turnover", "0.87%"},
|
||||
{"OrderListHash", "168731c8f3a19f230cc1410818b3b573"}
|
||||
{"Portfolio Turnover", "0.92%"},
|
||||
{"OrderListHash", "7afa589d648c3f24253cd59156a2014e"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 163415;
|
||||
public override long DataPoints => 163416;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 87289;
|
||||
public override long DataPoints => 87292;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
|
||||
@@ -14,14 +14,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Future;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
@@ -43,40 +36,40 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 14790;
|
||||
public override long DataPoints => 14884;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "36"},
|
||||
{"Total Orders", "32"},
|
||||
{"Average Win", "0.33%"},
|
||||
{"Average Loss", "-0.03%"},
|
||||
{"Compounding Annual Return", "0.102%"},
|
||||
{"Average Loss", "-0.04%"},
|
||||
{"Compounding Annual Return", "0.110%"},
|
||||
{"Drawdown", "0.300%"},
|
||||
{"Expectancy", "0.171"},
|
||||
{"Expectancy", "0.184"},
|
||||
{"Start Equity", "1000000"},
|
||||
{"End Equity", "1001024.4"},
|
||||
{"Net Profit", "0.102%"},
|
||||
{"Sharpe Ratio", "-1.702"},
|
||||
{"Sortino Ratio", "-0.836"},
|
||||
{"Probabilistic Sharpe Ratio", "14.653%"},
|
||||
{"Loss Rate", "89%"},
|
||||
{"Win Rate", "11%"},
|
||||
{"Profit-Loss Ratio", "9.54"},
|
||||
{"End Equity", "1001108"},
|
||||
{"Net Profit", "0.111%"},
|
||||
{"Sharpe Ratio", "-1.688"},
|
||||
{"Sortino Ratio", "-0.772"},
|
||||
{"Probabilistic Sharpe Ratio", "14.944%"},
|
||||
{"Loss Rate", "88%"},
|
||||
{"Win Rate", "12%"},
|
||||
{"Profit-Loss Ratio", "8.47"},
|
||||
{"Alpha", "-0.007"},
|
||||
{"Beta", "0.002"},
|
||||
{"Annual Standard Deviation", "0.004"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-1.353"},
|
||||
{"Tracking Error", "0.089"},
|
||||
{"Treynor Ratio", "-4.126"},
|
||||
{"Total Fees", "$80.60"},
|
||||
{"Treynor Ratio", "-4.099"},
|
||||
{"Total Fees", "$72.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", "ES VRJST036ZY0X"},
|
||||
{"Portfolio Turnover", "0.97%"},
|
||||
{"OrderListHash", "52c852d720692fab1e12212b2aba03d4"}
|
||||
{"Portfolio Turnover", "0.87%"},
|
||||
{"OrderListHash", "ef59fd5e4a7ae483a60d25736cf5d2d8"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 228834;
|
||||
public override long DataPoints => 228935;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
|
||||
159
Algorithm.CSharp/CallbackCommandRegressionAlgorithm.cs
Normal file
159
Algorithm.CSharp/CallbackCommandRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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.Commands;
|
||||
using QuantConnect.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm asserting the behavior of different callback commands call
|
||||
/// </summary>
|
||||
public class CallbackCommandRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 07);
|
||||
SetEndDate(2013, 10, 11);
|
||||
|
||||
AddEquity("SPY");
|
||||
AddEquity("BAC");
|
||||
AddEquity("IBM");
|
||||
AddCommand<BoolCommand>();
|
||||
AddCommand<VoidCommand>();
|
||||
|
||||
var potentialCommand = new VoidCommand
|
||||
{
|
||||
Target = new[] { "BAC" },
|
||||
Quantity = 10,
|
||||
Parameters = new() { { "tag", "Signal X" } }
|
||||
};
|
||||
var commandLink = Link(potentialCommand);
|
||||
Notify.Email("email@address", "Trade Command Event", $"Signal X trade\nFollow link to trigger: {commandLink}");
|
||||
|
||||
var commandLink2 = Link(new { Symbol = "SPY", Parameters = new Dictionary<string, int>() { { "Quantity", 10 } } });
|
||||
Notify.Email("email@address", "Untyped Command Event", $"Signal Y trade\nFollow link to trigger: {commandLink2}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle generic command callback
|
||||
/// </summary>
|
||||
public override bool? OnCommand(dynamic data)
|
||||
{
|
||||
Buy(data.Symbol, data.parameters["quantity"]);
|
||||
return true;
|
||||
}
|
||||
|
||||
private class VoidCommand : Command
|
||||
{
|
||||
public DateTime TargetTime { get; set; }
|
||||
public string[] Target { get; set; }
|
||||
public decimal Quantity { get; set; }
|
||||
public Dictionary<string, string> Parameters { get; set; }
|
||||
public override bool? Run(IAlgorithm algorithm)
|
||||
{
|
||||
if (TargetTime != algorithm.Time)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
((QCAlgorithm)algorithm).Order(Target[0], Quantity, tag: Parameters["tag"]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
private class BoolCommand : Command
|
||||
{
|
||||
public bool? Result { get; set; }
|
||||
public override bool? Run(IAlgorithm algorithm)
|
||||
{
|
||||
var shouldTrade = MyCustomMethod();
|
||||
if (shouldTrade.HasValue && shouldTrade.Value)
|
||||
{
|
||||
((QCAlgorithm)algorithm).Buy("IBM", 1);
|
||||
}
|
||||
return shouldTrade;
|
||||
}
|
||||
|
||||
private bool? MyCustomMethod()
|
||||
{
|
||||
return Result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 3943;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <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 Orders", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "271.453%"},
|
||||
{"Drawdown", "2.200%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "101691.92"},
|
||||
{"Net Profit", "1.692%"},
|
||||
{"Sharpe Ratio", "8.854"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "67.609%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "-0.005"},
|
||||
{"Beta", "0.996"},
|
||||
{"Annual Standard Deviation", "0.222"},
|
||||
{"Annual Variance", "0.049"},
|
||||
{"Information Ratio", "-14.565"},
|
||||
{"Tracking Error", "0.001"},
|
||||
{"Treynor Ratio", "1.97"},
|
||||
{"Total Fees", "$3.44"},
|
||||
{"Estimated Strategy Capacity", "$56000000.00"},
|
||||
{"Lowest Capacity Asset", "SPY R735QTJ8XC9X"},
|
||||
{"Portfolio Turnover", "19.93%"},
|
||||
{"OrderListHash", "3da9fa60bf95b9ed148b95e02e0cfc9e"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data.Market;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm asserts the consolidated US equity daily bars from the hour bars exactly matches
|
||||
/// the daily bars returned from the database
|
||||
/// </summary>
|
||||
public class ConsolidateHourBarsIntoDailyBarsRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _spy;
|
||||
private RelativeStrengthIndex _rsi;
|
||||
private RelativeStrengthIndex _rsiTimeDelta;
|
||||
private Dictionary<DateTime, decimal> _values = new();
|
||||
private int _count;
|
||||
private bool _indicatorsCompared;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 5, 1);
|
||||
SetEndDate(2020, 6, 5);
|
||||
|
||||
_spy = AddEquity("SPY", Resolution.Hour).Symbol;
|
||||
|
||||
// We will use these two indicators to compare the daily consolidated bars equals
|
||||
// the ones returned from the database. We use this specific type of indicator as
|
||||
// it depends on its previous values. Thus, if at some point the bars received by
|
||||
// the indicators differ, so will their final values
|
||||
_rsi = new RelativeStrengthIndex("FIRST", 15, MovingAverageType.Wilders);
|
||||
RegisterIndicator(_spy, _rsi, Resolution.Daily, selector: (bar) =>
|
||||
{
|
||||
var tradeBar = (TradeBar)bar;
|
||||
return (tradeBar.Close + tradeBar.Open) / 2;
|
||||
});
|
||||
|
||||
// We won't register this indicator as we will update it manually at the end of the
|
||||
// month, so that we can compare the values of the indicator that received consolidated
|
||||
// bars and the values of this one
|
||||
_rsiTimeDelta = new RelativeStrengthIndex("SECOND" ,15, MovingAverageType.Wilders);
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (IsWarmingUp) return;
|
||||
|
||||
if (slice.ContainsKey(_spy) && slice[_spy] != null)
|
||||
{
|
||||
if (Time.Month == EndDate.Month)
|
||||
{
|
||||
var history = History(_spy, _count, Resolution.Daily);
|
||||
foreach (var bar in history)
|
||||
{
|
||||
var time = bar.EndTime.Date;
|
||||
var average = (bar.Close + bar.Open) / 2;
|
||||
_rsiTimeDelta.Update(bar.EndTime, average);
|
||||
if (_rsiTimeDelta.Current.Value != _values[time])
|
||||
{
|
||||
throw new RegressionTestException($"Both {_rsi.Name} and {_rsiTimeDelta.Name} should have the same values, but they differ. {_rsi.Name}: {_values[time]} | {_rsiTimeDelta.Name}: {_rsiTimeDelta.Current.Value}");
|
||||
}
|
||||
}
|
||||
_indicatorsCompared = true;
|
||||
Quit();
|
||||
}
|
||||
else
|
||||
{
|
||||
_values[Time.Date] = _rsi.Current.Value;
|
||||
|
||||
// Since the symbol resolution is hour and the symbol is equity, we know the last bar received in a day will
|
||||
// be at the market close, this is 16h. We need to count how many daily bars were consolidated in order to know
|
||||
// how many we need to request from the history
|
||||
if (Time.Hour == 16)
|
||||
{
|
||||
_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (!_indicatorsCompared)
|
||||
{
|
||||
throw new RegressionTestException($"Indicators {_rsi.Name} and {_rsiTimeDelta.Name} should have been compared, but they were not. Please make sure the indicators are getting SPY data");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 290;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 20;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <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 Orders", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "100000"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-5.215"},
|
||||
{"Tracking Error", "0.159"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", ""},
|
||||
{"Portfolio Turnover", "0%"},
|
||||
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ using QuantConnect.Data.Market;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Future;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
@@ -31,69 +32,83 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
public class ConsolidateRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private List<int> _consolidationCounts;
|
||||
private List<int> _expectedConsolidationCounts;
|
||||
private List<SimpleMovingAverage> _smas;
|
||||
private List<DateTime> _lastSmaUpdates;
|
||||
private int _expectedConsolidations;
|
||||
private int _customDataConsolidator;
|
||||
private Symbol _symbol;
|
||||
private int _customDataConsolidatorCount;
|
||||
private Future _future;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 08);
|
||||
SetEndDate(2013, 10, 20);
|
||||
SetStartDate(2020, 01, 05);
|
||||
SetEndDate(2020, 01, 20);
|
||||
|
||||
var SP500 = QuantConnect.Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME);
|
||||
_symbol = FutureChainProvider.GetFutureContractList(SP500, StartDate).First();
|
||||
var security = AddFutureContract(_symbol);
|
||||
var symbol = FutureChainProvider.GetFutureContractList(SP500, StartDate).First();
|
||||
_future = AddFutureContract(symbol);
|
||||
|
||||
_consolidationCounts = Enumerable.Repeat(0, 9).ToList();
|
||||
var tradableDatesCount = QuantConnect.Time.EachTradeableDayInTimeZone(_future.Exchange.Hours,
|
||||
StartDate,
|
||||
EndDate,
|
||||
_future.Exchange.TimeZone,
|
||||
false).Count();
|
||||
_expectedConsolidationCounts = new(10);
|
||||
|
||||
Consolidate<QuoteBar>(symbol, time => new CalendarInfo(time.RoundDown(TimeSpan.FromDays(1)), TimeSpan.FromDays(1)),
|
||||
bar => UpdateQuoteBar(bar, 0));
|
||||
// The consolidator will respect the full 1 day bar span and will not consolidate the last tradable date,
|
||||
// since scan will not be called at 202/01/21 12am
|
||||
_expectedConsolidationCounts.Add(tradableDatesCount - 1);
|
||||
|
||||
Consolidate<QuoteBar>(symbol, time => new CalendarInfo(time.RoundDown(TimeSpan.FromDays(1)), TimeSpan.FromDays(1)),
|
||||
TickType.Quote, bar => UpdateQuoteBar(bar, 1));
|
||||
_expectedConsolidationCounts.Add(tradableDatesCount - 1);
|
||||
|
||||
Consolidate<QuoteBar>(symbol, TimeSpan.FromDays(1), bar => UpdateQuoteBar(bar, 2));
|
||||
_expectedConsolidationCounts.Add(tradableDatesCount - 1);
|
||||
|
||||
Consolidate(symbol, Resolution.Daily, TickType.Quote, (Action<QuoteBar>)(bar => UpdateQuoteBar(bar, 3)));
|
||||
_expectedConsolidationCounts.Add(tradableDatesCount);
|
||||
|
||||
Consolidate(symbol, TimeSpan.FromDays(1), bar => UpdateTradeBar(bar, 4));
|
||||
_expectedConsolidationCounts.Add(tradableDatesCount - 1);
|
||||
|
||||
Consolidate<TradeBar>(symbol, TimeSpan.FromDays(1), bar => UpdateTradeBar(bar, 5));
|
||||
_expectedConsolidationCounts.Add(tradableDatesCount - 1);
|
||||
|
||||
// Test using abstract T types, through defining a 'BaseData' handler
|
||||
|
||||
Consolidate(symbol, Resolution.Daily, null, (Action<BaseData>)(bar => UpdateBar(bar, 6)));
|
||||
_expectedConsolidationCounts.Add(tradableDatesCount);
|
||||
|
||||
Consolidate(symbol, TimeSpan.FromDays(1), null, (Action<BaseData>)(bar => UpdateBar(bar, 7)));
|
||||
_expectedConsolidationCounts.Add(tradableDatesCount - 1);
|
||||
|
||||
Consolidate(symbol, TimeSpan.FromDays(1), (Action<BaseData>)(bar => UpdateBar(bar, 8)));
|
||||
_expectedConsolidationCounts.Add(tradableDatesCount - 1);
|
||||
|
||||
_consolidationCounts = Enumerable.Repeat(0, _expectedConsolidationCounts.Count).ToList();
|
||||
_smas = _consolidationCounts.Select(_ => new SimpleMovingAverage(10)).ToList();
|
||||
_lastSmaUpdates = _consolidationCounts.Select(_ => DateTime.MinValue).ToList();
|
||||
|
||||
Consolidate<QuoteBar>(_symbol, time => new CalendarInfo(time.RoundDown(TimeSpan.FromDays(1)), TimeSpan.FromDays(1)),
|
||||
bar => UpdateQuoteBar(bar, 0));
|
||||
|
||||
Consolidate<QuoteBar>(_symbol, time => new CalendarInfo(time.RoundDown(TimeSpan.FromDays(1)), TimeSpan.FromDays(1)),
|
||||
TickType.Quote, bar => UpdateQuoteBar(bar, 1));
|
||||
|
||||
Consolidate<QuoteBar>(_symbol, TimeSpan.FromDays(1), bar => UpdateQuoteBar(bar, 2));
|
||||
|
||||
Consolidate(_symbol, Resolution.Daily, TickType.Quote, (Action<QuoteBar>)(bar => UpdateQuoteBar(bar, 3)));
|
||||
|
||||
Consolidate(_symbol, TimeSpan.FromDays(1), bar => UpdateTradeBar(bar, 4));
|
||||
|
||||
Consolidate<TradeBar>(_symbol, TimeSpan.FromDays(1), bar => UpdateTradeBar(bar, 5));
|
||||
|
||||
// custom data
|
||||
var symbol = AddData<CustomDataRegressionAlgorithm.Bitcoin>("BTC", Resolution.Minute).Symbol;
|
||||
Consolidate<TradeBar>(symbol, TimeSpan.FromDays(1), bar => _customDataConsolidator++);
|
||||
var customSecurity = AddData<CustomDataRegressionAlgorithm.Bitcoin>("BTC", Resolution.Minute);
|
||||
Consolidate<TradeBar>(customSecurity.Symbol, TimeSpan.FromDays(1), bar => _customDataConsolidatorCount++);
|
||||
|
||||
try
|
||||
{
|
||||
Consolidate<QuoteBar>(symbol, TimeSpan.FromDays(1), bar => { UpdateQuoteBar(bar, -1); });
|
||||
Consolidate<QuoteBar>(customSecurity.Symbol, TimeSpan.FromDays(1), bar => { UpdateQuoteBar(bar, -1); });
|
||||
throw new RegressionTestException($"Expected {nameof(ArgumentException)} to be thrown");
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
// will try to use BaseDataConsolidator for which input is TradeBars not QuoteBars
|
||||
}
|
||||
|
||||
// Test using abstract T types, through defining a 'BaseData' handler
|
||||
Consolidate(_symbol, Resolution.Daily, null, (Action<BaseData>)(bar => UpdateBar(bar, 6)));
|
||||
|
||||
Consolidate(_symbol, TimeSpan.FromDays(1), null, (Action<BaseData>)(bar => UpdateBar(bar, 7)));
|
||||
|
||||
Consolidate(_symbol, TimeSpan.FromDays(1), (Action<BaseData>)(bar => UpdateBar(bar, 8)));
|
||||
|
||||
_expectedConsolidations = QuantConnect.Time.EachTradeableDayInTimeZone(security.Exchange.Hours,
|
||||
StartDate,
|
||||
EndDate,
|
||||
security.Exchange.TimeZone,
|
||||
false).Count();
|
||||
}
|
||||
|
||||
private void UpdateBar(BaseData tradeBar, int position)
|
||||
{
|
||||
if (!(tradeBar is TradeBar))
|
||||
@@ -119,16 +134,27 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (_consolidationCounts.Any(i => i != _expectedConsolidations) || _customDataConsolidator == 0)
|
||||
for (var i = 0; i < _consolidationCounts.Count; i++)
|
||||
{
|
||||
throw new RegressionTestException("Unexpected consolidation count");
|
||||
var consolidationCount = _consolidationCounts[i];
|
||||
var expectedConsolidationCount = _expectedConsolidationCounts[i];
|
||||
|
||||
if (consolidationCount != expectedConsolidationCount)
|
||||
{
|
||||
throw new RegressionTestException($"Expected {expectedConsolidationCount} consolidations for consolidator {i} but received {consolidationCount}");
|
||||
}
|
||||
}
|
||||
|
||||
if (_customDataConsolidatorCount == 0)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected custom data consolidation count: {_customDataConsolidatorCount}");
|
||||
}
|
||||
|
||||
for (var i = 0; i < _smas.Count; i++)
|
||||
{
|
||||
if (_smas[i].Samples != _expectedConsolidations)
|
||||
if (_smas[i].Samples != _expectedConsolidationCounts[i])
|
||||
{
|
||||
throw new RegressionTestException($"Expected {_expectedConsolidations} samples in each SMA but found {_smas[i].Samples} in SMA in index {i}");
|
||||
throw new RegressionTestException($"Expected {_expectedConsolidationCounts} samples in each SMA but found {_smas[i].Samples} in SMA in index {i}");
|
||||
}
|
||||
|
||||
if (_smas[i].Current.Time != _lastSmaUpdates[i])
|
||||
@@ -144,9 +170,9 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
if (!Portfolio.Invested && _future.HasData)
|
||||
{
|
||||
SetHoldings(_symbol, 0.5);
|
||||
SetHoldings(_future.Symbol, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +189,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 12244;
|
||||
public long DataPoints => 14228;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -183,30 +209,30 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Orders", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "6636.699%"},
|
||||
{"Drawdown", "15.900%"},
|
||||
{"Compounding Annual Return", "665.524%"},
|
||||
{"Drawdown", "1.500%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "116177.7"},
|
||||
{"Net Profit", "16.178%"},
|
||||
{"Sharpe Ratio", "640.313"},
|
||||
{"End Equity", "109332.4"},
|
||||
{"Net Profit", "9.332%"},
|
||||
{"Sharpe Ratio", "9.805"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "99.824%"},
|
||||
{"Probabilistic Sharpe Ratio", "93.474%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "636.164"},
|
||||
{"Beta", "5.924"},
|
||||
{"Annual Standard Deviation", "1.012"},
|
||||
{"Annual Variance", "1.024"},
|
||||
{"Information Ratio", "696.123"},
|
||||
{"Tracking Error", "0.928"},
|
||||
{"Treynor Ratio", "109.404"},
|
||||
{"Total Fees", "$23.65"},
|
||||
{"Estimated Strategy Capacity", "$210000000.00"},
|
||||
{"Lowest Capacity Asset", "ES VMKLFZIH2MTD"},
|
||||
{"Portfolio Turnover", "81.19%"},
|
||||
{"OrderListHash", "dfd9a280d3c6470b305c03e0b72c234e"}
|
||||
{"Alpha", "3.164"},
|
||||
{"Beta", "0.957"},
|
||||
{"Annual Standard Deviation", "0.383"},
|
||||
{"Annual Variance", "0.146"},
|
||||
{"Information Ratio", "8.29"},
|
||||
{"Tracking Error", "0.379"},
|
||||
{"Treynor Ratio", "3.917"},
|
||||
{"Total Fees", "$15.05"},
|
||||
{"Estimated Strategy Capacity", "$2100000000.00"},
|
||||
{"Lowest Capacity Asset", "ES XCZJLC9NOB29"},
|
||||
{"Portfolio Turnover", "64.34%"},
|
||||
{"OrderListHash", "d814db6d5a9c97ee6de477ea06cd3834"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 713369;
|
||||
public long DataPoints => 713371;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -155,7 +155,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 723496;
|
||||
public long DataPoints => 723498;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 596356;
|
||||
public long DataPoints => 596358;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 19886;
|
||||
public long DataPoints => 19888;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Future;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm reproducing GH issue #8386 and other related bugs.
|
||||
/// It asserts that open positions are liquidated when a contract is delisted, even if the contract was added as an internal subscription.
|
||||
/// It also asserts that the contract is not tradable after being delisted.
|
||||
/// </summary>
|
||||
public class ContinuousFutureOpenPositionsLiquidationOnDelistingRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Future _continuousContract;
|
||||
private Symbol _prevContractSymbol;
|
||||
private bool _traded;
|
||||
private bool _mapped;
|
||||
private bool _delistedContractChecked;
|
||||
private DateTime _firstMappedContractRemovalTime;
|
||||
private int _removalCount;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 08);
|
||||
SetEndDate(2013, 12, 30);
|
||||
|
||||
_continuousContract = AddFuture(Futures.Indices.SP500EMini,
|
||||
dataNormalizationMode: DataNormalizationMode.BackwardsRatio,
|
||||
dataMappingMode: DataMappingMode.OpenInterest,
|
||||
contractDepthOffset: 0
|
||||
);
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (!_traded && _continuousContract.HasData)
|
||||
{
|
||||
var ticket = MarketOrder(_continuousContract.Mapped, 1);
|
||||
if (ticket.Status == OrderStatus.Invalid)
|
||||
{
|
||||
throw new RegressionTestException($"Order should be valid: {ticket}");
|
||||
}
|
||||
_traded = true;
|
||||
}
|
||||
|
||||
if (slice.SymbolChangedEvents.Count > 0)
|
||||
{
|
||||
foreach (var change in slice.SymbolChangedEvents.Values)
|
||||
{
|
||||
Debug($"[{Time}] :: Mapping: {change}");
|
||||
_prevContractSymbol = Symbol(change.OldSymbol);
|
||||
_mapped = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_delistedContractChecked &&
|
||||
_prevContractSymbol != null &&
|
||||
Time.Date > _prevContractSymbol.ID.Date &&
|
||||
IsMarketOpen(_prevContractSymbol))
|
||||
{
|
||||
_delistedContractChecked = true;
|
||||
var delistedContract = Securities.Total.Single(sec => sec.Symbol == _prevContractSymbol);
|
||||
|
||||
if (delistedContract.Invested)
|
||||
{
|
||||
throw new RegressionTestException($"Position should be closed when {_prevContractSymbol} got delisted {_prevContractSymbol.ID.Date}");
|
||||
}
|
||||
|
||||
if (!delistedContract.IsDelisted)
|
||||
{
|
||||
throw new RegressionTestException($"Contract should be delisted: {delistedContract.Symbol}");
|
||||
}
|
||||
|
||||
if (delistedContract.IsTradable)
|
||||
{
|
||||
throw new RegressionTestException($"Contract should not be tradable: {delistedContract.Symbol}");
|
||||
}
|
||||
|
||||
var ticket = MarketOrder(_prevContractSymbol, 1);
|
||||
|
||||
if (ticket.Status != OrderStatus.Invalid)
|
||||
{
|
||||
throw new RegressionTestException($"Delisted contract order should be invalid: {ticket}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSecuritiesChanged(SecurityChanges changes)
|
||||
{
|
||||
if (_prevContractSymbol != null)
|
||||
{
|
||||
if (changes.RemovedSecurities.Any(x => x.Symbol == _prevContractSymbol))
|
||||
{
|
||||
throw new RegressionTestException($"Previous contract symbol {_prevContractSymbol} should not be removed as a non-internal security");
|
||||
}
|
||||
|
||||
changes.FilterInternalSecurities = false;
|
||||
|
||||
if (!changes.RemovedSecurities.Any(x => x.Symbol == _prevContractSymbol))
|
||||
{
|
||||
throw new RegressionTestException($"Previous contract symbol {_prevContractSymbol} should be removed as an internal security");
|
||||
}
|
||||
|
||||
_firstMappedContractRemovalTime = Time;
|
||||
_removalCount++;
|
||||
}
|
||||
|
||||
changes.FilterInternalSecurities = false;
|
||||
Debug($"[{Time}] :: {changes}");
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
Debug($"[{Time}] :: Order event: {orderEvent}");
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (!_traded)
|
||||
{
|
||||
throw new RegressionTestException("No trades have been made");
|
||||
}
|
||||
|
||||
if (!_mapped)
|
||||
{
|
||||
throw new RegressionTestException("No mapping events have been fired");
|
||||
}
|
||||
|
||||
if (!_delistedContractChecked)
|
||||
{
|
||||
throw new RegressionTestException("No delisted contract has been checked");
|
||||
}
|
||||
|
||||
if (_prevContractSymbol == null)
|
||||
{
|
||||
throw new RegressionTestException("No previous contract symbol has been set");
|
||||
}
|
||||
|
||||
var tradedContract = Securities.Total.Single(sec => sec.Symbol == _prevContractSymbol);
|
||||
if (tradedContract.Invested)
|
||||
{
|
||||
throw new RegressionTestException($"Position should be closed when {_prevContractSymbol} got delisted on {_prevContractSymbol.ID.Date}");
|
||||
}
|
||||
|
||||
if (_firstMappedContractRemovalTime == default || _firstMappedContractRemovalTime >= _prevContractSymbol.ID.Date)
|
||||
{
|
||||
throw new RegressionTestException($"First mapped contract should have been removed before it's expiry date");
|
||||
}
|
||||
|
||||
if (_removalCount != 1)
|
||||
{
|
||||
throw new RegressionTestException($"The mapped contract should have been removed once only");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public virtual long DataPoints => 396945;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public virtual Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "2"},
|
||||
{"Average Win", "7.02%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "34.386%"},
|
||||
{"Drawdown", "1.500%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "107016.6"},
|
||||
{"Net Profit", "7.017%"},
|
||||
{"Sharpe Ratio", "3.217"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "99.828%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "100%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.227"},
|
||||
{"Beta", "0.109"},
|
||||
{"Annual Standard Deviation", "0.084"},
|
||||
{"Annual Variance", "0.007"},
|
||||
{"Information Ratio", "-1.122"},
|
||||
{"Tracking Error", "0.112"},
|
||||
{"Treynor Ratio", "2.49"},
|
||||
{"Total Fees", "$2.15"},
|
||||
{"Estimated Strategy Capacity", "$1700000000.00"},
|
||||
{"Lowest Capacity Asset", "ES VMKLFZIH2MTD"},
|
||||
{"Portfolio Turnover", "2.01%"},
|
||||
{"OrderListHash", "838e662caaa5a385c43ef27df1efbaf4"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ using QuantConnect.Data;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Data.Market;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Securities.Future;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
@@ -31,7 +30,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public class ContinuousFutureRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private List<SymbolChangedEvent> _mappings = new();
|
||||
private List<Symbol> _previousMappedContractSymbols = new();
|
||||
private Symbol _currentMappedSymbol;
|
||||
private Future _continuousContract;
|
||||
private DateTime _lastMonth;
|
||||
@@ -77,7 +76,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
if (changedEvent.Symbol == _continuousContract.Symbol)
|
||||
{
|
||||
_mappings.Add(changedEvent);
|
||||
_previousMappedContractSymbols.Add(Symbol(changedEvent.OldSymbol));
|
||||
Log($"{Time} - SymbolChanged event: {changedEvent}");
|
||||
|
||||
if (_currentMappedSymbol == _continuousContract.Mapped)
|
||||
@@ -144,15 +143,20 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
var expectedMappingCounts = 2;
|
||||
if (_mappings.Count != expectedMappingCounts)
|
||||
if (_previousMappedContractSymbols.Count != expectedMappingCounts)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected symbol changed events: {_mappings.Count}, was expecting {expectedMappingCounts}");
|
||||
throw new RegressionTestException($"Unexpected symbol changed events: {_previousMappedContractSymbols.Count}, was expecting {expectedMappingCounts}");
|
||||
}
|
||||
|
||||
var securities = Securities.Total.Where(sec => !sec.IsTradable && !sec.Symbol.IsCanonical() && sec.Symbol.SecurityType == SecurityType.Future).ToList();
|
||||
if (securities.Count != 1)
|
||||
var delistedSecurities = _previousMappedContractSymbols
|
||||
.Select(x => Securities.Total.Single(sec => sec.Symbol == x))
|
||||
.Where(x => x.Symbol.ID.Date < Time)
|
||||
.ToList();
|
||||
var markedDelistedSecurities = delistedSecurities.Where(x => x.IsDelisted && !x.IsTradable).ToList();
|
||||
if (markedDelistedSecurities.Count != delistedSecurities.Count)
|
||||
{
|
||||
throw new RegressionTestException($"We should have a single non tradable future contract security! found: {securities.Count}");
|
||||
throw new RegressionTestException($"Not all delisted contracts are properly market as delisted and non-tradable: " +
|
||||
$"only {markedDelistedSecurities.Count} are marked, was expecting {delistedSecurities.Count}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +173,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 713369;
|
||||
public long DataPoints => 713375;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -186,18 +190,18 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "3"},
|
||||
{"Average Win", "1.50%"},
|
||||
{"Total Orders", "4"},
|
||||
{"Average Win", "0.84%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "3.337%"},
|
||||
{"Compounding Annual Return", "3.380%"},
|
||||
{"Drawdown", "1.600%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "101666.4"},
|
||||
{"Net Profit", "1.666%"},
|
||||
{"Sharpe Ratio", "0.594"},
|
||||
{"Sortino Ratio", "0.198"},
|
||||
{"Probabilistic Sharpe Ratio", "44.801%"},
|
||||
{"End Equity", "101687.3"},
|
||||
{"Net Profit", "1.687%"},
|
||||
{"Sharpe Ratio", "0.605"},
|
||||
{"Sortino Ratio", "0.202"},
|
||||
{"Probabilistic Sharpe Ratio", "45.198%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "100%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
@@ -205,14 +209,14 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Beta", "0.134"},
|
||||
{"Annual Standard Deviation", "0.027"},
|
||||
{"Annual Variance", "0.001"},
|
||||
{"Information Ratio", "-2.69"},
|
||||
{"Information Ratio", "-2.687"},
|
||||
{"Tracking Error", "0.075"},
|
||||
{"Treynor Ratio", "0.119"},
|
||||
{"Treynor Ratio", "0.121"},
|
||||
{"Total Fees", "$6.45"},
|
||||
{"Estimated Strategy Capacity", "$8000000000.00"},
|
||||
{"Estimated Strategy Capacity", "$2600000000.00"},
|
||||
{"Lowest Capacity Asset", "ES VMKLFZIH2MTD"},
|
||||
{"Portfolio Turnover", "1.39%"},
|
||||
{"OrderListHash", "40c1137e0bc83b2bc920495af119c8fc"}
|
||||
{"Portfolio Turnover", "1.88%"},
|
||||
{"OrderListHash", "1287c3b983c5bac6491bb5ac296c4b55"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Future;
|
||||
using System;
|
||||
using QuantConnect.Util;
|
||||
using System.Linq;
|
||||
using NodaTime;
|
||||
using QuantConnect.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for regression algorithms testing that when a continuous future rollover happens,
|
||||
/// the continuous contract is updated correctly with the new contract data, regardless of the
|
||||
/// offset between the exchange time zone and the data time zone.
|
||||
/// </summary>
|
||||
public abstract class ContinuousFutureRolloverBaseRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
const string Ticker = Futures.Indices.SP500EMini;
|
||||
|
||||
private Future _continuousContract;
|
||||
|
||||
private DateTime _rolloverTime;
|
||||
|
||||
private MarketHoursDatabase.Entry _originalMhdbEntry;
|
||||
|
||||
protected abstract Resolution Resolution { get; }
|
||||
|
||||
protected abstract Offset ExchangeToDataTimeZoneOffset { get; }
|
||||
|
||||
private DateTimeZone DataTimeZone => TimeZones.Utc;
|
||||
|
||||
private DateTimeZone ExchangeTimeZone => DateTimeZone.ForOffset(ExchangeToDataTimeZoneOffset);
|
||||
|
||||
private bool RolloverHappened => _rolloverTime != DateTime.MinValue;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 8);
|
||||
SetEndDate(2013, 12, 20);
|
||||
|
||||
_originalMhdbEntry = MarketHoursDatabase.GetEntry(Market.CME, Ticker, SecurityType.Future);
|
||||
var exchangeHours = new SecurityExchangeHours(ExchangeTimeZone,
|
||||
_originalMhdbEntry.ExchangeHours.Holidays,
|
||||
_originalMhdbEntry.ExchangeHours.MarketHours.ToDictionary(),
|
||||
_originalMhdbEntry.ExchangeHours.EarlyCloses,
|
||||
_originalMhdbEntry.ExchangeHours.LateOpens);
|
||||
MarketHoursDatabase.SetEntry(Market.CME, Ticker, SecurityType.Future, exchangeHours, DataTimeZone);
|
||||
|
||||
SetTimeZone(ExchangeTimeZone);
|
||||
|
||||
_continuousContract = AddFuture(Ticker,
|
||||
Resolution,
|
||||
extendedMarketHours: true,
|
||||
dataNormalizationMode: DataNormalizationMode.Raw,
|
||||
dataMappingMode: DataMappingMode.OpenInterest,
|
||||
contractDepthOffset: 0
|
||||
);
|
||||
|
||||
SetBenchmark(x => 0);
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
try
|
||||
{
|
||||
var receivedRollover = false;
|
||||
foreach (var (symbol, symbolChangedEvent) in slice.SymbolChangedEvents)
|
||||
{
|
||||
if (RolloverHappened)
|
||||
{
|
||||
throw new RegressionTestException($"[{Time}] -- Unexpected symbol changed event for {symbol}. Expected only one mapping.");
|
||||
}
|
||||
|
||||
receivedRollover = true;
|
||||
_rolloverTime = symbolChangedEvent.EndTime;
|
||||
|
||||
var oldSymbol = symbolChangedEvent.OldSymbol;
|
||||
var newSymbol = symbolChangedEvent.NewSymbol;
|
||||
Debug($"[{Time}] -- Rollover: {oldSymbol} -> {newSymbol}");
|
||||
|
||||
if (symbol != _continuousContract.Symbol)
|
||||
{
|
||||
throw new RegressionTestException($"[{Time}] -- Unexpected symbol changed event for {symbol}");
|
||||
}
|
||||
|
||||
var expectedMappingDate = new DateTime(2013, 12, 18);
|
||||
if (_rolloverTime != expectedMappingDate)
|
||||
{
|
||||
throw new RegressionTestException($"[{Time}] -- Unexpected date {_rolloverTime}. Expected {expectedMappingDate}");
|
||||
}
|
||||
|
||||
var expectedMappingOldSymbol = "ES VMKLFZIH2MTD";
|
||||
var expectedMappingNewSymbol = "ES VP274HSU1AF5";
|
||||
if (symbolChangedEvent.OldSymbol != expectedMappingOldSymbol || symbolChangedEvent.NewSymbol != expectedMappingNewSymbol)
|
||||
{
|
||||
throw new RegressionTestException($"[{Time}] -- Unexpected mapping. " +
|
||||
$"Expected {expectedMappingOldSymbol} -> {expectedMappingNewSymbol} " +
|
||||
$"but was {symbolChangedEvent.OldSymbol} -> {symbolChangedEvent.NewSymbol}");
|
||||
}
|
||||
}
|
||||
|
||||
var mappedFuture = Securities[_continuousContract.Mapped];
|
||||
var mappedFuturePrice = mappedFuture.Price;
|
||||
|
||||
var otherFuture = Securities.Values.SingleOrDefault(x => !x.Symbol.IsCanonical() && x.Symbol != _continuousContract.Mapped);
|
||||
var otherFuturePrice = otherFuture?.Price;
|
||||
|
||||
var continuousContractPrice = _continuousContract.Price;
|
||||
|
||||
Debug($"[{Time}] Contracts prices:\n" +
|
||||
$" -- Mapped future: {mappedFuture.Symbol} :: {mappedFuture.Price} :: {mappedFuture.GetLastData()}\n" +
|
||||
$" -- Other future: {otherFuture?.Symbol} :: {otherFuture?.Price} :: {otherFuture?.GetLastData()}\n" +
|
||||
$" -- Mapped future from continuous contract: {_continuousContract.Symbol} :: {_continuousContract.Mapped} :: " +
|
||||
$"{_continuousContract.Price} :: {_continuousContract.GetLastData()}\n");
|
||||
|
||||
if (receivedRollover)
|
||||
{
|
||||
if (continuousContractPrice != otherFuturePrice)
|
||||
{
|
||||
var continuousContractLastData = _continuousContract.GetLastData();
|
||||
throw new RegressionTestException($"[{Time}] -- Prices do not match. " +
|
||||
$"At the time of the rollover, expected continuous future price to be the same as " +
|
||||
$"the previously mapped contract since no data for the new mapped contract has been received:\n" +
|
||||
$" Continuous contract ({_continuousContract.Symbol}) price: " +
|
||||
$"{continuousContractPrice} :: {continuousContractLastData.Symbol.Underlying} :: " +
|
||||
$"{continuousContractLastData.Time} - {continuousContractLastData.EndTime} :: {continuousContractLastData}. \n" +
|
||||
$" Mapped contract ({mappedFuture.Symbol}) price: {mappedFuturePrice} :: {mappedFuture.GetLastData()}. \n" +
|
||||
$" Other contract ({otherFuture?.Symbol}) price: {otherFuturePrice} :: {otherFuture?.GetLastData()}\n");
|
||||
}
|
||||
}
|
||||
else if (mappedFuturePrice != 0 || !RolloverHappened)
|
||||
{
|
||||
if (continuousContractPrice != mappedFuturePrice)
|
||||
{
|
||||
var continuousContractLastData = _continuousContract.GetLastData();
|
||||
throw new RegressionTestException($"[{Time}] -- Prices do not match. " +
|
||||
$"Expected continuous future price to be the same as the mapped contract:\n" +
|
||||
$" Continuous contract ({_continuousContract.Symbol}) price: {continuousContractPrice} :: " +
|
||||
$"{continuousContractLastData.Symbol.Underlying} :: {continuousContractLastData}. \n" +
|
||||
$" Mapped contract ({mappedFuture.Symbol}) price: {mappedFuturePrice} :: {mappedFuture.GetLastData()}. \n" +
|
||||
$" Other contract ({otherFuture?.Symbol}) price: {otherFuturePrice} :: {otherFuture?.GetLastData()}\n");
|
||||
}
|
||||
}
|
||||
// No data for the mapped future yet after rollover
|
||||
else
|
||||
{
|
||||
if (otherFuture == null)
|
||||
{
|
||||
throw new RegressionTestException($"[{Time}] --" +
|
||||
$" Mapped future price is 0 (no data has arrived) so the previous mapped contract is expected to be there");
|
||||
}
|
||||
|
||||
var continuousContractLastData = _continuousContract.GetLastData();
|
||||
|
||||
if (continuousContractLastData.EndTime > _rolloverTime)
|
||||
{
|
||||
throw new RegressionTestException($"[{Time}] -- Expected continuous future contract last data to be from the previously " +
|
||||
$"mapped contract until the new mapped contract gets data:\n" +
|
||||
$" Rollover time: {_rolloverTime}\n" +
|
||||
$" Continuous contract ({_continuousContract.Symbol}) last data: " +
|
||||
$"{continuousContractLastData.Symbol.Underlying} :: " +
|
||||
$"{continuousContractLastData.Time} - {continuousContractLastData.EndTime} :: {continuousContractLastData}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ResetMarketHoursDatabase();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
ResetMarketHoursDatabase();
|
||||
|
||||
if (!RolloverHappened)
|
||||
{
|
||||
throw new RegressionTestException($"[{Time}] -- Rollover did not happen.");
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetMarketHoursDatabase()
|
||||
{
|
||||
MarketHoursDatabase.SetEntry(Market.CME, Ticker, SecurityType.Future, _originalMhdbEntry.ExchangeHours, _originalMhdbEntry.DataTimeZone);
|
||||
}
|
||||
|
||||
/// <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 { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public virtual long DataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <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 Orders", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "100000"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", ""},
|
||||
{"Portfolio Turnover", "0%"},
|
||||
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
|
||||
};
|
||||
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 NodaTime;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for regression algorithms testing that when a continuous future rollover happens,
|
||||
/// the continuous contract is updated correctly with the new contract data.
|
||||
/// The algorithms asserts the behavior for the case when the exchange time zone is ahead of the data time zone.
|
||||
/// </summary>
|
||||
public class ContinuousFutureRolloverDailyExchangeTimeZoneAheadOfDataRegressionAlgorithm
|
||||
: ContinuousFutureRolloverBaseRegressionAlgorithm
|
||||
{
|
||||
protected override Resolution Resolution => Resolution.Daily;
|
||||
|
||||
protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(2);
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 1022;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 NodaTime;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for regression algorithms testing that when a continuous future rollover happens,
|
||||
/// the continuous contract is updated correctly with the new contract data.
|
||||
/// The algorithms asserts the behavior for the case when the exchange time zone is behind of the data time zone.
|
||||
/// </summary>
|
||||
public class ContinuousFutureRolloverDailyExchangeTimeZoneBehindOfDataRegressionAlgorithm
|
||||
: ContinuousFutureRolloverBaseRegressionAlgorithm
|
||||
{
|
||||
protected override Resolution Resolution => Resolution.Daily;
|
||||
|
||||
protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(-2);
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 1017;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 NodaTime;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for regression algorithms testing that when a continuous future rollover happens,
|
||||
/// the continuous contract is updated correctly with the new contract data.
|
||||
/// The algorithms asserts the behavior for the case when the data time zone is the same as the exchange time zone.
|
||||
/// </summary>
|
||||
public class ContinuousFutureRolloverDailyExchangeTimeZoneSameAsDataRegressionAlgorithm
|
||||
: ContinuousFutureRolloverBaseRegressionAlgorithm
|
||||
{
|
||||
protected override Resolution Resolution => Resolution.Daily;
|
||||
|
||||
protected override Offset ExchangeToDataTimeZoneOffset => Offset.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 1015;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 NodaTime;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for regression algorithms testing that when a continuous future rollover happens,
|
||||
/// the continuous contract is updated correctly with the new contract data.
|
||||
/// The algorithms asserts the behavior for the case when the exchange time zone is ahead of the data time zone.
|
||||
/// </summary>
|
||||
public class ContinuousFutureRolloverHourExchangeTimeZoneAheadOfDataRegressionAlgorithm
|
||||
: ContinuousFutureRolloverBaseRegressionAlgorithm
|
||||
{
|
||||
protected override Resolution Resolution => Resolution.Hour;
|
||||
|
||||
protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(2);
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 18850;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 NodaTime;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for regression algorithms testing that when a continuous future rollover happens,
|
||||
/// the continuous contract is updated correctly with the new contract data.
|
||||
/// The algorithms asserts the behavior for the case when the exchange time zone is behind of the data time zone.
|
||||
/// </summary>
|
||||
public class ContinuousFutureRolloverHourExchangeTimeZoneBehindOfDataRegressionAlgorithm
|
||||
: ContinuousFutureRolloverBaseRegressionAlgorithm
|
||||
{
|
||||
protected override Resolution Resolution => Resolution.Hour;
|
||||
|
||||
protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(-2);
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 18844;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 NodaTime;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for regression algorithms testing that when a continuous future rollover happens,
|
||||
/// the continuous contract is updated correctly with the new contract data.
|
||||
/// The algorithms asserts the behavior for the case when the data time zone is the same as the exchange time zone.
|
||||
/// </summary>
|
||||
public class ContinuousFutureRolloverHourExchangeTimeZoneSameAsDataRegressionAlgorithm
|
||||
: ContinuousFutureRolloverBaseRegressionAlgorithm
|
||||
{
|
||||
protected override Resolution Resolution => Resolution.Hour;
|
||||
|
||||
protected override Offset ExchangeToDataTimeZoneOffset => Offset.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 18846;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 NodaTime;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for regression algorithms testing that when a continuous future rollover happens,
|
||||
/// the continuous contract is updated correctly with the new contract data.
|
||||
/// The algorithms asserts the behavior for the case when the exchange time zone is ahead of the data time zone.
|
||||
/// </summary>
|
||||
public class ContinuousFutureRolloverMinuteExchangeTimeZoneAheadOfDataRegressionAlgorithm
|
||||
: ContinuousFutureRolloverBaseRegressionAlgorithm
|
||||
{
|
||||
protected override Resolution Resolution => Resolution.Minute;
|
||||
|
||||
protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(2);
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 1127376;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 NodaTime;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for regression algorithms testing that when a continuous future rollover happens,
|
||||
/// the continuous contract is updated correctly with the new contract data.
|
||||
/// The algorithms asserts the behavior for the case when the exchange time zone is behind of the data time zone.
|
||||
/// </summary>
|
||||
public class ContinuousFutureRolloverMinuteExchangeTimeZoneBehindOfDataRegressionAlgorithm
|
||||
: ContinuousFutureRolloverBaseRegressionAlgorithm
|
||||
{
|
||||
protected override Resolution Resolution => Resolution.Minute;
|
||||
|
||||
protected override Offset ExchangeToDataTimeZoneOffset => Offset.FromHours(-2);
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 1127374;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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 NodaTime;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for regression algorithms testing that when a continuous future rollover happens,
|
||||
/// the continuous contract is updated correctly with the new contract data.
|
||||
/// The algorithms asserts the behavior for the case when the data time zone is the same as the exchange time zone.
|
||||
/// </summary>
|
||||
public class ContinuousFutureRolloverMinuteExchangeTimeZoneSameAsDataRegressionAlgorithm
|
||||
: ContinuousFutureRolloverBaseRegressionAlgorithm
|
||||
{
|
||||
protected override Resolution Resolution => Resolution.Minute;
|
||||
|
||||
protected override Offset ExchangeToDataTimeZoneOffset => Offset.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 1127488;
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 1267;
|
||||
public long DataPoints => 1268;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
134
Algorithm.CSharp/CorrelationTypeComparisonRegressionAlgorithm.cs
Normal file
134
Algorithm.CSharp/CorrelationTypeComparisonRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.Collections.Generic;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Compares two correlation types and asserts they are not equal during the algorithm's execution.
|
||||
/// </summary>
|
||||
public class CorrelationTypeComparisonRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Correlation _correlationPearson;
|
||||
private Correlation _correlationSpearman;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise the data and resolution required, as well as the start-end dates for your algorithm. All algorithms must initialized.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 08); //Set Start Date
|
||||
SetEndDate(2013, 10, 17); //Set End Date
|
||||
var symbol = AddEquity("AAPL", Resolution.Daily).Symbol;
|
||||
var spy = AddEquity("SPY", Resolution.Daily).Symbol;
|
||||
_correlationPearson = C(symbol, spy, 5, CorrelationType.Pearson);
|
||||
_correlationSpearman = C(symbol, spy, 5, CorrelationType.Spearman);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
/// </summary>
|
||||
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (_correlationPearson.IsReady && _correlationSpearman.IsReady)
|
||||
{
|
||||
var pearsonValue = _correlationPearson.Current.Value;
|
||||
var spearmanValue = _correlationSpearman.Current.Value;
|
||||
|
||||
// Check that the correlation values are not the same
|
||||
if (pearsonValue == spearmanValue)
|
||||
{
|
||||
// Throw an exception if the correlation values are equal
|
||||
throw new RegressionTestException($"Error: Pearson and Spearman correlation values are the same: Pearson = {pearsonValue}, Spearman = {spearmanValue}. This should not happen.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 (!_correlationPearson.IsReady || !_correlationSpearman.IsReady)
|
||||
{
|
||||
throw new RegressionTestException("Error: Both correlation values should be ready at the end of the algorithm.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <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 List<Language> Languages { get; } = new() { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 80;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <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 Orders", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "100000"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-19.184"},
|
||||
{"Tracking Error", "0.138"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", ""},
|
||||
{"Portfolio Turnover", "0%"},
|
||||
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -40,8 +40,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2011, 9, 13);
|
||||
SetEndDate(2015, 12, 01);
|
||||
SetStartDate(2020, 01, 05);
|
||||
SetEndDate(2020, 01, 10);
|
||||
|
||||
//Set the cash for the strategy:
|
||||
SetCash(100000);
|
||||
@@ -109,7 +109,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 10491;
|
||||
public long DataPoints => 57;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -129,30 +129,30 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Orders", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "155.211%"},
|
||||
{"Drawdown", "84.800%"},
|
||||
{"Compounding Annual Return", "34781.071%"},
|
||||
{"Drawdown", "4.300%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "5223241.65"},
|
||||
{"Net Profit", "5123.242%"},
|
||||
{"Sharpe Ratio", "2.058"},
|
||||
{"Sortino Ratio", "2.492"},
|
||||
{"Probabilistic Sharpe Ratio", "68.833%"},
|
||||
{"End Equity", "110102.2"},
|
||||
{"Net Profit", "10.102%"},
|
||||
{"Sharpe Ratio", "283.719"},
|
||||
{"Sortino Ratio", "1123.876"},
|
||||
{"Probabilistic Sharpe Ratio", "81.716%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "1.724"},
|
||||
{"Beta", "0.043"},
|
||||
{"Annual Standard Deviation", "0.841"},
|
||||
{"Annual Variance", "0.707"},
|
||||
{"Information Ratio", "1.902"},
|
||||
{"Tracking Error", "0.848"},
|
||||
{"Treynor Ratio", "40.293"},
|
||||
{"Alpha", "184.11"},
|
||||
{"Beta", "-6.241"},
|
||||
{"Annual Standard Deviation", "0.635"},
|
||||
{"Annual Variance", "0.403"},
|
||||
{"Information Ratio", "260.511"},
|
||||
{"Tracking Error", "0.689"},
|
||||
{"Treynor Ratio", "-28.849"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", "BTC.Bitcoin 2S"},
|
||||
{"Portfolio Turnover", "0.06%"},
|
||||
{"OrderListHash", "999305e00ec9861f5ff261794e81213d"}
|
||||
{"Portfolio Turnover", "16.73%"},
|
||||
{"OrderListHash", "b890a8e73bf118e943ad2f2e712f12d0"}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -168,6 +168,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
public decimal High { get; set; }
|
||||
[JsonProperty("low")]
|
||||
public decimal Low { get; set; }
|
||||
public decimal Mid { get; set; }
|
||||
[JsonProperty("last")]
|
||||
public decimal Close { get; set; }
|
||||
[JsonProperty("bid")]
|
||||
@@ -217,7 +218,10 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
//return "http://my-ftp-server.com/futures-data-" + date.ToString("Ymd") + ".zip";
|
||||
// OR simply return a fixed small data file. Large files will slow down your backtest
|
||||
return new SubscriptionDataSource("https://www.quantconnect.com/api/v2/proxy/quandl/api/v3/datasets/BCHARTS/BITSTAMPUSD.csv?order=asc&api_key=WyAazVXnq7ATy_fefTqm", SubscriptionTransportMedium.RemoteFile);
|
||||
return new SubscriptionDataSource("https://www.quantconnect.com/api/v2/proxy/nasdaq/api/v3/datatables/QDL/BITFINEX.csv?code=BTCUSD&api_key=WyAazVXnq7ATy_fefTqm")
|
||||
{
|
||||
Sort = true
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -248,20 +252,20 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
}
|
||||
|
||||
//Example Line Format:
|
||||
//Date Open High Low Close Volume (BTC) Volume (Currency) Weighted Price
|
||||
//2011-09-13 5.8 6.0 5.65 5.97 58.37138238, 346.0973893944 5.929230648356
|
||||
// code date high low mid last bid ask volume
|
||||
// BTCUSD 2024-10-08 63248.0 61940.0 62246.5 62245.0 62246.0 62247.0 5.929230648356
|
||||
try
|
||||
{
|
||||
string[] data = line.Split(',');
|
||||
coin.Time = DateTime.Parse(data[0], CultureInfo.InvariantCulture);
|
||||
coin.Time = DateTime.Parse(data[1], CultureInfo.InvariantCulture);
|
||||
coin.EndTime = coin.Time.AddDays(1);
|
||||
coin.Open = Convert.ToDecimal(data[1], CultureInfo.InvariantCulture);
|
||||
coin.High = Convert.ToDecimal(data[2], CultureInfo.InvariantCulture);
|
||||
coin.Low = Convert.ToDecimal(data[3], CultureInfo.InvariantCulture);
|
||||
coin.Close = Convert.ToDecimal(data[4], CultureInfo.InvariantCulture);
|
||||
coin.VolumeBTC = Convert.ToDecimal(data[5], CultureInfo.InvariantCulture);
|
||||
coin.VolumeUSD = Convert.ToDecimal(data[6], CultureInfo.InvariantCulture);
|
||||
coin.WeightedPrice = Convert.ToDecimal(data[7], CultureInfo.InvariantCulture);
|
||||
coin.Mid = Convert.ToDecimal(data[4], CultureInfo.InvariantCulture);
|
||||
coin.Close = Convert.ToDecimal(data[5], CultureInfo.InvariantCulture);
|
||||
coin.Bid = Convert.ToDecimal(data[6], CultureInfo.InvariantCulture);
|
||||
coin.Ask = Convert.ToDecimal(data[7], CultureInfo.InvariantCulture);
|
||||
coin.VolumeBTC = Convert.ToDecimal(data[8], CultureInfo.InvariantCulture);
|
||||
coin.Value = coin.Close;
|
||||
}
|
||||
catch { /* Do nothing, skip first title row */ }
|
||||
|
||||
@@ -33,15 +33,15 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <meta name="tag" content="regression test" />
|
||||
public class CustomDataRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private bool _warmedUpChecked = false;
|
||||
private bool _warmedUpChecked;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2011, 9, 14);
|
||||
SetEndDate(2015, 12, 01);
|
||||
SetStartDate(2020, 01, 05);
|
||||
SetEndDate(2020, 01, 10);
|
||||
|
||||
//Set the cash for the strategy:
|
||||
SetCash(100000);
|
||||
@@ -110,7 +110,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 8943;
|
||||
public long DataPoints => 50;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -130,30 +130,30 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Orders", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "155.365%"},
|
||||
{"Drawdown", "84.800%"},
|
||||
{"Compounding Annual Return", "27587.925%"},
|
||||
{"Drawdown", "4.200%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "5223170.23"},
|
||||
{"Net Profit", "5123.170%"},
|
||||
{"Sharpe Ratio", "2.094"},
|
||||
{"Sortino Ratio", "2.535"},
|
||||
{"Probabilistic Sharpe Ratio", "69.967%"},
|
||||
{"End Equity", "109685"},
|
||||
{"Net Profit", "9.685%"},
|
||||
{"Sharpe Ratio", "238.834"},
|
||||
{"Sortino Ratio", "945.079"},
|
||||
{"Probabilistic Sharpe Ratio", "81.660%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "1.753"},
|
||||
{"Beta", "0.055"},
|
||||
{"Annual Standard Deviation", "0.84"},
|
||||
{"Annual Variance", "0.706"},
|
||||
{"Information Ratio", "1.942"},
|
||||
{"Tracking Error", "0.848"},
|
||||
{"Treynor Ratio", "32.18"},
|
||||
{"Alpha", "149.482"},
|
||||
{"Beta", "-6.002"},
|
||||
{"Annual Standard Deviation", "0.61"},
|
||||
{"Annual Variance", "0.371"},
|
||||
{"Information Ratio", "218.36"},
|
||||
{"Tracking Error", "0.664"},
|
||||
{"Treynor Ratio", "-24.253"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", "BTC.Bitcoin 2S"},
|
||||
{"Portfolio Turnover", "0.06%"},
|
||||
{"OrderListHash", "e69e78cd6fe7bc4627da2f51e25539d0"}
|
||||
{"Portfolio Turnover", "16.03%"},
|
||||
{"OrderListHash", "dde8821614d33c89e6e75c536447b7da"}
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -169,6 +169,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
public decimal High { get; set; }
|
||||
[JsonProperty("low")]
|
||||
public decimal Low { get; set; }
|
||||
public decimal Mid { get; set; }
|
||||
|
||||
[JsonProperty("last")]
|
||||
public decimal Close { get; set; }
|
||||
[JsonProperty("bid")]
|
||||
@@ -179,7 +181,6 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
public decimal WeightedPrice { get; set; }
|
||||
[JsonProperty("volume")]
|
||||
public decimal VolumeBTC { get; set; }
|
||||
public decimal VolumeUSD { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The end time of this data. Some data covers spans (trade bars)
|
||||
@@ -218,7 +219,10 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
//return "http://my-ftp-server.com/futures-data-" + date.ToString("Ymd") + ".zip";
|
||||
// OR simply return a fixed small data file. Large files will slow down your backtest
|
||||
return new SubscriptionDataSource("https://www.quantconnect.com/api/v2/proxy/quandl/api/v3/datasets/BCHARTS/BITSTAMPUSD.csv?order=asc&api_key=WyAazVXnq7ATy_fefTqm", SubscriptionTransportMedium.RemoteFile);
|
||||
return new SubscriptionDataSource("https://www.quantconnect.com/api/v2/proxy/nasdaq/api/v3/datatables/QDL/BITFINEX.csv?code=BTCUSD&api_key=qAWKpUfmSVFnU3bRQwKy")
|
||||
{
|
||||
Sort = true
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -249,20 +253,20 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
}
|
||||
|
||||
//Example Line Format:
|
||||
//Date Open High Low Close Volume (BTC) Volume (Currency) Weighted Price
|
||||
//2011-09-13 5.8 6.0 5.65 5.97 58.37138238, 346.0973893944 5.929230648356
|
||||
// code date high low mid last bid ask volume
|
||||
// BTCUSD 2024-10-08 63248.0 61940.0 62246.5 62245.0 62246.0 62247.0 477.91102114
|
||||
try
|
||||
{
|
||||
string[] data = line.Split(',');
|
||||
coin.Time = DateTime.Parse(data[0], CultureInfo.InvariantCulture);
|
||||
coin.Time = DateTime.Parse(data[1], CultureInfo.InvariantCulture);
|
||||
coin.EndTime = coin.Time.AddDays(1);
|
||||
coin.Open = Convert.ToDecimal(data[1], CultureInfo.InvariantCulture);
|
||||
coin.High = Convert.ToDecimal(data[2], CultureInfo.InvariantCulture);
|
||||
coin.Low = Convert.ToDecimal(data[3], CultureInfo.InvariantCulture);
|
||||
coin.Close = Convert.ToDecimal(data[4], CultureInfo.InvariantCulture);
|
||||
coin.VolumeBTC = Convert.ToDecimal(data[5], CultureInfo.InvariantCulture);
|
||||
coin.VolumeUSD = Convert.ToDecimal(data[6], CultureInfo.InvariantCulture);
|
||||
coin.WeightedPrice = Convert.ToDecimal(data[7], CultureInfo.InvariantCulture);
|
||||
coin.Mid = Convert.ToDecimal(data[4], CultureInfo.InvariantCulture);
|
||||
coin.Close = Convert.ToDecimal(data[5], CultureInfo.InvariantCulture);
|
||||
coin.Bid = Convert.ToDecimal(data[6], CultureInfo.InvariantCulture);
|
||||
coin.Ask = Convert.ToDecimal(data[7], CultureInfo.InvariantCulture);
|
||||
coin.VolumeBTC = Convert.ToDecimal(data[8], CultureInfo.InvariantCulture);
|
||||
coin.Value = coin.Close;
|
||||
}
|
||||
catch { /* Do nothing, skip first title row */ }
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 1679;
|
||||
public override long DataPoints => 1681;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public virtual long DataPoints => 525809;
|
||||
public virtual long DataPoints => 525811;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using QuantConnect.Data;
|
||||
using System.Globalization;
|
||||
using QuantConnect.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// The regression algorithm showcases the utilization of a custom data source with the Sort flag set to true.
|
||||
/// This means that the source initially provides data in descending order, which is then organized into ascending order and returned in the <see cref="OnData(Slice)"/> function.
|
||||
/// </summary>
|
||||
public class DescendingCustomDataObjectStoreRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
protected virtual string CustomDataKey => "CustomData/SortCustomData";
|
||||
|
||||
protected readonly static string[] descendingCustomData = new string[]
|
||||
{
|
||||
"2024-10-03 19:00:00,173.5,176.0,172.0,175.2,120195681,4882.29",
|
||||
"2024-10-02 18:00:00,174.0,177.0,173.0,175.8,116275729,4641.97",
|
||||
"2024-10-01 17:00:00,175.0,178.0,172.5,174.5,127707078,6591.27",
|
||||
"2024-09-30 11:00:00,174.8,176.5,172.8,175.0,127707078,6591.27",
|
||||
"2024-09-27 10:00:00,172.5,175.0,171.5,173.5,120195681,4882.29",
|
||||
"2024-09-26 09:00:00,171.0,172.5,170.0,171.8,117516350,4820.53",
|
||||
"2024-09-25 08:00:00,169.5,172.0,169.0,171.0,110427867,4661.55",
|
||||
"2024-09-24 07:00:00,170.0,171.0,168.0,169.5,127624733,4823.52",
|
||||
"2024-09-23 06:00:00,172.0,173.5,169.5,171.5,123586417,4303.93",
|
||||
"2024-09-20 05:00:00,168.0,171.0,167.5,170.5,151929179,5429.87",
|
||||
"2024-09-19 04:00:00,170.5,171.5,166.0,167.0,160523863,5219.24",
|
||||
"2024-09-18 03:00:00,173.0,174.0,169.0,172.0,145721790,5163.09",
|
||||
"2024-09-17 02:00:00,171.0,173.5,170.0,172.5,144794030,5405.72",
|
||||
"2024-09-16 01:00:00,168.0,171.0,167.0,170.0,214402430,8753.33",
|
||||
"2024-09-13 16:00:00,173.5,176.0,172.0,175.2,120195681,4882.29",
|
||||
"2024-09-12 15:00:00,174.5,177.5,173.5,176.5,171728134,7774.83",
|
||||
"2024-09-11 14:00:00,175.0,178.0,174.0,175.5,191516153,8349.59",
|
||||
"2024-09-10 13:00:00,174.5,176.0,173.0,174.0,151162819,5915.8",
|
||||
"2024-09-09 12:00:00,176.0,178.0,175.0,177.0,116275729,4641.97"
|
||||
};
|
||||
|
||||
private Symbol _customSymbol;
|
||||
|
||||
private List<SortCustomData> _receivedData = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2024, 09, 09);
|
||||
SetEndDate(2024, 10, 03);
|
||||
SetCash(100000);
|
||||
|
||||
SetBenchmark(x => 0);
|
||||
|
||||
SortCustomData.CustomDataKey = CustomDataKey;
|
||||
|
||||
_customSymbol = AddData<SortCustomData>("SortCustomData", Resolution.Daily).Symbol;
|
||||
|
||||
// Saving data here for demonstration and regression testing purposes.
|
||||
// In real scenarios, data has to be saved to the object store before the algorithm starts.
|
||||
ObjectStore.Save(CustomDataKey, string.Join("\n", descendingCustomData));
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (slice.ContainsKey(_customSymbol))
|
||||
{
|
||||
var sortCustomData = slice.Get<SortCustomData>(_customSymbol);
|
||||
if (sortCustomData.Open == 0 || sortCustomData.High == 0 || sortCustomData.Low == 0 || sortCustomData.Close == 0 || sortCustomData.Price == 0)
|
||||
{
|
||||
throw new RegressionTestException("One or more custom data fields (Open, High, Low, Close, Price) are zero.");
|
||||
}
|
||||
|
||||
_receivedData.Add(sortCustomData);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (_receivedData.Count == 0)
|
||||
{
|
||||
throw new RegressionTestException("Custom data was not fetched");
|
||||
}
|
||||
|
||||
var history = History<SortCustomData>(_customSymbol, StartDate, EndDate, Resolution.Hour).ToList();
|
||||
|
||||
if (history.Count != _receivedData.Count)
|
||||
{
|
||||
throw new RegressionTestException("History request returned different data than expected");
|
||||
}
|
||||
|
||||
// Iterate through the history collection, checking if the EndTime is in ascending order.
|
||||
for (int i = 0; i < history.Count - 1; i++)
|
||||
{
|
||||
if (history[i].EndTime > history[i + 1].EndTime)
|
||||
{
|
||||
throw new RegressionTestException($"Order failure: {history[i].EndTime} > {history[i + 1].EndTime} at index {i}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <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 List<Language> Languages => new() { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all TimeSlices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 20;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 19;
|
||||
|
||||
/// <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 ()
|
||||
{
|
||||
{"Total Orders", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "100000"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", ""},
|
||||
{"Portfolio Turnover", "0%"},
|
||||
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
|
||||
};
|
||||
}
|
||||
|
||||
public class SortCustomData : BaseData
|
||||
{
|
||||
public static string CustomDataKey { get; set; }
|
||||
|
||||
public decimal Open { get; set; }
|
||||
public decimal High { get; set; }
|
||||
public decimal Low { get; set; }
|
||||
public decimal Close { get; set; }
|
||||
|
||||
public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
|
||||
{
|
||||
return new SubscriptionDataSource(CustomDataKey, SubscriptionTransportMedium.ObjectStore, FileFormat.Csv)
|
||||
{
|
||||
// Indicate that the data from the subscription will be returned in descending order.
|
||||
Sort = true
|
||||
};
|
||||
}
|
||||
|
||||
public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
|
||||
{
|
||||
var csv = line.Split(",");
|
||||
var data = new SortCustomData()
|
||||
{
|
||||
Symbol = config.Symbol,
|
||||
Time = DateTime.ParseExact(csv[0], DateFormat.DB, CultureInfo.InvariantCulture),
|
||||
Value = csv[4].ToDecimal(),
|
||||
Open = csv[1].ToDecimal(),
|
||||
High = csv[2].ToDecimal(),
|
||||
Low = csv[3].ToDecimal(),
|
||||
Close = csv[4].ToDecimal()
|
||||
};
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 41466;
|
||||
public override long DataPoints => 41467;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public virtual long DataPoints => 32071;
|
||||
public virtual long DataPoints => 32073;
|
||||
|
||||
/// </summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 103816;
|
||||
public override long DataPoints => 103818;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
|
||||
@@ -112,7 +112,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 309280;
|
||||
public long DataPoints => 309282;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -150,7 +150,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0.005"},
|
||||
{"Information Ratio", "-0.134"},
|
||||
{"Tracking Error", "0.385"},
|
||||
{"Treynor Ratio", "3.784"},
|
||||
{"Treynor Ratio", "3.785"},
|
||||
{"Total Fees", "$2.84"},
|
||||
{"Estimated Strategy Capacity", "$120000000.00"},
|
||||
{"Lowest Capacity Asset", "ES XFH59UPBIJ7O|ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -200,7 +200,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 212195;
|
||||
public long DataPoints => 212196;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -238,7 +238,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0.004"},
|
||||
{"Information Ratio", "-0.226"},
|
||||
{"Tracking Error", "0.378"},
|
||||
{"Treynor Ratio", "-21.838"},
|
||||
{"Treynor Ratio", "-21.841"},
|
||||
{"Total Fees", "$1.42"},
|
||||
{"Estimated Strategy Capacity", "$120000000.00"},
|
||||
{"Lowest Capacity Asset", "ES XFH59UPBIJ7O|ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -165,7 +165,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 212195;
|
||||
public long DataPoints => 212196;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -203,7 +203,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0.098"},
|
||||
{"Information Ratio", "-0.649"},
|
||||
{"Tracking Error", "0.483"},
|
||||
{"Treynor Ratio", "-18.589"},
|
||||
{"Treynor Ratio", "-18.59"},
|
||||
{"Total Fees", "$7.10"},
|
||||
{"Estimated Strategy Capacity", "$24000000.00"},
|
||||
{"Lowest Capacity Asset", "ES XFH59UPBIJ7O|ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 212195;
|
||||
public long DataPoints => 212196;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -214,7 +214,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0.003"},
|
||||
{"Information Ratio", "-0.198"},
|
||||
{"Tracking Error", "0.377"},
|
||||
{"Treynor Ratio", "-23.06"},
|
||||
{"Treynor Ratio", "-23.065"},
|
||||
{"Total Fees", "$1.42"},
|
||||
{"Estimated Strategy Capacity", "$180000000.00"},
|
||||
{"Lowest Capacity Asset", "ES XFH59UPHGV9G|ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 24378;
|
||||
public long DataPoints => 24379;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 212195;
|
||||
public long DataPoints => 212196;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -239,7 +239,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0.003"},
|
||||
{"Information Ratio", "-0.248"},
|
||||
{"Tracking Error", "0.377"},
|
||||
{"Treynor Ratio", "-36.295"},
|
||||
{"Treynor Ratio", "-36.3"},
|
||||
{"Total Fees", "$1.42"},
|
||||
{"Estimated Strategy Capacity", "$79000000.00"},
|
||||
{"Lowest Capacity Asset", "ES 31EL5FAOOQON8|ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -174,7 +174,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 212195;
|
||||
public long DataPoints => 212196;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -212,7 +212,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0.003"},
|
||||
{"Information Ratio", "-0.249"},
|
||||
{"Tracking Error", "0.377"},
|
||||
{"Treynor Ratio", "-32.551"},
|
||||
{"Treynor Ratio", "-32.556"},
|
||||
{"Total Fees", "$1.42"},
|
||||
{"Estimated Strategy Capacity", "$290000000.00"},
|
||||
{"Lowest Capacity Asset", "ES 31EL5FBZBMXES|ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -185,7 +185,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 212195;
|
||||
public long DataPoints => 212196;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -223,7 +223,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0.002"},
|
||||
{"Information Ratio", "0.064"},
|
||||
{"Tracking Error", "0.378"},
|
||||
{"Treynor Ratio", "-13.132"},
|
||||
{"Treynor Ratio", "-13.127"},
|
||||
{"Total Fees", "$1.42"},
|
||||
{"Estimated Strategy Capacity", "$13000000.00"},
|
||||
{"Lowest Capacity Asset", "ES XFH59UP5K75W|ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -168,7 +168,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 212195;
|
||||
public long DataPoints => 212196;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -194,7 +194,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "101736.08"},
|
||||
{"Net Profit", "1.736%"},
|
||||
{"Sharpe Ratio", "0.597"},
|
||||
{"Sharpe Ratio", "0.596"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "49.736%"},
|
||||
{"Loss Rate", "0%"},
|
||||
@@ -206,7 +206,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0.001"},
|
||||
{"Information Ratio", "0.009"},
|
||||
{"Tracking Error", "0.375"},
|
||||
{"Treynor Ratio", "-11.057"},
|
||||
{"Treynor Ratio", "-11.048"},
|
||||
{"Total Fees", "$1.42"},
|
||||
{"Estimated Strategy Capacity", "$100000000.00"},
|
||||
{"Lowest Capacity Asset", "ES XFH59UPNF7B8|ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -182,7 +182,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 212195;
|
||||
public long DataPoints => 212196;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -220,7 +220,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0.001"},
|
||||
{"Information Ratio", "0.058"},
|
||||
{"Tracking Error", "0.375"},
|
||||
{"Treynor Ratio", "-41.846"},
|
||||
{"Treynor Ratio", "-41.831"},
|
||||
{"Total Fees", "$1.42"},
|
||||
{"Estimated Strategy Capacity", "$12000000.00"},
|
||||
{"Lowest Capacity Asset", "ES 31EL5FAOUP0P0|ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -167,7 +167,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 212195;
|
||||
public long DataPoints => 212196;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -193,7 +193,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "103423.58"},
|
||||
{"Net Profit", "3.424%"},
|
||||
{"Sharpe Ratio", "1.089"},
|
||||
{"Sharpe Ratio", "1.088"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "73.287%"},
|
||||
{"Loss Rate", "0%"},
|
||||
@@ -205,7 +205,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0.001"},
|
||||
{"Information Ratio", "0.075"},
|
||||
{"Tracking Error", "0.377"},
|
||||
{"Treynor Ratio", "-24.083"},
|
||||
{"Treynor Ratio", "-24.076"},
|
||||
{"Total Fees", "$1.42"},
|
||||
{"Estimated Strategy Capacity", "$110000000.00"},
|
||||
{"Lowest Capacity Asset", "ES 31EL5FAJQ6SBO|ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all time slices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 75960;
|
||||
public long DataPoints => 75961;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -178,7 +178,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 212941;
|
||||
public long DataPoints => 212942;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -204,7 +204,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "98233.93"},
|
||||
{"Net Profit", "-1.766%"},
|
||||
{"Sharpe Ratio", "-1.14"},
|
||||
{"Sharpe Ratio", "-1.141"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0.020%"},
|
||||
{"Loss Rate", "50%"},
|
||||
@@ -216,7 +216,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-0.602"},
|
||||
{"Tracking Error", "0.291"},
|
||||
{"Treynor Ratio", "-16.64"},
|
||||
{"Treynor Ratio", "-16.65"},
|
||||
{"Total Fees", "$3.57"},
|
||||
{"Estimated Strategy Capacity", "$16000000.00"},
|
||||
{"Lowest Capacity Asset", "ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 268273;
|
||||
public long DataPoints => 268275;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 3630;
|
||||
public long DataPoints => 3631;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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 QuantConnect.Algorithm.Framework.Alphas;
|
||||
using QuantConnect.Algorithm.Framework.Execution;
|
||||
using QuantConnect.Algorithm.Framework.Portfolio;
|
||||
using QuantConnect.Algorithm.Framework.Selection;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm to test ImmediateExecutionModel places orders with the
|
||||
/// correct quantity (taking into account the fee's) so that the fill quantity
|
||||
/// is the expected one.
|
||||
/// </summary>
|
||||
public class ImmediateExecutionModelWorksWithBinanceFeeModel: QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2022, 12, 13);
|
||||
SetEndDate(2022, 12, 14);
|
||||
SetAccountCurrency("BUSD");
|
||||
SetCash("BUSD", 100000, 1);
|
||||
|
||||
UniverseSettings.Resolution = Resolution.Minute;
|
||||
|
||||
var symbols = new List<Symbol>() { QuantConnect.Symbol.Create("BTCBUSD", SecurityType.Crypto, Market.Binance) };
|
||||
SetUniverseSelection(new ManualUniverseSelectionModel(symbols));
|
||||
SetAlpha(new ConstantAlphaModel(InsightType.Price, InsightDirection.Up, TimeSpan.FromMinutes(20), 0.025, null));
|
||||
|
||||
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel(Resolution.Minute));
|
||||
SetExecution(new ImmediateExecutionModel());
|
||||
SetBrokerageModel(Brokerages.BrokerageName.Binance, AccountType.Margin);
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
if (orderEvent.Status == OrderStatus.Filled)
|
||||
{
|
||||
if (Math.Abs(orderEvent.Quantity - 5.8m) > 0.01m)
|
||||
{
|
||||
throw new RegressionTestException($"The expected quantity was {5.8m} but the quantity from the order was {orderEvent.Quantity}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanRunLocally => true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 2882;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 60;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <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 Orders", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000.00"},
|
||||
{"End Equity", "103411.39"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "BUSD99.75"},
|
||||
{"Estimated Strategy Capacity", "BUSD600000.00"},
|
||||
{"Lowest Capacity Asset", "BTCBUSD 18N"},
|
||||
{"Portfolio Turnover", "48.18%"},
|
||||
{"OrderListHash", "2ad07f12d7c80fd4a904269d62794e9e"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.Securities.Option;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm asserting that the option chain APIs return consistent values for index options
|
||||
/// See <see cref="QCAlgorithm.OptionChain(Symbol)"/> and <see cref="QCAlgorithm.OptionChainProvider"/>
|
||||
/// </summary>
|
||||
public class IndexOptionChainApisConsistencyRegressionAlgorithm : OptionChainApisConsistencyRegressionAlgorithm
|
||||
{
|
||||
protected override DateTime TestDate => new DateTime(2021, 1, 4);
|
||||
|
||||
protected override Option GetOption()
|
||||
{
|
||||
return AddIndexOption("SPX");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 2862;
|
||||
}
|
||||
}
|
||||
@@ -186,7 +186,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 1575423;
|
||||
public long DataPoints => 1575425;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 82371;
|
||||
public long DataPoints => 82372;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 82371;
|
||||
public long DataPoints => 82372;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Option;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm asserting that the option chain APIs return consistent values.
|
||||
/// See <see cref="QCAlgorithm.OptionChain(Symbol)"/> and <see cref="QCAlgorithm.OptionChainProvider"/>
|
||||
/// </summary>
|
||||
public class OptionChainApisConsistencyRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
protected virtual DateTime TestDate => new DateTime(2015, 12, 25);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(TestDate);
|
||||
SetEndDate(TestDate);
|
||||
|
||||
var option = GetOption();
|
||||
|
||||
var optionChainFromAlgorithmApi = OptionChain(option.Symbol).Contracts.Values.Select(x => x.Symbol).ToList();
|
||||
|
||||
var exchangeTime = UtcTime.ConvertFromUtc(option.Exchange.TimeZone);
|
||||
var optionChainFromProviderApi = OptionChainProvider.GetOptionContractList(option.Symbol, exchangeTime).ToList();
|
||||
|
||||
if (optionChainFromAlgorithmApi.Count == 0)
|
||||
{
|
||||
throw new RegressionTestException("No options in chain from algorithm API");
|
||||
}
|
||||
|
||||
if (optionChainFromProviderApi.Count == 0)
|
||||
{
|
||||
throw new RegressionTestException("No options in chain from provider API");
|
||||
}
|
||||
|
||||
if (optionChainFromAlgorithmApi.Count != optionChainFromProviderApi.Count)
|
||||
{
|
||||
throw new RegressionTestException($"Expected {optionChainFromProviderApi.Count} options in chain from provider API, " +
|
||||
$"but got {optionChainFromAlgorithmApi.Count}");
|
||||
}
|
||||
|
||||
for (var i = 0; i < optionChainFromAlgorithmApi.Count; i++)
|
||||
{
|
||||
var symbol1 = optionChainFromAlgorithmApi[i];
|
||||
var symbol2 = optionChainFromProviderApi[i];
|
||||
|
||||
if (symbol1 != symbol2)
|
||||
{
|
||||
throw new RegressionTestException($"Expected {symbol2} in chain from provider API, but got {symbol1}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Option GetOption()
|
||||
{
|
||||
return AddOption("GOOG");
|
||||
}
|
||||
|
||||
/// <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 virtual bool CanRunLocally { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public virtual List<Language> Languages { get; } = new() { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public virtual long DataPoints => 2021;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public virtual int AlgorithmHistoryDataPoints => 2;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public virtual AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public virtual Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "100000"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", ""},
|
||||
{"Portfolio Turnover", "0%"},
|
||||
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm illustrating the usage of the <see cref="QCAlgorithm.OptionChains(IEnumerable{Symbol})"/> method
|
||||
/// to get multiple option chains, which contains additional data besides the symbols, including prices, implied volatility and greeks.
|
||||
/// It also shows how this data can be used to filter the contracts based on certain criteria.
|
||||
/// </summary>
|
||||
public class OptionChainsMultipleFullDataRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _googOptionContract;
|
||||
private Symbol _spxOptionContract;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2015, 12, 24);
|
||||
SetEndDate(2015, 12, 24);
|
||||
SetCash(100000);
|
||||
|
||||
var goog = AddEquity("GOOG").Symbol;
|
||||
var spx = AddIndex("SPX").Symbol;
|
||||
|
||||
var chains = OptionChains(new[] { goog, spx });
|
||||
|
||||
_googOptionContract = GetContract(chains, goog, TimeSpan.FromDays(10));
|
||||
_spxOptionContract = GetContract(chains, spx, TimeSpan.FromDays(60));
|
||||
|
||||
AddOptionContract(_googOptionContract);
|
||||
AddIndexOptionContract(_spxOptionContract);
|
||||
}
|
||||
|
||||
private Symbol GetContract(OptionChains chains, Symbol underlying, TimeSpan expirySpan)
|
||||
{
|
||||
return chains
|
||||
.Where(kvp => kvp.Key.Underlying == underlying)
|
||||
.Select(kvp => kvp.Value)
|
||||
.Single()
|
||||
// Get contracts expiring within a given span, with an implied volatility greater than 0.5 and a delta less than 0.5
|
||||
.Where(contractData => contractData.ID.Date - Time <= expirySpan &&
|
||||
contractData.ImpliedVolatility > 0.5m &&
|
||||
contractData.Greeks.Delta < 0.5m)
|
||||
// Get the contract with the latest expiration date
|
||||
.OrderByDescending(x => x.ID.Date)
|
||||
.First();
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
// Do some trading with the selected contract for sample purposes
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
MarketOrder(_googOptionContract, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Liquidate();
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public virtual List<Language> Languages { get; } = new() { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 1059;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 2;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <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 Orders", "210"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "96041"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$209.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", "GOOCV W6U7PD1F2WYU|GOOCV VP83T1ZUHROL"},
|
||||
{"Portfolio Turnover", "85.46%"},
|
||||
{"OrderListHash", "a7ab1a9e64fe9ba76ea33a40a78a4e3b"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Securities.Option;
|
||||
using QuantConnect.Securities.Option.StrategyMatcher;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm exercising an equity Long Call Backspread option strategy and asserting it's being detected by Lean and works as expected
|
||||
/// </summary>
|
||||
public class OptionEquityCallBackspreadRegressionAlgorithm : OptionEquityBaseStrategyRegressionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
/// </summary>
|
||||
/// <param name="slice">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
OptionChain chain;
|
||||
if (IsMarketOpen(_optionSymbol) && slice.OptionChains.TryGetValue(_optionSymbol, out chain))
|
||||
{
|
||||
var callContracts = chain
|
||||
.Where(contract => contract.Right == OptionRight.Call);
|
||||
var expiry = callContracts.Min(x => x.Expiry);
|
||||
callContracts = callContracts.Where(x => x.Expiry == expiry)
|
||||
.OrderBy(x => x.Strike)
|
||||
.ToList();
|
||||
|
||||
var strike = callContracts.Select(x => x.Strike).Distinct();
|
||||
if (strike.Count() < 2) return;
|
||||
|
||||
var lowStrikeCall = callContracts.First();
|
||||
var highStrikeCall = callContracts.First(contract => contract.Strike > lowStrikeCall.Strike && contract.Expiry == expiry);
|
||||
|
||||
var initialMargin = Portfolio.MarginRemaining;
|
||||
|
||||
var optionStrategy = OptionStrategies.CallBackspread(_optionSymbol, lowStrikeCall.Strike, highStrikeCall.Strike, expiry);
|
||||
Buy(optionStrategy, 5);
|
||||
var freeMarginPostTrade = Portfolio.MarginRemaining;
|
||||
|
||||
// It is a combination of bear call spread and long call
|
||||
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.BearCallSpread.Name, 5);
|
||||
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.NakedCall.Name, 5);
|
||||
|
||||
// Should only involve the bear call spread part
|
||||
var expectedMarginUsage = (highStrikeCall.Strike - lowStrikeCall.Strike) * Securities[highStrikeCall.Symbol].SymbolProperties.ContractMultiplier * 5;
|
||||
|
||||
if (expectedMarginUsage != Portfolio.TotalMarginUsed)
|
||||
{
|
||||
throw new Exception($"Unexpect margin used!:{Portfolio.TotalMarginUsed}");
|
||||
}
|
||||
|
||||
// we payed the ask and value using the assets price
|
||||
var priceLadderDifference = GetPriceSpreadDifference(lowStrikeCall.Symbol, highStrikeCall.Symbol);
|
||||
if (initialMargin != (freeMarginPostTrade + expectedMarginUsage + _paidFees - priceLadderDifference))
|
||||
{
|
||||
throw new Exception("Unexpect margin remaining!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 15023;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public override int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "200000"},
|
||||
{"End Equity", "198565.25"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$9.75"},
|
||||
{"Estimated Strategy Capacity", "$47000.00"},
|
||||
{"Lowest Capacity Asset", "GOOCV W78ZERHAOVVQ|GOOCV VP83T1ZUHROL"},
|
||||
{"Portfolio Turnover", "11.81%"},
|
||||
{"OrderListHash", "6ece6c59826ea66fa7b0a1094a0021c7"}
|
||||
};
|
||||
}
|
||||
}
|
||||
128
Algorithm.CSharp/OptionEquityPutBackspreadRegressionAlgorithm.cs
Normal file
128
Algorithm.CSharp/OptionEquityPutBackspreadRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Securities.Option;
|
||||
using QuantConnect.Securities.Option.StrategyMatcher;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm exercising an equity Long Put Backspread option strategy and asserting it's being detected by Lean and works as expected
|
||||
/// </summary>
|
||||
public class OptionEquityPutBackspreadRegressionAlgorithm : OptionEquityBaseStrategyRegressionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
/// </summary>
|
||||
/// <param name="slice">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
OptionChain chain;
|
||||
if (IsMarketOpen(_optionSymbol) && slice.OptionChains.TryGetValue(_optionSymbol, out chain))
|
||||
{
|
||||
var putContracts = chain
|
||||
.Where(contract => contract.Right == OptionRight.Put);
|
||||
var expiry = putContracts.Min(x => x.Expiry);
|
||||
putContracts = putContracts.Where(x => x.Expiry == expiry)
|
||||
.OrderBy(x => x.Strike)
|
||||
.ToList();
|
||||
|
||||
var strike = putContracts.Select(x => x.Strike).Distinct();
|
||||
if (strike.Count() < 2) return;
|
||||
|
||||
var lowStrikePut = putContracts.First();
|
||||
var highStrikePut = putContracts.First(contract => contract.Strike > lowStrikePut.Strike && contract.Expiry == lowStrikePut.Expiry);
|
||||
|
||||
var initialMargin = Portfolio.MarginRemaining;
|
||||
|
||||
var optionStrategy = OptionStrategies.PutBackspread(_optionSymbol, highStrikePut.Strike, lowStrikePut.Strike, expiry);
|
||||
Buy(optionStrategy, 5);
|
||||
var freeMarginPostTrade = Portfolio.MarginRemaining;
|
||||
|
||||
// It is a combination of bull put spread and long put
|
||||
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.BullPutSpread.Name, 5);
|
||||
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.NakedPut.Name, 5);
|
||||
|
||||
// Should only involve the bull put spread part
|
||||
var expectedMarginUsage = (highStrikePut.Strike - lowStrikePut.Strike) * Securities[highStrikePut.Symbol].SymbolProperties.ContractMultiplier * 5;
|
||||
|
||||
if (expectedMarginUsage != Portfolio.TotalMarginUsed)
|
||||
{
|
||||
throw new Exception("Unexpect margin used!");
|
||||
}
|
||||
|
||||
// we payed the ask and value using the assets price
|
||||
var priceLadderDifference = GetPriceSpreadDifference(lowStrikePut.Symbol, highStrikePut.Symbol);
|
||||
if (initialMargin != (freeMarginPostTrade + expectedMarginUsage + _paidFees - priceLadderDifference))
|
||||
{
|
||||
throw new Exception("Unexpect margin remaining!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 15023;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public override int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "200000"},
|
||||
{"End Equity", "199015.25"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$9.75"},
|
||||
{"Estimated Strategy Capacity", "$1100000.00"},
|
||||
{"Lowest Capacity Asset", "GOOCV 306CZL2DIL4G6|GOOCV VP83T1ZUHROL"},
|
||||
{"Portfolio Turnover", "9.15%"},
|
||||
{"OrderListHash", "1a51f04db9201f960dc04668b7f5d41d"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Option;
|
||||
using QuantConnect.Securities.Option.StrategyMatcher;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm exercising an equity Short Call Backspread option strategy and asserting it's being detected by Lean and works as expected
|
||||
/// </summary>
|
||||
public class OptionEquityShortCallBackspreadRegressionAlgorithm : OptionEquityBaseStrategyRegressionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
/// </summary>
|
||||
/// <param name="slice">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
OptionChain chain;
|
||||
if (IsMarketOpen(_optionSymbol) && slice.OptionChains.TryGetValue(_optionSymbol, out chain))
|
||||
{
|
||||
var callContracts = chain
|
||||
.Where(contract => contract.Right == OptionRight.Call);
|
||||
var expiry = callContracts.Min(x => x.Expiry);
|
||||
callContracts = callContracts.Where(x => x.Expiry == expiry)
|
||||
.OrderBy(x => x.Strike)
|
||||
.ToList();
|
||||
|
||||
var strike = callContracts.Select(x => x.Strike).Distinct();
|
||||
if (strike.Count() < 2) return;
|
||||
|
||||
var lowStrikeCall = callContracts.First();
|
||||
var highStrikeCall = callContracts.First(contract => contract.Strike > lowStrikeCall.Strike && contract.Expiry == lowStrikeCall.Expiry);
|
||||
|
||||
var initialMargin = Portfolio.MarginRemaining;
|
||||
|
||||
var optionStrategy = OptionStrategies.ShortCallBackspread(_optionSymbol, lowStrikeCall.Strike, highStrikeCall.Strike, expiry);
|
||||
Buy(optionStrategy, 5);
|
||||
var freeMarginPostTrade = Portfolio.MarginRemaining;
|
||||
|
||||
// It is a combination of bull call spread and naked call
|
||||
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.BullCallSpread.Name, 5);
|
||||
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.NakedCall.Name, 5);
|
||||
|
||||
// Should only involve the naked call part
|
||||
var security = Securities[highStrikeCall.Symbol];
|
||||
var expectedMarginUsage = security.BuyingPowerModel.GetMaintenanceMargin(MaintenanceMarginParameters.ForQuantityAtCurrentPrice(security, -5)).Value;
|
||||
|
||||
if (expectedMarginUsage != Portfolio.TotalMarginUsed)
|
||||
{
|
||||
throw new Exception("Unexpect margin used!");
|
||||
}
|
||||
|
||||
// we payed the ask and value using the assets price
|
||||
var priceLadderDifference = GetPriceSpreadDifference(lowStrikeCall.Symbol, highStrikeCall.Symbol);
|
||||
if (initialMargin != (freeMarginPostTrade + expectedMarginUsage + _paidFees - priceLadderDifference))
|
||||
{
|
||||
throw new Exception("Unexpect margin remaining!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 15023;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public override int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "200000"},
|
||||
{"End Equity", "199915.25"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$9.75"},
|
||||
{"Estimated Strategy Capacity", "$53000.00"},
|
||||
{"Lowest Capacity Asset", "GOOCV W78ZERHAOVVQ|GOOCV VP83T1ZUHROL"},
|
||||
{"Portfolio Turnover", "11.48%"},
|
||||
{"OrderListHash", "357f13ed9e71c4dd8bb8e51e339ba7c5"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Option;
|
||||
using QuantConnect.Securities.Option.StrategyMatcher;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm exercising an equity Short Put Backspread option strategy and asserting it's being detected by Lean and works as expected
|
||||
/// </summary>
|
||||
public class OptionEquityShortPutBackspreadRegressionAlgorithm : OptionEquityBaseStrategyRegressionAlgorithm
|
||||
{
|
||||
/// <summary>
|
||||
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
/// </summary>
|
||||
/// <param name="slice">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
OptionChain chain;
|
||||
if (IsMarketOpen(_optionSymbol) && slice.OptionChains.TryGetValue(_optionSymbol, out chain))
|
||||
{
|
||||
var putContracts = chain
|
||||
.Where(contract => contract.Right == OptionRight.Put);
|
||||
var expiry = putContracts.Min(x => x.Expiry);
|
||||
putContracts = putContracts.Where(x => x.Expiry == expiry)
|
||||
.OrderBy(x => x.Strike)
|
||||
.ToList();
|
||||
|
||||
var strike = putContracts.Select(x => x.Strike).Distinct();
|
||||
if (strike.Count() < 2) return;
|
||||
|
||||
var lowStrikePut = putContracts.First();
|
||||
var highStrikePut = putContracts.First(contract => contract.Strike > lowStrikePut.Strike && contract.Expiry == lowStrikePut.Expiry);
|
||||
|
||||
var initialMargin = Portfolio.MarginRemaining;
|
||||
|
||||
var optionStrategy = OptionStrategies.ShortPutBackspread(_optionSymbol, highStrikePut.Strike, lowStrikePut.Strike, expiry);
|
||||
Buy(optionStrategy, 5);
|
||||
var freeMarginPostTrade = Portfolio.MarginRemaining;
|
||||
|
||||
// It is a combination of bear put spread and naked put
|
||||
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.BearPutSpread.Name, 5);
|
||||
AssertOptionStrategyIsPresent(OptionStrategyDefinitions.NakedPut.Name, 5);
|
||||
|
||||
// Should only involve the naked put part
|
||||
var security = Securities[lowStrikePut.Symbol];
|
||||
var expectedMarginUsage = security.BuyingPowerModel.GetMaintenanceMargin(MaintenanceMarginParameters.ForQuantityAtCurrentPrice(security, -5)).Value;
|
||||
|
||||
if (expectedMarginUsage != Portfolio.TotalMarginUsed)
|
||||
{
|
||||
throw new Exception("Unexpect margin used!");
|
||||
}
|
||||
|
||||
// we payed the ask and value using the assets price
|
||||
var priceLadderDifference = GetPriceSpreadDifference(lowStrikePut.Symbol, highStrikePut.Symbol);
|
||||
if (initialMargin != (freeMarginPostTrade + expectedMarginUsage + _paidFees - priceLadderDifference))
|
||||
{
|
||||
throw new Exception("Unexpect margin remaining!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 15023;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public override int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "200000"},
|
||||
{"End Equity", "199165.25"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$9.75"},
|
||||
{"Estimated Strategy Capacity", "$1200000.00"},
|
||||
{"Lowest Capacity Asset", "GOOCV 306CZL2DIL4G6|GOOCV VP83T1ZUHROL"},
|
||||
{"Portfolio Turnover", "8.84%"},
|
||||
{"OrderListHash", "7294da06231632975e97c57721d26442"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -153,7 +153,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 212195;
|
||||
public long DataPoints => 212196;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -191,7 +191,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Annual Variance", "0.003"},
|
||||
{"Information Ratio", "-0.198"},
|
||||
{"Tracking Error", "0.377"},
|
||||
{"Treynor Ratio", "-23.06"},
|
||||
{"Treynor Ratio", "-23.065"},
|
||||
{"Total Fees", "$1.42"},
|
||||
{"Estimated Strategy Capacity", "$180000000.00"},
|
||||
{"Lowest Capacity Asset", "ES XFH59UPHGV9G|ES XFH59UK0MYO1"},
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
foreach (var contract in contracts)
|
||||
{
|
||||
Greeks greeks = new Greeks();
|
||||
Greeks greeks = null;
|
||||
try
|
||||
{
|
||||
greeks = contract.Greeks;
|
||||
@@ -110,7 +110,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
// Greeks should be valid if they were successfuly accessed for supported option style
|
||||
if (_optionStyleIsSupported)
|
||||
{
|
||||
if (greeks.Delta == 0m && greeks.Gamma == 0m && greeks.Theta == 0m && greeks.Vega == 0m && greeks.Rho == 0m)
|
||||
if (greeks == null ||
|
||||
(greeks.Delta == 0m && greeks.Gamma == 0m && greeks.Theta == 0m && greeks.Vega == 0m && greeks.Rho == 0m))
|
||||
{
|
||||
throw new RegressionTestException($"Expected greeks to not be zero simultaneously for {contract.Symbol.Value}, an {_option.Style} style option, using {_option?.PriceModel.GetType().Name}, but they were");
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 175;
|
||||
public override long DataPoints => 177;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 175;
|
||||
public override long DataPoints => 177;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<DebugType>portable</DebugType>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.38" />
|
||||
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.41" />
|
||||
<PackageReference Include="Accord" Version="3.6.0" />
|
||||
<PackageReference Include="Accord.Fuzzy" Version="3.6.0" />
|
||||
<PackageReference Include="Accord.MachineLearning" Version="3.6.0" />
|
||||
|
||||
@@ -37,8 +37,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 07);
|
||||
SetEndDate(2013, 10, 09);
|
||||
SetStartDate(2020, 01, 05);
|
||||
SetEndDate(2020, 01, 10);
|
||||
|
||||
var SP500 = QuantConnect.Symbol.Create(Futures.Indices.SP500EMini, SecurityType.Future, Market.CME);
|
||||
_symbol = FutureChainProvider.GetFutureContractList(SP500, StartDate.AddDays(1)).First();
|
||||
@@ -160,7 +160,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 4090;
|
||||
public long DataPoints => 6803;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
@@ -180,30 +180,30 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Orders", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "-100.000%"},
|
||||
{"Drawdown", "19.500%"},
|
||||
{"Compounding Annual Return", "22662.692%"},
|
||||
{"Drawdown", "1.700%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "88402.7"},
|
||||
{"Net Profit", "-11.597%"},
|
||||
{"Sharpe Ratio", "-0.687"},
|
||||
{"End Equity", "109332.4"},
|
||||
{"Net Profit", "9.332%"},
|
||||
{"Sharpe Ratio", "157.927"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Probabilistic Sharpe Ratio", "95.713%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "7.176"},
|
||||
{"Beta", "10.771"},
|
||||
{"Annual Standard Deviation", "1.467"},
|
||||
{"Annual Variance", "2.151"},
|
||||
{"Information Ratio", "-0.186"},
|
||||
{"Tracking Error", "1.331"},
|
||||
{"Treynor Ratio", "-0.094"},
|
||||
{"Total Fees", "$23.65"},
|
||||
{"Estimated Strategy Capacity", "$290000000.00"},
|
||||
{"Lowest Capacity Asset", "ES VMKLFZIH2MTD"},
|
||||
{"Portfolio Turnover", "309.20%"},
|
||||
{"OrderListHash", "448896f610140b728419d56313cc61bc"}
|
||||
{"Alpha", "103.354"},
|
||||
{"Beta", "1.96"},
|
||||
{"Annual Standard Deviation", "0.663"},
|
||||
{"Annual Variance", "0.439"},
|
||||
{"Information Ratio", "159.787"},
|
||||
{"Tracking Error", "0.651"},
|
||||
{"Treynor Ratio", "53.381"},
|
||||
{"Total Fees", "$15.05"},
|
||||
{"Estimated Strategy Capacity", "$1900000000.00"},
|
||||
{"Lowest Capacity Asset", "ES XCZJLC9NOB29"},
|
||||
{"Portfolio Turnover", "171.57%"},
|
||||
{"OrderListHash", "d814db6d5a9c97ee6de477ea06cd3834"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data;
|
||||
using QuantConnect.Data.Consolidators;
|
||||
using QuantConnect.Indicators;
|
||||
using QuantConnect.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm that asserts Stochastic indicator, registered with a different resolution consolidator,
|
||||
/// is warmed up properly by calling QCAlgorithm.WarmUpIndicator
|
||||
/// </summary>
|
||||
public class StochasticIndicatorWarmsUpProperlyRegressionAlgorithm: QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private bool _dataPointsReceived;
|
||||
private Symbol _spy;
|
||||
private RelativeStrengthIndex _rsi;
|
||||
private RelativeStrengthIndex _rsiHistory;
|
||||
private Stochastic _sto;
|
||||
private Stochastic _stoHistory;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 1);
|
||||
SetEndDate(2020, 2, 1);
|
||||
|
||||
_spy = AddEquity("SPY", Resolution.Hour).Symbol;
|
||||
|
||||
var dailyConsolidator = new TradeBarConsolidator(TimeSpan.FromDays(1));
|
||||
_rsi = new RelativeStrengthIndex(14, MovingAverageType.Wilders);
|
||||
_sto = new Stochastic("FIRST", 14, 3, 3);
|
||||
RegisterIndicator(_spy, _rsi, dailyConsolidator);
|
||||
RegisterIndicator(_spy, _sto, dailyConsolidator);
|
||||
|
||||
WarmUpIndicator(_spy, _rsi, TimeSpan.FromDays(1));
|
||||
WarmUpIndicator(_spy, _sto, TimeSpan.FromDays(1));
|
||||
|
||||
_rsiHistory = new RelativeStrengthIndex(14, MovingAverageType.Wilders);
|
||||
_stoHistory = new Stochastic("SECOND", 14, 3, 3);
|
||||
RegisterIndicator(_spy, _rsiHistory, dailyConsolidator);
|
||||
RegisterIndicator(_spy, _stoHistory, dailyConsolidator);
|
||||
|
||||
var history = History(_spy, Math.Max(_rsiHistory.WarmUpPeriod, _stoHistory.WarmUpPeriod), Resolution.Daily);
|
||||
|
||||
// Warm up RSI indicator
|
||||
foreach (var bar in history)
|
||||
{
|
||||
_rsiHistory.Update(bar.EndTime, bar.Close);
|
||||
}
|
||||
|
||||
// Warm up STO indicator
|
||||
foreach (var bar in history.TakeLast(_stoHistory.WarmUpPeriod))
|
||||
{
|
||||
_stoHistory.Update(bar);
|
||||
}
|
||||
|
||||
var indicators = new List<IIndicator>() { _rsi, _sto, _rsiHistory, _stoHistory };
|
||||
|
||||
foreach (var indicator in indicators)
|
||||
{
|
||||
if (!indicator.IsReady)
|
||||
{
|
||||
throw new RegressionTestException($"{indicator.Name} should be ready, but it is not. Number of samples: {indicator.Samples}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (IsWarmingUp) return;
|
||||
|
||||
if (slice.ContainsKey(_spy))
|
||||
{
|
||||
_dataPointsReceived = true;
|
||||
|
||||
if (_rsi.Current.Value != _rsiHistory.Current.Value)
|
||||
{
|
||||
throw new RegressionTestException($"Values of indicators differ: {_rsi.Name}: {_rsi.Current.Value} | {_rsiHistory.Name}: {_rsiHistory.Current.Value}");
|
||||
}
|
||||
|
||||
if (_sto.StochK.Current.Value != _stoHistory.StochK.Current.Value)
|
||||
{
|
||||
throw new RegressionTestException($"Stoch K values of indicators differ: {_sto.Name}.StochK: {_sto.StochK.Current.Value} | {_stoHistory.Name}.StochK: {_stoHistory.StochK.Current.Value}");
|
||||
}
|
||||
|
||||
if (_sto.StochD.Current.Value != _stoHistory.StochD.Current.Value)
|
||||
{
|
||||
throw new RegressionTestException($"Stoch D values of indicators differ: {_sto.Name}.StochD: {_sto.StochD.Current.Value} | {_stoHistory.Name}.StochD: {_stoHistory.StochD.Current.Value}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (!_dataPointsReceived)
|
||||
{
|
||||
throw new Exception("No data points received");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 302;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 44;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <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 Orders", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "100000"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-0.016"},
|
||||
{"Tracking Error", "0.101"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", ""},
|
||||
{"Portfolio Turnover", "0%"},
|
||||
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Consolidators;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Util;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm asserting fill forwarded data behavior for consolidators and indicators.
|
||||
/// 1. Test that the on-consolidated event is not called for fill forwarded data in identity and higher period consolidators
|
||||
/// 2. Test that the intra-day fill-forwarded data is not fed to indicators
|
||||
/// </summary>
|
||||
public class StrictEndTimeLowerResolutionFillForwardRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Security _aapl;
|
||||
|
||||
private BaseData _lastNonFilledForwardedData;
|
||||
private int _dataCount;
|
||||
private int _indicatorUpdateCount;
|
||||
|
||||
protected virtual bool ExtendedMarketHours => false;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 07);
|
||||
SetEndDate(2013, 10, 30);
|
||||
|
||||
Settings.DailyPreciseEndTime = true;
|
||||
|
||||
// Fill forward resolution will be minute
|
||||
AddEquity("SPY", Resolution.Minute);
|
||||
_aapl = AddEquity("AAPL", Resolution.Daily, extendedMarketHours: ExtendedMarketHours);
|
||||
|
||||
var tradableDates = QuantConnect.Time.EachTradeableDayInTimeZone(_aapl.Exchange.Hours, StartDate, EndDate,
|
||||
_aapl.Exchange.TimeZone, _aapl.IsExtendedMarketHours).ToList();
|
||||
|
||||
TestIdentityConsolidator(tradableDates);
|
||||
TestHigherPeriodConsolidator(tradableDates);
|
||||
TestIndicator(tradableDates);
|
||||
}
|
||||
|
||||
private void TestIdentityConsolidator(List<DateTime> tradableDates)
|
||||
{
|
||||
var i = 0;
|
||||
var consolidator = Consolidate<TradeBar>(_aapl.Symbol, TimeSpan.FromDays(1), (bar) =>
|
||||
{
|
||||
var expectedDate = tradableDates[i++];
|
||||
var schedule = LeanData.GetDailyCalendar(expectedDate.AddDays(1), _aapl.Exchange, false);
|
||||
|
||||
if (bar.Time != schedule.Start || bar.EndTime != schedule.End)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected consolidated bar time. " +
|
||||
$"Expected: [{schedule.Start} - {schedule.End}], Actual: [{bar.Time} - {bar.EndTime}]");
|
||||
}
|
||||
|
||||
Debug($"Consolidated (identity) :: {bar}");
|
||||
});
|
||||
|
||||
if (consolidator is not IdentityDataConsolidator<TradeBar>)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected consolidator type. " +
|
||||
$"Expected {typeof(IdentityDataConsolidator<TradeBar>)} but was {consolidator.GetType()}");
|
||||
}
|
||||
}
|
||||
|
||||
private void TestHigherPeriodConsolidator(List<DateTime> tradableDates)
|
||||
{
|
||||
var i = 0;
|
||||
// Add a consolidator to assert that fill forward data is not used
|
||||
Consolidate<TradeBar>(_aapl.Symbol, TimeSpan.FromDays(2), (bar) =>
|
||||
{
|
||||
var expectedStartDate = tradableDates[i++];
|
||||
var startDateSchedule = LeanData.GetDailyCalendar(expectedStartDate.AddDays(1), _aapl.Exchange, false);
|
||||
|
||||
var expectedStartTime = startDateSchedule.Start;
|
||||
var expectedEndTime = expectedStartTime.AddDays(2);
|
||||
|
||||
if (bar.Time != expectedStartTime || bar.EndTime != expectedEndTime)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected consolidated bar time. " +
|
||||
$"Expected: [{expectedStartTime} - {expectedEndTime}], Actual: [{bar.Time} - {bar.EndTime}]");
|
||||
}
|
||||
|
||||
if (tradableDates[i] == expectedStartDate.AddDays(1))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
Debug($"Consolidated (2 days) :: {bar}");
|
||||
});
|
||||
}
|
||||
|
||||
private void TestIndicator(List<DateTime> tradableDates)
|
||||
{
|
||||
var i = 0;
|
||||
EMA(_aapl.Symbol, 3, Resolution.Daily).Updated += (sender, data) =>
|
||||
{
|
||||
_indicatorUpdateCount++;
|
||||
|
||||
var expectedEndTime = _aapl.Exchange.Hours.GetNextMarketClose(tradableDates[i++], false);
|
||||
if (data.EndTime != expectedEndTime)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected EMA time. Expected: {expectedEndTime}, Actual: {data.EndTime}");
|
||||
}
|
||||
|
||||
Debug($"EMA :: [{data.EndTime}] {data}");
|
||||
};
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
if (slice.TryGetValue(_aapl.Symbol, out var data))
|
||||
{
|
||||
var baseData = data as BaseData;
|
||||
if (!baseData.IsFillForward)
|
||||
{
|
||||
_lastNonFilledForwardedData = baseData;
|
||||
}
|
||||
|
||||
var timeInExchangeTz = UtcTime.ConvertFromUtc(_aapl.Exchange.TimeZone);
|
||||
var daySchedule = LeanData.GetDailyCalendar(timeInExchangeTz, _aapl.Exchange, false);
|
||||
|
||||
if (timeInExchangeTz == daySchedule.End)
|
||||
{
|
||||
if (baseData.IsFillForward)
|
||||
{
|
||||
throw new RegressionTestException("End of day data should not be fill forward for daily subscription when data is available");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!baseData.IsFillForward
|
||||
|| _lastNonFilledForwardedData == null
|
||||
|| _lastNonFilledForwardedData.Time.Date != baseData.Time.Date
|
||||
|| _lastNonFilledForwardedData.EndTime.Date != baseData.EndTime.Date)
|
||||
{
|
||||
throw new RegressionTestException("Data should be fill forward to minute resolution during the day");
|
||||
}
|
||||
}
|
||||
|
||||
_dataCount++;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
var tradableDates = QuantConnect.Time.EachTradeableDay(_aapl, StartDate.AddDays(1), EndDate, ExtendedMarketHours);
|
||||
var tradableDatesCount = 1;
|
||||
var expectedDataCount = 1; // One for the first day
|
||||
foreach (var date in tradableDates)
|
||||
{
|
||||
tradableDatesCount++;
|
||||
var hours = _aapl.Exchange.Hours.GetMarketHours(date);
|
||||
foreach (var segment in hours.Segments)
|
||||
{
|
||||
if (ExtendedMarketHours || segment.State == MarketHoursState.Market)
|
||||
{
|
||||
expectedDataCount += (int)(segment.End - segment.Start).TotalMinutes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_dataCount != expectedDataCount)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected data count. Expected: {expectedDataCount}, Actual: {_dataCount}");
|
||||
}
|
||||
|
||||
if (_indicatorUpdateCount != tradableDatesCount)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected indicator update count. Expected: {tradableDatesCount}, Actual: {_indicatorUpdateCount}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public virtual long DataPoints => 20805;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <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 Orders", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "100000"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-7.12"},
|
||||
{"Tracking Error", "0.109"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", ""},
|
||||
{"Portfolio Turnover", "0%"},
|
||||
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm asserting fill forwarded data behavior for consolidators and indicators.
|
||||
/// 1. Test that the on-consolidated event is not called for fill forwarded data in identity and higher period consolidators
|
||||
/// 2. Test that the intra-day fill-forwarded data is not fed to indicators
|
||||
/// </summary>
|
||||
public class StrictEndTimeLowerResolutionFillForwardWithExtendedMarketHoursRegressionAlgorithm : StrictEndTimeLowerResolutionFillForwardRegressionAlgorithm
|
||||
{
|
||||
protected override bool ExtendedMarketHours => true;
|
||||
|
||||
public override long DataPoints => 30495;
|
||||
}
|
||||
}
|
||||
135
Algorithm.CSharp/VolumeShareSlippageModelAlgorithm.cs
Normal file
135
Algorithm.CSharp/VolumeShareSlippageModelAlgorithm.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuantConnect.Algorithm.Framework.Portfolio;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders.Slippage;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Example algorithm implementing VolumeShareSlippageModel.
|
||||
/// </summary>
|
||||
public class VolumeShareSlippageModelAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private List<Symbol> _longs = new();
|
||||
private List<Symbol> _shorts = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 11, 29);
|
||||
SetEndDate(2020, 12, 2);
|
||||
// To set the slippage model to limit to fill only 30% volume of the historical volume, with 5% slippage impact.
|
||||
SetSecurityInitializer((security) => security.SetSlippageModel(new VolumeShareSlippageModel(0.3m, 0.05m)));
|
||||
|
||||
// Create SPY symbol to explore its constituents.
|
||||
var spy = QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA);
|
||||
|
||||
UniverseSettings.Resolution = Resolution.Daily;
|
||||
// Add universe to trade on the most and least weighted stocks among SPY constituents.
|
||||
AddUniverse(Universe.ETF(spy, universeFilterFunc: Selection));
|
||||
}
|
||||
|
||||
private IEnumerable<Symbol> Selection(IEnumerable<ETFConstituentUniverse> constituents)
|
||||
{
|
||||
var sortedByDollarVolume = constituents.OrderBy(x => x.Weight).ToList();
|
||||
// Add the 10 most weighted stocks to the universe to long later.
|
||||
_longs = sortedByDollarVolume.TakeLast(10)
|
||||
.Select(x => x.Symbol)
|
||||
.ToList();
|
||||
// Add the 10 least weighted stocks to the universe to short later.
|
||||
_shorts = sortedByDollarVolume.Take(10)
|
||||
.Select(x => x.Symbol)
|
||||
.ToList();
|
||||
|
||||
return _longs.Union(_shorts);
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
// Equally invest into the selected stocks to evenly dissipate capital risk.
|
||||
// Dollar neutral of long and short stocks to eliminate systematic risk, only capitalize the popularity gap.
|
||||
var targets = _longs.Select(symbol => new PortfolioTarget(symbol, 0.05m)).ToList();
|
||||
targets.AddRange(_shorts.Select(symbol => new PortfolioTarget(symbol, -0.05m)).ToList());
|
||||
|
||||
// Liquidate the ones not being the most and least popularity stocks to release fund for higher expected return trades.
|
||||
SetHoldings(targets, liquidateExistingHoldings: true);
|
||||
}
|
||||
|
||||
/// <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 { get; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 1035;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <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 Orders", "4"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "20.900%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "100190.84"},
|
||||
{"Net Profit", "0.191%"},
|
||||
{"Sharpe Ratio", "9.794"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.297"},
|
||||
{"Beta", "-0.064"},
|
||||
{"Annual Standard Deviation", "0.017"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-18.213"},
|
||||
{"Tracking Error", "0.099"},
|
||||
{"Treynor Ratio", "-2.695"},
|
||||
{"Total Fees", "$4.00"},
|
||||
{"Estimated Strategy Capacity", "$4400000000.00"},
|
||||
{"Lowest Capacity Asset", "GOOCV VP83T1ZUHROL"},
|
||||
{"Portfolio Turnover", "4.22%"},
|
||||
{"OrderListHash", "9d2bd0df7c094c393e77f72b7739bfa0"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,6 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 28889;
|
||||
public override long DataPoints => 28892;
|
||||
}
|
||||
}
|
||||
|
||||
98
Algorithm.CSharp/ZeroDTEIndexOptionsRegressionAlgorithm.cs
Normal file
98
Algorithm.CSharp/ZeroDTEIndexOptionsRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Algorithm asserting that options are selected every day and that selection for 0DTE contracts works as expected,
|
||||
/// always including the contracts that expire the same date the option chain belongs to.
|
||||
/// </summary>
|
||||
public class ZeroDTEIndexOptionsRegressionAlgorithm : ZeroDTEOptionsRegressionAlgorithm
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2021, 01, 15);
|
||||
SetEndDate(2021, 01, 15);
|
||||
SetCash(100000);
|
||||
|
||||
var index = AddIndex("SPX");
|
||||
var indexOption = AddIndexOption(index.Symbol);
|
||||
|
||||
indexOption.SetFilter(u => u.IncludeWeeklys().Expiration(0, 0));
|
||||
_option = indexOption;
|
||||
|
||||
// use the underlying equity as the benchmark
|
||||
SetBenchmark(index.Symbol);
|
||||
|
||||
_selectionDays = new List<DateTime>()
|
||||
{
|
||||
new DateTime(2021, 01, 15),
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public override long DataPoints => 27;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public override int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public override AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public override Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Start Equity", "100000"},
|
||||
{"End Equity", "100000"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Estimated Strategy Capacity", "$0"},
|
||||
{"Lowest Capacity Asset", ""},
|
||||
{"Portfolio Turnover", "0%"},
|
||||
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ using System.Linq;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Option;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
@@ -29,10 +30,13 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public class ZeroDTEOptionsRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private List<DateTime> _selectionDays;
|
||||
protected List<DateTime> _selectionDays;
|
||||
private int _currentSelectionDayIndex;
|
||||
|
||||
private int _previouslyAddedContracts;
|
||||
private bool _selectionChecked;
|
||||
|
||||
protected Option _option;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -44,6 +48,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
var option = AddOption(equity.Symbol);
|
||||
|
||||
option.SetFilter(u => u.IncludeWeeklys().Expiration(0, 0));
|
||||
_option = option;
|
||||
|
||||
// use the underlying equity as the benchmark
|
||||
SetBenchmark(equity.Symbol);
|
||||
@@ -62,27 +67,43 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
public override void OnSecuritiesChanged(SecurityChanges changes)
|
||||
{
|
||||
// We expect selection every trading day
|
||||
if (Time.Date != _selectionDays[_currentSelectionDayIndex++])
|
||||
var exchangeTime = UtcTime.ConvertFromUtc(_option.Exchange.TimeZone);
|
||||
|
||||
// Selection happens at midnight
|
||||
if (exchangeTime.TimeOfDay == TimeSpan.Zero)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected date. Expected {_selectionDays[_currentSelectionDayIndex]} but was {Time.Date}");
|
||||
_selectionChecked = true;
|
||||
|
||||
// We expect selection every trading day
|
||||
if (Time.Date != _selectionDays[_currentSelectionDayIndex++])
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected date. Expected {_selectionDays[_currentSelectionDayIndex]} but was {Time.Date}");
|
||||
}
|
||||
|
||||
var addedOptions = changes.AddedSecurities.Where(x => x.Symbol.SecurityType == _option.Symbol.SecurityType && !x.Symbol.IsCanonical()).ToList();
|
||||
|
||||
if (addedOptions.Count == 0)
|
||||
{
|
||||
throw new RegressionTestException("No options were added");
|
||||
}
|
||||
|
||||
var removedOptions = changes.RemovedSecurities.Where(x => x.Symbol.SecurityType == _option.Symbol.SecurityType && !x.Symbol.IsCanonical()).ToList();
|
||||
|
||||
// Since we are selecting only 0DTE contracts, they must be deselected that same day
|
||||
if (removedOptions.Count != _previouslyAddedContracts)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected number of removed contracts. Expected {_previouslyAddedContracts} but was {removedOptions.Count}");
|
||||
}
|
||||
_previouslyAddedContracts = addedOptions.Count;
|
||||
}
|
||||
}
|
||||
|
||||
var addedOptions = changes.AddedSecurities.Where(x => x.Symbol.SecurityType == SecurityType.Option && !x.Symbol.IsCanonical()).ToList();
|
||||
|
||||
if (addedOptions.Count == 0)
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (!_selectionChecked)
|
||||
{
|
||||
throw new RegressionTestException("No options were added");
|
||||
throw new RegressionTestException("Selection was not checked");
|
||||
}
|
||||
|
||||
var removedOptions = changes.RemovedSecurities.Where(x => x.Symbol.SecurityType == SecurityType.Option && !x.Symbol.IsCanonical()).ToList();
|
||||
|
||||
// Since we are selecting only 0DTE contracts, they must be deselected that same day
|
||||
if (removedOptions.Count != _previouslyAddedContracts)
|
||||
{
|
||||
throw new RegressionTestException($"Unexpected number of removed contracts. Expected {_previouslyAddedContracts} but was {removedOptions.Count}");
|
||||
}
|
||||
_previouslyAddedContracts = addedOptions.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -93,27 +114,27 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate which languages this algorithm is written in.
|
||||
/// </summary>
|
||||
public List<Language> Languages { get; } = new() { Language.CSharp };
|
||||
public virtual List<Language> Languages { get; } = new() { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of all timeslices of algorithm
|
||||
/// </summary>
|
||||
public long DataPoints => 227;
|
||||
public virtual long DataPoints => 227;
|
||||
|
||||
/// <summary>
|
||||
/// Data Points count of the algorithm history
|
||||
/// </summary>
|
||||
public int AlgorithmHistoryDataPoints => 0;
|
||||
public virtual int AlgorithmHistoryDataPoints => 0;
|
||||
|
||||
/// <summary>
|
||||
/// Final status of the algorithm
|
||||
/// </summary>
|
||||
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
public virtual AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
|
||||
|
||||
/// <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>
|
||||
public virtual Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Orders", "0"},
|
||||
{"Average Win", "0%"},
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<PackageLicenseFile>LICENSE</PackageLicenseFile>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.38" />
|
||||
<PackageReference Include="QuantConnect.pythonnet" Version="2.0.41" />
|
||||
<PackageReference Include="Accord" Version="3.6.0" />
|
||||
<PackageReference Include="Accord.Math" Version="3.6.0" />
|
||||
<PackageReference Include="Accord.Statistics" Version="3.6.0" />
|
||||
|
||||
@@ -70,11 +70,12 @@ namespace QuantConnect.Algorithm.Framework.Selection
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the future chain universe filter
|
||||
/// Defines the future chain universe filter
|
||||
/// </summary>
|
||||
protected override FutureFilterUniverse Filter(FutureFilterUniverse filter)
|
||||
{
|
||||
return filter.Contracts(FilterByOpenInterest(filter.ToDictionary(x => x, x => _marketHoursDatabase.GetEntry(x.ID.Market, x, x.ID.SecurityType))));
|
||||
// Remove duplicated keys
|
||||
return filter.Contracts(FilterByOpenInterest(filter.DistinctBy(x => x).ToDictionary(x => x, x => _marketHoursDatabase.GetEntry(x.ID.Market, x, x.ID.SecurityType))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -35,7 +35,7 @@ class BaseFrameworkRegressionAlgorithm(QCAlgorithm):
|
||||
# With this procedure, the Alpha Model will experience multiple universe changes
|
||||
self.add_universe_selection(ScheduledUniverseSelectionModel(
|
||||
self.date_rules.every_day(), self.time_rules.midnight,
|
||||
lambda dt: symbols if dt.replace(tzinfo=None) < self.end_date - timedelta(1) else []))
|
||||
lambda dt: symbols if dt < self.end_date - timedelta(1) else []))
|
||||
|
||||
self.set_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(31), 0.025, None))
|
||||
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel())
|
||||
|
||||
100
Algorithm.Python/CallbackCommandRegressionAlgorithm.py
Normal file
100
Algorithm.Python/CallbackCommandRegressionAlgorithm.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# 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.
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
class InvalidCommand():
|
||||
variable = 10
|
||||
|
||||
class VoidCommand():
|
||||
quantity = 0
|
||||
target = []
|
||||
parameters = {}
|
||||
targettime = None
|
||||
|
||||
def run(self, algo: QCAlgorithm) -> bool | None:
|
||||
if not self.targettime or self.targettime != algo.time:
|
||||
return
|
||||
tag = self.parameters["tag"]
|
||||
algo.order(self.target[0], self.get_quantity(), tag=tag)
|
||||
|
||||
def get_quantity(self):
|
||||
return self.quantity
|
||||
|
||||
class BoolCommand(Command):
|
||||
something_else = {}
|
||||
array_test = []
|
||||
result = False
|
||||
|
||||
def run(self, algo: QCAlgorithm) -> bool | None:
|
||||
trade_ibm = self.my_custom_method()
|
||||
if trade_ibm:
|
||||
algo.debug(f"BoolCommand.run: {str(self)}")
|
||||
algo.buy("IBM", 1)
|
||||
return trade_ibm
|
||||
|
||||
def my_custom_method(self):
|
||||
return self.result
|
||||
|
||||
### <summary>
|
||||
### Regression algorithm asserting the behavior of different callback commands call
|
||||
### </summary>
|
||||
class CallbackCommandRegressionAlgorithm(QCAlgorithm):
|
||||
def initialize(self):
|
||||
'''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.'''
|
||||
|
||||
self.set_start_date(2013, 10, 7)
|
||||
self.set_end_date(2013, 10, 11)
|
||||
|
||||
self.add_equity("SPY")
|
||||
self.add_equity("IBM")
|
||||
self.add_equity("BAC")
|
||||
|
||||
self.add_command(VoidCommand)
|
||||
self.add_command(BoolCommand)
|
||||
|
||||
threw_exception = False
|
||||
try:
|
||||
self.add_command(InvalidCommand)
|
||||
except:
|
||||
threw_exception = True
|
||||
if not threw_exception:
|
||||
raise ValueError('InvalidCommand did not throw!')
|
||||
|
||||
bool_command = BoolCommand()
|
||||
bool_command.result = True
|
||||
bool_command.something_else = { "Property": 10 }
|
||||
bool_command.array_test = [ "SPY", "BTCUSD" ]
|
||||
link = self.link(bool_command)
|
||||
if "&command[array_test][0]=SPY&command[array_test][1]=BTCUSD&command[result]=True&command[something_else][Property]=10&command[$type]=BoolCommand" not in link:
|
||||
raise ValueError(f'Invalid link was generated! {link}')
|
||||
|
||||
potential_command = VoidCommand()
|
||||
potential_command.target = [ "BAC" ]
|
||||
potential_command.quantity = 10
|
||||
potential_command.parameters = { "tag": "Signal X" }
|
||||
|
||||
command_link = self.link(potential_command)
|
||||
if "command[target][0]=BAC&command[quantity]=10&command[parameters][tag]=Signal+X&command[$type]=VoidCommand" not in command_link:
|
||||
raise ValueError(f'Invalid link was generated! {command_link}')
|
||||
self.notify.email("email@address", "Trade Command Event", f"Signal X trade\nFollow link to trigger: {command_link}")
|
||||
|
||||
untyped_command_link = self.link({ "symbol": "SPY", "parameters": { "quantity": 10 } })
|
||||
if "&command[symbol]=SPY&command[parameters][quantity]=10" not in untyped_command_link:
|
||||
raise ValueError(f'Invalid link was generated! {untyped_command_link}')
|
||||
self.notify.email("email@address", "Untyped Command Event", f"Signal Y trade\nFollow link to trigger: {untyped_command_link}")
|
||||
|
||||
def on_command(self, data):
|
||||
self.debug(f"on_command: {str(data)}")
|
||||
self.buy(data.symbol, data.parameters["quantity"])
|
||||
return True # False, None
|
||||
@@ -0,0 +1,69 @@
|
||||
# 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.
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm asserts the consolidated US equity daily bars from the hour bars exactly matches
|
||||
### the daily bars returned from the database
|
||||
### </summary>
|
||||
class ConsolidateHourBarsIntoDailyBarsRegressionAlgorithm(QCAlgorithm):
|
||||
def initialize(self):
|
||||
self.set_start_date(2020, 5, 1)
|
||||
self.set_end_date(2020, 6, 5)
|
||||
|
||||
self.spy = self.add_equity("SPY", Resolution.HOUR).symbol
|
||||
|
||||
# We will use these two indicators to compare the daily consolidated bars equals
|
||||
# the ones returned from the database. We use this specific type of indicator as
|
||||
# it depends on its previous values. Thus, if at some point the bars received by
|
||||
# the indicators differ, so will their final values
|
||||
self._rsi = RelativeStrengthIndex("First", 15, MovingAverageType.WILDERS)
|
||||
self.register_indicator(self.spy, self._rsi, Resolution.DAILY, selector= lambda bar: (bar.close + bar.open) / 2)
|
||||
|
||||
# We won't register this indicator as we will update it manually at the end of the
|
||||
# month, so that we can compare the values of the indicator that received consolidated
|
||||
# bars and the values of this one
|
||||
self._rsi_timedelta = RelativeStrengthIndex("Second", 15, MovingAverageType.WILDERS)
|
||||
self._values = {}
|
||||
self.count = 0;
|
||||
self._indicators_compared = False;
|
||||
|
||||
def on_data(self, data: Slice):
|
||||
if self.is_warming_up:
|
||||
return
|
||||
|
||||
if data.contains_key(self.spy) and data[self.spy] != None:
|
||||
if self.time.month == self.end_date.month:
|
||||
history = self.history[TradeBar](self.spy, self.count, Resolution.DAILY)
|
||||
for bar in history:
|
||||
time = bar.end_time.strftime('%Y-%m-%d')
|
||||
average = (bar.close + bar.open) / 2
|
||||
self._rsi_timedelta.update(bar.end_time, average)
|
||||
if self._rsi_timedelta.current.value != self._values[time]:
|
||||
raise Exception(f"Both {self._rsi.name} and {self._rsi_timedelta.name} should have the same values, but they differ. {self._rsi.name}: {self._values[time]} | {self._rsi_timedelta.name}: {self._rsi_timedelta.current.value}")
|
||||
self._indicators_compared = True
|
||||
self.quit()
|
||||
else:
|
||||
time = self.time.strftime('%Y-%m-%d')
|
||||
self._values[time] = self._rsi.current.value
|
||||
|
||||
# Since the symbol resolution is hour and the symbol is equity, we know the last bar received in a day will
|
||||
# be at the market close, this is 16h. We need to count how many daily bars were consolidated in order to know
|
||||
# how many we need to request from the history
|
||||
if self.time.hour == 16:
|
||||
self.count += 1
|
||||
|
||||
def on_end_of_algorithm(self):
|
||||
if not self._indicators_compared:
|
||||
raise Exception(f"Indicators {self._rsi.name} and {self._rsi_timedelta.name} should have been compared, but they were not. Please make sure the indicators are getting SPY data")
|
||||
@@ -21,14 +21,44 @@ class ConsolidateRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
# Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
|
||||
def initialize(self):
|
||||
self.set_start_date(2013, 10, 8)
|
||||
self.set_end_date(2013, 10, 20)
|
||||
self.set_start_date(2020, 1, 5)
|
||||
self.set_end_date(2020, 1, 20)
|
||||
|
||||
SP500 = Symbol.create(Futures.Indices.SP_500_E_MINI, SecurityType.FUTURE, Market.CME)
|
||||
self._symbol = _symbol = self.future_chain_provider.get_future_contract_list(SP500, self.start_date)[0]
|
||||
self.add_future_contract(_symbol)
|
||||
symbol = self.future_chain_provider.get_future_contract_list(SP500, self.start_date)[0]
|
||||
self._future = self.add_future_contract(symbol)
|
||||
|
||||
self._consolidation_counts = [0] * 6
|
||||
tradable_dates_count = len(list(Time.each_tradeable_day_in_time_zone(self._future.exchange.hours,
|
||||
self.start_date,
|
||||
self.end_date,
|
||||
self._future.exchange.time_zone,
|
||||
False)));
|
||||
self._expected_consolidation_counts = [];
|
||||
|
||||
self.consolidate(symbol, Calendar.MONTHLY, lambda bar: self.update_monthly_consolidator(bar, -1)) # shouldn't consolidate
|
||||
|
||||
self.consolidate(symbol, Calendar.WEEKLY, TickType.TRADE, lambda bar: self.update_weekly_consolidator(bar))
|
||||
|
||||
self.consolidate(symbol, Resolution.DAILY, lambda bar: self.update_trade_bar(bar, 0))
|
||||
self._expected_consolidation_counts.append(tradable_dates_count)
|
||||
|
||||
self.consolidate(symbol, Resolution.DAILY, TickType.QUOTE, lambda bar: self.update_quote_bar(bar, 1))
|
||||
self._expected_consolidation_counts.append(tradable_dates_count)
|
||||
|
||||
self.consolidate(symbol, timedelta(1), lambda bar: self.update_trade_bar(bar, 2))
|
||||
self._expected_consolidation_counts.append(tradable_dates_count - 1)
|
||||
|
||||
self.consolidate(symbol, timedelta(1), TickType.QUOTE, lambda bar: self.update_quote_bar(bar, 3))
|
||||
self._expected_consolidation_counts.append(tradable_dates_count - 1)
|
||||
|
||||
# sending None tick type
|
||||
self.consolidate(symbol, timedelta(1), None, lambda bar: self.update_trade_bar(bar, 4))
|
||||
self._expected_consolidation_counts.append(tradable_dates_count - 1)
|
||||
|
||||
self.consolidate(symbol, Resolution.DAILY, None, lambda bar: self.update_trade_bar(bar, 5))
|
||||
self._expected_consolidation_counts.append(tradable_dates_count)
|
||||
|
||||
self._consolidation_counts = [0] * len(self._expected_consolidation_counts)
|
||||
self._smas = [SimpleMovingAverage(10) for x in self._consolidation_counts]
|
||||
self._last_sma_updates = [datetime.min for x in self._consolidation_counts]
|
||||
self._monthly_consolidator_sma = SimpleMovingAverage(10)
|
||||
@@ -37,26 +67,9 @@ class ConsolidateRegressionAlgorithm(QCAlgorithm):
|
||||
self._weekly_consolidation_count = 0
|
||||
self._last_weekly_sma_update = datetime.min
|
||||
|
||||
self.consolidate(_symbol, Calendar.MONTHLY, lambda bar: self.update_monthly_consolidator(bar, -1)) # shouldn't consolidate
|
||||
|
||||
self.consolidate(_symbol, Calendar.WEEKLY, TickType.TRADE, lambda bar: self.update_weekly_consolidator(bar))
|
||||
|
||||
self.consolidate(_symbol, Resolution.DAILY, lambda bar: self.update_trade_bar(bar, 0))
|
||||
|
||||
self.consolidate(_symbol, Resolution.DAILY, TickType.QUOTE, lambda bar: self.update_quote_bar(bar, 1))
|
||||
|
||||
self.consolidate(_symbol, timedelta(1), lambda bar: self.update_trade_bar(bar, 2))
|
||||
|
||||
self.consolidate(_symbol, timedelta(1), TickType.QUOTE, lambda bar: self.update_quote_bar(bar, 3))
|
||||
|
||||
# sending None tick type
|
||||
self.consolidate(_symbol, timedelta(1), None, lambda bar: self.update_trade_bar(bar, 4))
|
||||
|
||||
self.consolidate(_symbol, Resolution.DAILY, None, lambda bar: self.update_trade_bar(bar, 5))
|
||||
|
||||
# custom data
|
||||
self._custom_data_consolidator = 0
|
||||
custom_symbol = self.add_data(Bitcoin, "BTC", Resolution.MINUTE).symbol
|
||||
custom_symbol = self.add_data(Bitcoin, "BTC", Resolution.DAILY).symbol
|
||||
self.consolidate(custom_symbol, timedelta(1), lambda bar: self.increment_counter(1))
|
||||
|
||||
self._custom_data_consolidator2 = 0
|
||||
@@ -87,18 +100,25 @@ class ConsolidateRegressionAlgorithm(QCAlgorithm):
|
||||
self._last_weekly_sma_update = bar.end_time
|
||||
self._weekly_consolidation_count += 1
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
expected_consolidations = 9
|
||||
expected_weekly_consolidations = 1
|
||||
if (any(i != expected_consolidations for i in self._consolidation_counts) or
|
||||
self._weekly_consolidation_count != expected_weekly_consolidations or
|
||||
self._custom_data_consolidator == 0 or
|
||||
self._custom_data_consolidator2 == 0):
|
||||
raise ValueError("Unexpected consolidation count")
|
||||
def on_end_of_algorithm(self):
|
||||
for i, expected_consolidation_count in enumerate(self._expected_consolidation_counts):
|
||||
consolidation_count = self._consolidation_counts[i]
|
||||
if consolidation_count != expected_consolidation_count:
|
||||
raise ValueError(f"Unexpected consolidation count for index {i}: expected {expected_consolidation_count} but was {consolidation_count}")
|
||||
|
||||
expected_weekly_consolidations = (self.end_date - self.start_date).days // 7
|
||||
if self._weekly_consolidation_count != expected_weekly_consolidations:
|
||||
raise ValueError(f"Expected {expected_weekly_consolidations} weekly consolidations but found {self._weekly_consolidation_count}")
|
||||
|
||||
if self._custom_data_consolidator == 0:
|
||||
raise ValueError("Custom data consolidator did not consolidate any data")
|
||||
|
||||
if self._custom_data_consolidator2 == 0:
|
||||
raise ValueError("Custom data consolidator 2 did not consolidate any data")
|
||||
|
||||
for i, sma in enumerate(self._smas):
|
||||
if sma.samples != expected_consolidations:
|
||||
raise Exception(f"Expected {expected_consolidations} samples in each SMA but found {sma.samples} in SMA in index {i}")
|
||||
if sma.samples != self._expected_consolidation_counts[i]:
|
||||
raise Exception(f"Expected {self._expected_consolidation_counts[i]} samples in each SMA but found {sma.samples} in SMA in index {i}")
|
||||
|
||||
last_update = self._last_sma_updates[i]
|
||||
if sma.current.time != last_update:
|
||||
@@ -115,5 +135,5 @@ class ConsolidateRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
# on_data event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
def on_data(self, data):
|
||||
if not self.portfolio.invested:
|
||||
self.set_holdings(self._symbol, 0.5)
|
||||
if not self.portfolio.invested and self._future.has_data:
|
||||
self.set_holdings(self._future.symbol, 0.5)
|
||||
|
||||
@@ -25,7 +25,7 @@ class ContinuousFutureRegressionAlgorithm(QCAlgorithm):
|
||||
self.set_start_date(2013, 7, 1)
|
||||
self.set_end_date(2014, 1, 1)
|
||||
|
||||
self._mappings = []
|
||||
self._previous_mapped_contract_symbols = []
|
||||
self._last_date_log = -1
|
||||
self._continuous_contract = self.add_future(Futures.Indices.SP_500_E_MINI,
|
||||
data_normalization_mode = DataNormalizationMode.BACKWARDS_RATIO,
|
||||
@@ -45,7 +45,7 @@ class ContinuousFutureRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
for changed_event in data.symbol_changed_events.values():
|
||||
if changed_event.symbol == self._continuous_contract.symbol:
|
||||
self._mappings.append(changed_event)
|
||||
self._previous_mapped_contract_symbols.append(self.symbol(changed_event.old_symbol))
|
||||
self.log(f"SymbolChanged event: {changed_event}")
|
||||
|
||||
if self._current_mapped_symbol == self._continuous_contract.mapped:
|
||||
@@ -77,5 +77,14 @@ class ContinuousFutureRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
def on_end_of_algorithm(self):
|
||||
expected_mapping_counts = 2
|
||||
if len(self._mappings) != expected_mapping_counts:
|
||||
raise ValueError(f"Unexpected symbol changed events: {self._mappings.count()}, was expecting {expected_mapping_counts}")
|
||||
if len(self._previous_mapped_contract_symbols) != expected_mapping_counts:
|
||||
raise ValueError(f"Unexpected symbol changed events: {len(self._previous_mapped_contract_symbols)}, was expecting {expected_mapping_counts}")
|
||||
|
||||
delisted_securities = [
|
||||
[security for security in self.securities.total if security.symbol == symbol][0]
|
||||
for symbol in self._previous_mapped_contract_symbols
|
||||
]
|
||||
marked_delisted_securities = [security for security in delisted_securities if security.is_delisted and not security.is_tradable]
|
||||
if len(marked_delisted_securities) != len(delisted_securities):
|
||||
raise ValueError(f"Not all delisted contracts are properly marked as delisted and non-tradable: "
|
||||
f"only {len(marked_delisted_securities)} are marked, was expecting {len(delisted_securities)}")
|
||||
|
||||
@@ -24,8 +24,8 @@ from AlgorithmImports import *
|
||||
class CustomDataPropertiesRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
def initialize(self):
|
||||
self.set_start_date(2011,9,13) # Set Start Date
|
||||
self.set_end_date(2015,12,1) # Set End Date
|
||||
self.set_start_date(2020, 1, 5) # Set Start Date
|
||||
self.set_end_date(2020, 1, 10) # Set End Date
|
||||
self.set_cash(100000) # Set Strategy Cash
|
||||
|
||||
# Define our custom data properties and exchange hours
|
||||
@@ -70,7 +70,9 @@ class Bitcoin(PythonData):
|
||||
|
||||
#return "http://my-ftp-server.com/futures-data-" + date.to_string("Ymd") + ".zip"
|
||||
# OR simply return a fixed small data file. Large files will slow down your backtest
|
||||
return SubscriptionDataSource("https://www.quantconnect.com/api/v2/proxy/quandl/api/v3/datasets/BCHARTS/BITSTAMPUSD.csv?order=asc&api_key=WyAazVXnq7ATy_fefTqm", SubscriptionTransportMedium.REMOTE_FILE)
|
||||
subscription = SubscriptionDataSource("https://www.quantconnect.com/api/v2/proxy/nasdaq/api/v3/datatables/QDL/BITFINEX.csv?code=BTCUSD&api_key=WyAazVXnq7ATy_fefTqm")
|
||||
subscription.Sort = True
|
||||
return subscription
|
||||
|
||||
|
||||
def reader(self, config, line, date, is_live_mode):
|
||||
@@ -103,22 +105,22 @@ class Bitcoin(PythonData):
|
||||
return None
|
||||
|
||||
# Example Line Format:
|
||||
# Date Open High Low Close Volume (BTC) Volume (Currency) Weighted Price
|
||||
# 2011-09-13 5.8 6.0 5.65 5.97 58.37138238, 346.0973893944 5.929230648356
|
||||
if not (line.strip() and line[0].isdigit()): return None
|
||||
#code date high low mid last bid ask volume
|
||||
#BTCUSD 2024-10-08 63248.0 61940.0 62246.5 62245.0 62246.0 62247.0 5.929230648356
|
||||
if not (line.strip() and line[7].isdigit()): return None
|
||||
|
||||
try:
|
||||
data = line.split(',')
|
||||
coin.time = datetime.strptime(data[0], "%Y-%m-%d")
|
||||
coin.time = datetime.strptime(data[1], "%Y-%m-%d")
|
||||
coin.end_time = coin.time + timedelta(days=1)
|
||||
coin.value = float(data[4])
|
||||
coin["Open"] = float(data[1])
|
||||
coin.value = float(data[5])
|
||||
coin["High"] = float(data[2])
|
||||
coin["Low"] = float(data[3])
|
||||
coin["Close"] = float(data[4])
|
||||
coin["VolumeBTC"] = float(data[5])
|
||||
coin["VolumeUSD"] = float(data[6])
|
||||
coin["WeightedPrice"] = float(data[7])
|
||||
coin["Mid"] = float(data[4])
|
||||
coin["Close"] = float(data[5])
|
||||
coin["Bid"] = float(data[6])
|
||||
coin["Ask"] = float(data[7])
|
||||
coin["VolumeBTC"] = float(data[8])
|
||||
return coin
|
||||
|
||||
except ValueError:
|
||||
|
||||
@@ -25,8 +25,8 @@ class CustomDataRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
def initialize(self):
|
||||
|
||||
self.set_start_date(2011,9,14) # Set Start Date
|
||||
self.set_end_date(2015,12,1) # Set End Date
|
||||
self.set_start_date(2020,1,5) # Set Start Date
|
||||
self.set_end_date(2020,1,10) # Set End Date
|
||||
self.set_cash(100000) # Set Strategy Cash
|
||||
|
||||
resolution = Resolution.SECOND if self.live_mode else Resolution.DAILY
|
||||
@@ -54,7 +54,7 @@ class CustomDataRegressionAlgorithm(QCAlgorithm):
|
||||
raise ValueError("Security was not warmed up!")
|
||||
|
||||
class Bitcoin(PythonData):
|
||||
'''Custom Data Type: Bitcoin data from Quandl - http://www.quandl.com/help/api-for-bitcoin-data'''
|
||||
'''Custom Data Type: Bitcoin data from Quandl - https://data.nasdaq.com/databases/BCHAIN'''
|
||||
|
||||
def get_source(self, config, date, is_live_mode):
|
||||
if is_live_mode:
|
||||
@@ -62,7 +62,9 @@ class Bitcoin(PythonData):
|
||||
|
||||
#return "http://my-ftp-server.com/futures-data-" + date.to_string("Ymd") + ".zip"
|
||||
# OR simply return a fixed small data file. Large files will slow down your backtest
|
||||
return SubscriptionDataSource("https://www.quantconnect.com/api/v2/proxy/quandl/api/v3/datasets/BCHARTS/BITSTAMPUSD.csv?order=asc&api_key=WyAazVXnq7ATy_fefTqm", SubscriptionTransportMedium.REMOTE_FILE)
|
||||
subscription = SubscriptionDataSource("https://www.quantconnect.com/api/v2/proxy/nasdaq/api/v3/datatables/QDL/BITFINEX.csv?code=BTCUSD&api_key=WyAazVXnq7ATy_fefTqm")
|
||||
subscription.Sort = True
|
||||
return subscription
|
||||
|
||||
|
||||
def reader(self, config, line, date, is_live_mode):
|
||||
@@ -95,22 +97,22 @@ class Bitcoin(PythonData):
|
||||
return None
|
||||
|
||||
# Example Line Format:
|
||||
# Date Open High Low Close Volume (BTC) Volume (Currency) Weighted Price
|
||||
# 2011-09-13 5.8 6.0 5.65 5.97 58.37138238, 346.0973893944 5.929230648356
|
||||
if not (line.strip() and line[0].isdigit()): return None
|
||||
# code date high low mid last bid ask volume
|
||||
# BTCUSD 2024-10-08 63248.0 61940.0 62246.5 62245.0 62246.0 62247.0 477.91102114
|
||||
if not (line.strip() and line[7].isdigit()): return None
|
||||
|
||||
try:
|
||||
data = line.split(',')
|
||||
coin.time = datetime.strptime(data[0], "%Y-%m-%d")
|
||||
coin.time = datetime.strptime(data[1], "%Y-%m-%d")
|
||||
coin.end_time = coin.time + timedelta(days=1)
|
||||
coin.value = float(data[4])
|
||||
coin["Open"] = float(data[1])
|
||||
coin.value = float(data[5])
|
||||
coin["High"] = float(data[2])
|
||||
coin["Low"] = float(data[3])
|
||||
coin["Close"] = float(data[4])
|
||||
coin["VolumeBTC"] = float(data[5])
|
||||
coin["VolumeUSD"] = float(data[6])
|
||||
coin["WeightedPrice"] = float(data[7])
|
||||
coin["Mid"] = float(data[4])
|
||||
coin["Close"] = float(data[5])
|
||||
coin["Bid"] = float(data[6])
|
||||
coin["Ask"] = float(data[7])
|
||||
coin["VolumeBTC"] = float(data[8])
|
||||
return coin
|
||||
|
||||
except ValueError:
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
# 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
|
||||
|
||||
from AlgorithmImports import *
|
||||
|
||||
|
||||
### <summary>
|
||||
### The regression algorithm showcases the utilization of a custom data source with the Sort flag set to true.
|
||||
### This means that the source initially provides data in descending order, which is then organized into ascending order and returned in the 'on_data' function.
|
||||
### </summary>
|
||||
class DescendingCustomDataObjectStoreRegressionAlgorithm(QCAlgorithm):
|
||||
descending_custom_data = [
|
||||
"2024-10-03 19:00:00,173.5,176.0,172.0,175.2,120195681,4882.29",
|
||||
"2024-10-02 18:00:00,174.0,177.0,173.0,175.8,116275729,4641.97",
|
||||
"2024-10-01 17:00:00,175.0,178.0,172.5,174.5,127707078,6591.27",
|
||||
"2024-09-30 11:00:00,174.8,176.5,172.8,175.0,127707078,6591.27",
|
||||
"2024-09-27 10:00:00,172.5,175.0,171.5,173.5,120195681,4882.29",
|
||||
"2024-09-26 09:00:00,171.0,172.5,170.0,171.8,117516350,4820.53",
|
||||
"2024-09-25 08:00:00,169.5,172.0,169.0,171.0,110427867,4661.55",
|
||||
"2024-09-24 07:00:00,170.0,171.0,168.0,169.5,127624733,4823.52",
|
||||
"2024-09-23 06:00:00,172.0,173.5,169.5,171.5,123586417,4303.93",
|
||||
"2024-09-20 05:00:00,168.0,171.0,167.5,170.5,151929179,5429.87",
|
||||
"2024-09-19 04:00:00,170.5,171.5,166.0,167.0,160523863,5219.24",
|
||||
"2024-09-18 03:00:00,173.0,174.0,169.0,172.0,145721790,5163.09",
|
||||
"2024-09-17 02:00:00,171.0,173.5,170.0,172.5,144794030,5405.72",
|
||||
"2024-09-16 01:00:00,168.0,171.0,167.0,170.0,214402430,8753.33",
|
||||
"2024-09-13 16:00:00,173.5,176.0,172.0,175.2,120195681,4882.29",
|
||||
"2024-09-12 15:00:00,174.5,177.5,173.5,176.5,171728134,7774.83",
|
||||
"2024-09-11 14:00:00,175.0,178.0,174.0,175.5,191516153,8349.59",
|
||||
"2024-09-10 13:00:00,174.5,176.0,173.0,174.0,151162819,5915.8",
|
||||
"2024-09-09 12:00:00,176.0,178.0,175.0,177.0,116275729,4641.97"
|
||||
]
|
||||
|
||||
def initialize(self):
|
||||
self.set_start_date(2024, 9, 9)
|
||||
self.set_end_date(2024, 10, 3)
|
||||
self.set_cash(100000)
|
||||
|
||||
self.set_benchmark(lambda x: 0)
|
||||
|
||||
SortCustomData.custom_data_key = self.get_custom_data_key()
|
||||
|
||||
self.custom_symbol = self.add_data(SortCustomData, "SortCustomData", Resolution.DAILY).symbol
|
||||
|
||||
# Saving data here for demonstration and regression testing purposes.
|
||||
# In real scenarios, data has to be saved to the object store before the algorithm starts.
|
||||
self.object_store.save(self.get_custom_data_key(), "\n".join(self.descending_custom_data))
|
||||
|
||||
self.received_data = []
|
||||
|
||||
def on_data(self, slice: Slice):
|
||||
if slice.contains_key(self.custom_symbol):
|
||||
custom_data = slice.get(SortCustomData, self.custom_symbol)
|
||||
if custom_data.open == 0 or custom_data.high == 0 or custom_data.low == 0 or custom_data.close == 0 or custom_data.price == 0:
|
||||
raise Exception("One or more custom data fields (open, high, low, close, price) are zero.")
|
||||
|
||||
self.received_data.append(custom_data)
|
||||
|
||||
def on_end_of_algorithm(self):
|
||||
if not self.received_data:
|
||||
raise Exception("Custom data was not fetched")
|
||||
|
||||
# Make sure history requests work as expected
|
||||
history = self.history(SortCustomData, self.custom_symbol, self.start_date, self.end_date, Resolution.DAILY)
|
||||
|
||||
if history.shape[0] != len(self.received_data):
|
||||
raise Exception("History request returned more or less data than expected")
|
||||
|
||||
# Iterate through the history collection, checking if the time is in ascending order.
|
||||
for i in range(len(history) - 1):
|
||||
# [1] - time
|
||||
if history.index[i][1] > history.index[i + 1][1]:
|
||||
raise RegressionTestException(
|
||||
f"Order failure: {history.index[i][1]} > {history.index[i + 1][1]} at index {i}.")
|
||||
|
||||
def get_custom_data_key(self):
|
||||
return "CustomData/SortCustomData"
|
||||
|
||||
|
||||
class SortCustomData(PythonData):
|
||||
custom_data_key = ""
|
||||
|
||||
def get_source(self, config, date, is_live):
|
||||
subscription = SubscriptionDataSource(self.custom_data_key, SubscriptionTransportMedium.OBJECT_STORE,
|
||||
FileFormat.CSV)
|
||||
# Indicate that the data from the subscription will be returned in descending order.
|
||||
subscription.Sort = True
|
||||
return subscription
|
||||
|
||||
def reader(self, config, line, date, is_live):
|
||||
data = line.split(',')
|
||||
obj_data = SortCustomData()
|
||||
obj_data.symbol = config.symbol
|
||||
obj_data.time = datetime.strptime(data[0], '%Y-%m-%d %H:%M:%S')
|
||||
obj_data.value = float(data[4])
|
||||
obj_data["Open"] = float(data[1])
|
||||
obj_data["High"] = float(data[2])
|
||||
obj_data["Low"] = float(data[3])
|
||||
obj_data["Close"] = float(data[4])
|
||||
return obj_data
|
||||
@@ -125,12 +125,9 @@ class ETFConstituentUniverseFrameworkRegressionAlgorithm(QCAlgorithm):
|
||||
self.universe_settings.resolution = Resolution.HOUR
|
||||
universe = self.add_universe(self.universe.etf(spy, self.universe_settings, self.filter_etf_constituents))
|
||||
|
||||
historical_data = self.history(universe, 1)
|
||||
if len(historical_data) != 1:
|
||||
raise ValueError(f"Unexpected history count {len(historical_data)}! Expected 1")
|
||||
for universe_data_collection in historical_data:
|
||||
if len(universe_data_collection) < 200:
|
||||
raise ValueError(f"Unexpected universe DataCollection count {len(universe_data_collection)}! Expected > 200")
|
||||
historical_data = self.history(universe, 1, flatten=True)
|
||||
if len(historical_data) < 200:
|
||||
raise ValueError(f"Unexpected universe DataCollection count {len(historical_data)}! Expected > 200")
|
||||
|
||||
### <summary>
|
||||
### Filters ETF constituents
|
||||
|
||||
@@ -49,7 +49,7 @@ class FundamentalRegressionAlgorithm(QCAlgorithm):
|
||||
raise ValueError(f"Unexpected Fundamental count {len(fundamentals)}! Expected 2")
|
||||
|
||||
# Request historical fundamental data for symbols
|
||||
history = self.history(Fundamental, TimeSpan(2, 0, 0, 0))
|
||||
history = self.history(Fundamental, timedelta(days=2))
|
||||
if len(history) != 4:
|
||||
raise ValueError(f"Unexpected Fundamental history count {len(history)}! Expected 4")
|
||||
|
||||
@@ -69,26 +69,28 @@ class FundamentalRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
def assert_fundamental_universe_data(self):
|
||||
# Case A
|
||||
universe_data_per_time = self.history(self._universe.data_type, [self._universe.symbol], TimeSpan(2, 0, 0, 0))
|
||||
if len(universe_data_per_time) != 2:
|
||||
raise ValueError(f"Unexpected Fundamentals history count {len(universe_data_per_time)}! Expected 2")
|
||||
|
||||
for universe_data_collection in universe_data_per_time:
|
||||
self.assert_fundamental_enumerator(universe_data_collection, "A")
|
||||
universe_data = self.history(self._universe.data_type, [self._universe.symbol], timedelta(days=2), flatten=True)
|
||||
self.assert_fundamental_history(universe_data, "A")
|
||||
|
||||
# Case B (sugar on A)
|
||||
universe_data_per_time = self.history(self._universe, TimeSpan(2, 0, 0, 0))
|
||||
if len(universe_data_per_time) != 2:
|
||||
raise ValueError(f"Unexpected Fundamentals history count {len(universe_data_per_time)}! Expected 2")
|
||||
|
||||
for universe_data_collection in universe_data_per_time:
|
||||
self.assert_fundamental_enumerator(universe_data_collection, "B")
|
||||
universe_data_per_time = self.history(self._universe, timedelta(days=2), flatten=True)
|
||||
self.assert_fundamental_history(universe_data_per_time, "B")
|
||||
|
||||
# Case C: Passing through the unvierse type and symbol
|
||||
enumerable_of_data_dictionary = self.history[self._universe.data_type]([self._universe.symbol], 100)
|
||||
for selection_collection_for_a_day in enumerable_of_data_dictionary:
|
||||
self.assert_fundamental_enumerator(selection_collection_for_a_day[self._universe.symbol], "C")
|
||||
|
||||
def assert_fundamental_history(self, df, case_name):
|
||||
dates = df.index.get_level_values('time').unique()
|
||||
if dates.shape[0] != 2:
|
||||
raise ValueError(f"Unexpected Fundamental universe dates count {dates.shape[0]}! Expected 2")
|
||||
|
||||
for date in dates:
|
||||
sub_df = df.loc[date]
|
||||
if sub_df.shape[0] < 7000:
|
||||
raise ValueError(f"Unexpected historical Fundamentals data count {sub_df.shape[0]} case {case_name}! Expected > 7000")
|
||||
|
||||
def assert_fundamental_enumerator(self, enumerable, case_name):
|
||||
data_point_count = 0
|
||||
for fundamental in enumerable:
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# 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.
|
||||
|
||||
# region imports
|
||||
from AlgorithmImports import *
|
||||
from Execution.ImmediateExecutionModel import ImmediateExecutionModel
|
||||
from QuantConnect.Orders import OrderEvent
|
||||
# endregion
|
||||
|
||||
### <summary>
|
||||
### Regression algorithm to test ImmediateExecutionModel places orders with the
|
||||
### correct quantity (taking into account the fee's) so that the fill quantity
|
||||
### is the expected one.
|
||||
### </summary>
|
||||
class ImmediateExecutionModelWorksWithBinanceFeeModel(QCAlgorithm):
|
||||
|
||||
def Initialize(self):
|
||||
# *** initial configurations and backtest ***
|
||||
self.SetStartDate(2022, 12, 13) # Set Start Date
|
||||
self.SetEndDate(2022, 12, 14) # Set End Date
|
||||
self.SetAccountCurrency("BUSD") # Set Account Currency
|
||||
self.SetCash("BUSD", 100000, 1) # Set Strategy Cash
|
||||
|
||||
self.universe_settings.resolution = Resolution.MINUTE
|
||||
|
||||
symbols = [ Symbol.create("BTCBUSD", SecurityType.CRYPTO, Market.BINANCE) ]
|
||||
|
||||
# set algorithm framework models
|
||||
self.set_universe_selection(ManualUniverseSelectionModel(symbols))
|
||||
self.set_alpha(ConstantAlphaModel(InsightType.PRICE, InsightDirection.UP, timedelta(minutes = 20), 0.025, None))
|
||||
|
||||
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel(Resolution.MINUTE))
|
||||
self.set_execution(ImmediateExecutionModel())
|
||||
|
||||
|
||||
self.SetBrokerageModel(BrokerageName.Binance, AccountType.Margin)
|
||||
|
||||
def on_order_event(self, order_event: OrderEvent) -> None:
|
||||
if order_event.status == OrderStatus.FILLED:
|
||||
if abs(order_event.quantity - 5.8) > 0.01:
|
||||
raise Exception(f"The expected quantity was 5.8 but the quantity from the order was {order_event.quantity}")
|
||||
@@ -0,0 +1,29 @@
|
||||
# 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.
|
||||
|
||||
from datetime import datetime
|
||||
from AlgorithmImports import *
|
||||
|
||||
from OptionChainApisConsistencyRegressionAlgorithm import OptionChainApisConsistencyRegressionAlgorithm
|
||||
|
||||
### <summary>
|
||||
### Regression algorithm asserting that the option chain APIs return consistent values for index options.
|
||||
### See QCAlgorithm.OptionChain(Symbol) and QCAlgorithm.OptionChainProvider
|
||||
### </summary>
|
||||
class IndexOptionChainApisConsistencyRegressionAlgorithm(OptionChainApisConsistencyRegressionAlgorithm):
|
||||
|
||||
def get_test_date(self) -> datetime:
|
||||
return datetime(2021, 1, 4)
|
||||
|
||||
def get_option(self) -> Option:
|
||||
return self.add_index_option("SPX")
|
||||
@@ -0,0 +1,57 @@
|
||||
# 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.
|
||||
|
||||
from datetime import datetime
|
||||
from AlgorithmImports import *
|
||||
|
||||
### <summary>
|
||||
### Regression algorithm asserting that the option chain APIs return consistent values.
|
||||
### See QCAlgorithm.OptionChain(Symbol) and QCAlgorithm.OptionChainProvider
|
||||
### </summary>
|
||||
class OptionChainApisConsistencyRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
def initialize(self) -> None:
|
||||
test_date = self.get_test_date()
|
||||
|
||||
self.set_start_date(test_date)
|
||||
self.set_end_date(test_date)
|
||||
|
||||
option = self.get_option()
|
||||
|
||||
option_chain_from_algorithm_api = [x.symbol for x in self.option_chain(option.symbol).contracts.values()]
|
||||
|
||||
exchange_time = Extensions.convert_from_utc(self.utc_time, option.exchange.time_zone)
|
||||
option_chain_from_provider_api = list(self.option_chain_provider.get_option_contract_list(option.symbol, exchange_time))
|
||||
|
||||
if len(option_chain_from_algorithm_api) == 0:
|
||||
raise AssertionError("No options in chain from algorithm API")
|
||||
|
||||
if len(option_chain_from_provider_api) == 0:
|
||||
raise AssertionError("No options in chain from provider API")
|
||||
|
||||
if len(option_chain_from_algorithm_api) != len(option_chain_from_provider_api):
|
||||
raise AssertionError(f"Expected {len(option_chain_from_provider_api)} options in chain from provider API, "
|
||||
f"but got {len(option_chain_from_algorithm_api)}");
|
||||
|
||||
for i in range(len(option_chain_from_algorithm_api)):
|
||||
symbol1 = option_chain_from_algorithm_api[i]
|
||||
symbol2 = option_chain_from_provider_api[i]
|
||||
|
||||
if symbol1 != symbol2:
|
||||
raise AssertionError(f"Expected {symbol2} in chain from provider API, but got {symbol1}")
|
||||
|
||||
def get_test_date(self) -> datetime:
|
||||
return datetime(2015, 12, 25)
|
||||
|
||||
def get_option(self) -> Option:
|
||||
return self.add_option("GOOG")
|
||||
@@ -12,6 +12,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
from AlgorithmImports import *
|
||||
from datetime import timedelta
|
||||
|
||||
### <summary>
|
||||
### Regression algorithm illustrating the usage of the <see cref="QCAlgorithm.OptionChain(Symbol)"/> method
|
||||
@@ -27,14 +28,16 @@ class OptionChainFullDataRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
goog = self.add_equity("GOOG").symbol
|
||||
|
||||
option_chain = self.option_chain(goog, flatten=True)
|
||||
|
||||
# Demonstration using data frame:
|
||||
df = option_chain.data_frame
|
||||
# Get contracts expiring within 10 days, with an implied volatility greater than 0.5 and a delta less than 0.5
|
||||
contracts = [
|
||||
contract_data
|
||||
for contract_data in self.option_chain(goog)
|
||||
if contract_data.id.date - self.time <= timedelta(days=10) and contract_data.implied_volatility > 0.5 and contract_data.greeks.delta < 0.5
|
||||
]
|
||||
# Get the contract with the latest expiration date
|
||||
self._option_contract = sorted(contracts, key=lambda x: x.id.date, reverse=True)[0]
|
||||
contracts = df.loc[(df.expiry <= self.time + timedelta(days=10)) & (df.impliedvolatility > 0.5) & (df.delta < 0.5)]
|
||||
|
||||
# Get the contract with the latest expiration date.
|
||||
# Note: the result of df.loc[] is a series, and its name is a tuple with a single element (contract symbol)
|
||||
self._option_contract = contracts.loc[contracts.expiry.idxmax()].name
|
||||
|
||||
self.add_option_contract(self._option_contract)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ class OptionChainedUniverseSelectionModelRegressionAlgorithm(QCAlgorithm):
|
||||
self.set_start_date(2014, 6, 6)
|
||||
self.set_end_date(2014, 6, 6)
|
||||
self.set_cash(100000)
|
||||
|
||||
|
||||
universe = self.add_universe("my-minute-universe-name", lambda time: [ "AAPL", "TWX" ])
|
||||
self.add_universe_selection(
|
||||
OptionChainedUniverseSelectionModel(
|
||||
@@ -34,9 +34,9 @@ class OptionChainedUniverseSelectionModelRegressionAlgorithm(QCAlgorithm):
|
||||
.expiration(0, 180))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def on_data(self, slice):
|
||||
if self.portfolio.invested or not (self.is_market_open("AAPL") and self.is_market_open("AAPL")): return
|
||||
if self.portfolio.invested or not (self.is_market_open("AAPL") and self.is_market_open("TWX")): return
|
||||
values = list(map(lambda x: x.value, filter(lambda x: x.key == "?AAPL" or x.key == "?TWX", slice.option_chains)))
|
||||
for chain in values:
|
||||
# we sort the contracts to find at the money (ATM) contract with farthest expiration
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user