Compare commits
136 Commits
9328
...
feature-py
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be6ead75e9 | ||
|
|
25f7ccf0c2 | ||
|
|
afb0a86291 | ||
|
|
f5c5faa3d4 | ||
|
|
c471d1ced0 | ||
|
|
5c345faea7 | ||
|
|
11bed1f0c3 | ||
|
|
ec57799a5d | ||
|
|
4c8aa638eb | ||
|
|
7c3caec07f | ||
|
|
45f71543bd | ||
|
|
13b9d91ecb | ||
|
|
f239018e77 | ||
|
|
f1c98b848a | ||
|
|
2c7bde422a | ||
|
|
9e95bfea90 | ||
|
|
b820ff4de8 | ||
|
|
8218a2be99 | ||
|
|
9e3031c562 | ||
|
|
2cf9239b6e | ||
|
|
4c63c60a89 | ||
|
|
9a2e47b05c | ||
|
|
711d46d6e2 | ||
|
|
5d6625c1ea | ||
|
|
0a70611d81 | ||
|
|
43c6d5cc5a | ||
|
|
f39acceadc | ||
|
|
921ddced04 | ||
|
|
ff8563244f | ||
|
|
041f1d9db5 | ||
|
|
eb1181f5f7 | ||
|
|
9d4014b7db | ||
|
|
a4f66628fd | ||
|
|
b9974e6f54 | ||
|
|
474c5cd890 | ||
|
|
0dfb368cb4 | ||
|
|
fbf5300bb6 | ||
|
|
c67845bd45 | ||
|
|
54ddbfbe24 | ||
|
|
bd7be31ede | ||
|
|
041a111b92 | ||
|
|
fa94bcc46a | ||
|
|
2988f7ec3c | ||
|
|
b785877d33 | ||
|
|
5dbb0db2f9 | ||
|
|
a489436743 | ||
|
|
ff2013b0d0 | ||
|
|
567e7b7679 | ||
|
|
646f22a63f | ||
|
|
fd138ec397 | ||
|
|
e98b31fc4c | ||
|
|
d2eae2f652 | ||
|
|
0f0a2bc9a8 | ||
|
|
8ad81dca71 | ||
|
|
f965f34a3f | ||
|
|
06a7d54c38 | ||
|
|
7174fcb9d7 | ||
|
|
29e9d678f2 | ||
|
|
098ac7d0a9 | ||
|
|
6cb4411f6e | ||
|
|
46ef9a9dbb | ||
|
|
d8d8134437 | ||
|
|
9a30c9bd5f | ||
|
|
f76a0efb0e | ||
|
|
7238fcd0f3 | ||
|
|
91e8393aac | ||
|
|
5771635265 | ||
|
|
914486fdb6 | ||
|
|
61fda8b62c | ||
|
|
124ac3b98e | ||
|
|
9dca43bccb | ||
|
|
6efeee07dc | ||
|
|
c452dd3726 | ||
|
|
c602fd0a3f | ||
|
|
104071cda5 | ||
|
|
a5d9526d65 | ||
|
|
8e525c63fc | ||
|
|
d0e9134cc9 | ||
|
|
883d354a98 | ||
|
|
76a53eb096 | ||
|
|
c02ee1b0d8 | ||
|
|
9167882ab2 | ||
|
|
854b987cd0 | ||
|
|
a26414d273 | ||
|
|
84264ca7ef | ||
|
|
b2ed398687 | ||
|
|
e8c316cbcf | ||
|
|
724e52c0b3 | ||
|
|
4252c79e45 | ||
|
|
cbb40dfa43 | ||
|
|
8792fa2600 | ||
|
|
20e9fd7899 | ||
|
|
e05a6bffd0 | ||
|
|
c2f0fdc47a | ||
|
|
b1b8da1e17 | ||
|
|
01a0454c57 | ||
|
|
90e2c48404 | ||
|
|
888c443264 | ||
|
|
03efc1b735 | ||
|
|
6ef2ead929 | ||
|
|
bfd319c91e | ||
|
|
5f61456df8 | ||
|
|
a46a551c03 | ||
|
|
cf9b547e2e | ||
|
|
faa4e91e04 | ||
|
|
cc83f19528 | ||
|
|
54af12b06a | ||
|
|
1d1c8f5f82 | ||
|
|
3966c0e91f | ||
|
|
28160e1301 | ||
|
|
f4679785a5 | ||
|
|
79b9009452 | ||
|
|
e5b5f80d9d | ||
|
|
027fde8f09 | ||
|
|
c7ccd60bf2 | ||
|
|
b30bb3fcf5 | ||
|
|
40a87eb056 | ||
|
|
035b29fdf5 | ||
|
|
f2fc1aae9e | ||
|
|
ff5fc5db5d | ||
|
|
40c3062348 | ||
|
|
371f2cd469 | ||
|
|
090ceb131e | ||
|
|
6885fd130b | ||
|
|
a450cea8d0 | ||
|
|
934128cfa0 | ||
|
|
c7a74306fb | ||
|
|
cfacc755fa | ||
|
|
194ed59cbe | ||
|
|
776caea1d3 | ||
|
|
460fd626a6 | ||
|
|
1e3a1e3c43 | ||
|
|
e20725b969 | ||
|
|
151901bbd1 | ||
|
|
fb2f846159 | ||
|
|
d03ac0fd90 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -271,3 +271,6 @@ QuantConnect.Lean.sln.DotSettings*
|
||||
|
||||
#User notebook files
|
||||
Research/Notebooks
|
||||
|
||||
#Docker result files
|
||||
Results/
|
||||
18
.idea/Lean.iml
generated
18
.idea/Lean.iml
generated
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/Algorithm.Python" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/Algorithm.Python/stubs" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
<component name="TestRunnerService">
|
||||
<option name="PROJECT_TEST_RUNNER" value="pytest" />
|
||||
</component>
|
||||
</module>
|
||||
14
.idea/readme.md
generated
14
.idea/readme.md
generated
@@ -91,14 +91,14 @@ From a terminal; Pycharm has a built in terminal on the bottom taskbar labeled *
|
||||
|
||||
2. Using the **run_docker.cfg** to store args for repeated use; any blank entries will resort to default values! example: **_./run_docker.bat run_docker.cfg_**
|
||||
|
||||
image=quantconnect/lean:latest
|
||||
config_file=
|
||||
data_dir=
|
||||
results_dir=
|
||||
debugging=
|
||||
python_dir=
|
||||
IMAGE=quantconnect/lean:latest
|
||||
CONFIG_FILE=
|
||||
DATA_DIR=
|
||||
RESULTS_DIR=
|
||||
DEBUGGING=
|
||||
PYTHON_DIR=
|
||||
|
||||
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat debugging=y_**
|
||||
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat DEBUGGING=y_**
|
||||
* Accepted args for inline include all listed in the file in #2; must follow the **key=value** format
|
||||
|
||||
<br />
|
||||
|
||||
@@ -16,6 +16,7 @@ before_install:
|
||||
- conda install -y cython=0.29.15
|
||||
- conda install -y scipy=1.4.1
|
||||
- conda install -y wrapt=1.12.1
|
||||
- pip install pyarrow==1.0.1
|
||||
install:
|
||||
- nuget restore QuantConnect.Lean.sln
|
||||
- nuget install NUnit.Runners -Version 3.11.1 -OutputDirectory testrunner
|
||||
@@ -23,4 +24,4 @@ script:
|
||||
- msbuild /p:Configuration=Release /p:VbcToolExe=vbnc.exe QuantConnect.Lean.sln
|
||||
- mono ./testrunner/NUnit.ConsoleRunner.3.11.1/tools/nunit3-console.exe ./Tests/bin/Release/QuantConnect.Tests.dll --where "cat != TravisExclude" --labels=Off
|
||||
- chmod +x ci_build_stubs.sh
|
||||
- sudo -E ./ci_build_stubs.sh -ipy -g -p
|
||||
- sudo -E ./ci_build_stubs.sh -d -t -g -p
|
||||
|
||||
@@ -106,14 +106,14 @@ From a terminal launch the run_docker.bat/.sh script; there are a few choices on
|
||||
|
||||
2. Using the **run_docker.cfg** to store args for repeated use; any blank entries will resort to default values! example: **_./run_docker.bat run_docker.cfg_**
|
||||
|
||||
image=quantconnect/lean:latest
|
||||
config_file=
|
||||
data_dir=
|
||||
results_dir=
|
||||
debugging=
|
||||
python_dir=
|
||||
IMAGE=quantconnect/lean:latest
|
||||
CONFIG_FILE=
|
||||
DATA_DIR=
|
||||
RESULTS_DIR=
|
||||
DEBUGGING=
|
||||
PYTHON_DIR=
|
||||
|
||||
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat debugging=y_**
|
||||
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat DEBUGGING=y_**
|
||||
* Accepted args for inline include all listed in the file in #2
|
||||
|
||||
<br />
|
||||
|
||||
34
.vscode/readme.md
vendored
34
.vscode/readme.md
vendored
@@ -101,14 +101,14 @@ This section will cover how to actually launch Lean in the container with your d
|
||||
|
||||
<h3>Option 1 (Recommended)</h3>
|
||||
|
||||
In VS Code click on the debug/run icon on the left toolbar, at the top you should see a drop down menu with launch options, be sure to select **Debug in Container**. This option will kick off a launch script that will start the docker. With this specific launch option the parameters are already configured in VS Codes **tasks.json** under the **run-docker** task args. These set arguements are:
|
||||
In VS Code click on the debug/run icon on the left toolbar, at the top you should see a drop down menu with launch options, be sure to select **Debug in Container**. This option will kick off a launch script that will start the docker. With this specific launch option the parameters are already configured in VS Codes **tasks.json** under the **run-docker** task args. These set arguments are:
|
||||
|
||||
"image=quantconnect/lean:latest",
|
||||
"config_file=${workspaceFolder}/Launcher/config.json",
|
||||
"data_dir=${workspaceFolder}/Data",
|
||||
"results_dir=${workspaceFolder}/",
|
||||
"debugging=Y",
|
||||
"python_location=${workspaceFolder}/Algorithm.Python"
|
||||
"IMAGE=quantconnect/lean:latest",
|
||||
"CONFIG_FILE=${workspaceFolder}/Launcher/config.json",
|
||||
"DATA_DIR=${workspaceFolder}/Data",
|
||||
"RESULTS_DIR=${workspaceFolder}/Results",
|
||||
"DEBUGGING=Y",
|
||||
"PYHTON_DIR=${workspaceFolder}/Algorithm.Python"
|
||||
|
||||
As defaults these are all great! Feel free to change them as needed for your setup.
|
||||
|
||||
@@ -120,21 +120,21 @@ From a terminal launch the run_docker.bat/.sh script; there are a few choices on
|
||||
1. Launch with no parameters and answer the questions regarding configuration (Press enter for defaults)
|
||||
|
||||
* Enter docker image [default: quantconnect/lean:latest]:
|
||||
* Enter absolute path to Lean config file [default: _~currentDir_\Launcher\config.json]:
|
||||
* Enter absolute path to Data folder [default: ~_currentDir_\Data\]:
|
||||
* Enter absolute path to store results [default: ~_currentDir_\]:
|
||||
* Enter absolute path to Lean config file [default: .\Launcher\config.json]:
|
||||
* Enter absolute path to Data folder [default: .\Data\]:
|
||||
* Enter absolute path to store results [default: .\Results]:
|
||||
* Would you like to debug C#? (Requires mono debugger attachment) [default: N]:
|
||||
|
||||
2. Using the **run_docker.cfg** to store args for repeated use; any blank entries will resort to default values! example: **_./run_docker.bat run_docker.cfg_**
|
||||
|
||||
image=quantconnect/lean:latest
|
||||
config_file=
|
||||
data_dir=
|
||||
results_dir=
|
||||
debugging=
|
||||
python_dir=
|
||||
IMAGE=quantconnect/lean:latest
|
||||
CONFIG_FILE=
|
||||
DATA_DIR=
|
||||
RESULTS_DIR=
|
||||
DEBUGGING=
|
||||
PYTHON_DIR=
|
||||
|
||||
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat debugging=y_**
|
||||
3. Inline arguments; anything you don't enter will use the default args! example: **_./run_docker.bat DEBUGGING=y_**
|
||||
* Accepted args for inline include all listed in the file in #2
|
||||
|
||||
<br />
|
||||
|
||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"python.autoComplete.extraPaths": [
|
||||
"Algorithm.Python/stubs"
|
||||
]
|
||||
}
|
||||
14
.vscode/tasks.json
vendored
14
.vscode/tasks.json
vendored
@@ -51,13 +51,13 @@
|
||||
"command": "${workspaceFolder}/run_docker.sh"
|
||||
},
|
||||
"args": [
|
||||
"image=quantconnect/lean:latest",
|
||||
"config_file=${workspaceFolder}/Launcher/config.json",
|
||||
"data_dir=${workspaceFolder}/Data",
|
||||
"results_dir=${workspaceFolder}/",
|
||||
"debugging=Y",
|
||||
"python_dir=${workspaceFolder}/Algorithm.Python",
|
||||
"exit=Y"
|
||||
"IMAGE=quantconnect/lean:latest",
|
||||
"CONFIG_FILE=${workspaceFolder}/Launcher/config.json",
|
||||
"DATA_DIR=${workspaceFolder}/Data",
|
||||
"RESULTS_DIR=${workspaceFolder}/Results",
|
||||
"DEBUGGING=Y",
|
||||
"PYTHON_DIR=${workspaceFolder}/Algorithm.Python",
|
||||
"EXIT=Y"
|
||||
],
|
||||
"problemMatcher": [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* 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>
|
||||
/// This regression algorithm tests that we receive the expected data when
|
||||
/// we add future option contracts individually using <see cref="AddFutureOptionContract"/>
|
||||
/// </summary>
|
||||
public class AddFutureOptionContractDataStreamingRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private bool _onDataReached;
|
||||
private bool _invested;
|
||||
private Symbol _es20h20;
|
||||
private Symbol _es19m20;
|
||||
|
||||
private readonly HashSet<Symbol> _symbolsReceived = new HashSet<Symbol>();
|
||||
private readonly HashSet<Symbol> _expectedSymbolsReceived = new HashSet<Symbol>();
|
||||
private readonly Dictionary<Symbol, List<QuoteBar>> _dataReceived = new Dictionary<Symbol, List<QuoteBar>>();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 1, 6);
|
||||
|
||||
_es20h20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2020, 3, 20)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
_es19m20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
var optionChains = OptionChainProvider.GetOptionContractList(_es20h20, Time)
|
||||
.Concat(OptionChainProvider.GetOptionContractList(_es19m20, Time));
|
||||
|
||||
foreach (var optionContract in optionChains)
|
||||
{
|
||||
_expectedSymbolsReceived.Add(AddFutureOptionContract(optionContract, Resolution.Minute).Symbol);
|
||||
}
|
||||
|
||||
if (_expectedSymbolsReceived.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Expected Symbols receive count is 0, expected >0");
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (!data.HasData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_onDataReached = true;
|
||||
|
||||
var hasOptionQuoteBars = false;
|
||||
foreach (var qb in data.QuoteBars.Values)
|
||||
{
|
||||
if (qb.Symbol.SecurityType != SecurityType.FutureOption)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hasOptionQuoteBars = true;
|
||||
|
||||
_symbolsReceived.Add(qb.Symbol);
|
||||
if (!_dataReceived.ContainsKey(qb.Symbol))
|
||||
{
|
||||
_dataReceived[qb.Symbol] = new List<QuoteBar>();
|
||||
}
|
||||
|
||||
_dataReceived[qb.Symbol].Add(qb);
|
||||
}
|
||||
|
||||
if (_invested || !hasOptionQuoteBars)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.ContainsKey(_es20h20) && data.ContainsKey(_es19m20))
|
||||
{
|
||||
SetHoldings(_es20h20, 0.2);
|
||||
SetHoldings(_es19m20, 0.2);
|
||||
|
||||
_invested = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
base.OnEndOfAlgorithm();
|
||||
|
||||
if (!_onDataReached)
|
||||
{
|
||||
throw new Exception("OnData() was never called.");
|
||||
}
|
||||
if (_symbolsReceived.Count != _expectedSymbolsReceived.Count)
|
||||
{
|
||||
throw new AggregateException($"Expected {_expectedSymbolsReceived.Count} option contracts Symbols, found {_symbolsReceived.Count}");
|
||||
}
|
||||
|
||||
var missingSymbols = new List<Symbol>();
|
||||
foreach (var expectedSymbol in _expectedSymbolsReceived)
|
||||
{
|
||||
if (!_symbolsReceived.Contains(expectedSymbol))
|
||||
{
|
||||
missingSymbols.Add(expectedSymbol);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingSymbols.Count > 0)
|
||||
{
|
||||
throw new Exception($"Symbols: \"{string.Join(", ", missingSymbols)}\" were not found in OnData");
|
||||
}
|
||||
|
||||
foreach (var expectedSymbol in _expectedSymbolsReceived)
|
||||
{
|
||||
var data = _dataReceived[expectedSymbol];
|
||||
var nonDupeDataCount = data.Select(x =>
|
||||
{
|
||||
x.EndTime = default(DateTime);
|
||||
return x;
|
||||
}).Distinct().Count();
|
||||
|
||||
if (nonDupeDataCount < 1000)
|
||||
{
|
||||
throw new Exception($"Received too few data points. Expected >=1000, found {nonDupeDataCount} for {expectedSymbol}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "217.585%"},
|
||||
{"Drawdown", "0.600%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0.635%"},
|
||||
{"Sharpe 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", "-14.395"},
|
||||
{"Tracking Error", "0.043"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$7.40"},
|
||||
{"Fitness Score", "1"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "3.199"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1074366800"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* 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;
|
||||
using QuantConnect.Securities.Future;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests that we only receive the option chain for a single future contract
|
||||
/// in the option universe filter.
|
||||
/// </summary>
|
||||
public class AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private bool _invested;
|
||||
private bool _onDataReached;
|
||||
private bool _optionFilterRan;
|
||||
private readonly HashSet<Symbol> _symbolsReceived = new HashSet<Symbol>();
|
||||
private readonly HashSet<Symbol> _expectedSymbolsReceived = new HashSet<Symbol>();
|
||||
private readonly Dictionary<Symbol, List<QuoteBar>> _dataReceived = new Dictionary<Symbol, List<QuoteBar>>();
|
||||
|
||||
private Future _es;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 1, 6);
|
||||
|
||||
_es = AddFuture(Futures.Indices.SP500EMini, Resolution.Minute, Market.CME);
|
||||
_es.SetFilter((futureFilter) =>
|
||||
{
|
||||
return futureFilter.Expiration(0, 365).ExpirationCycle(new[] { 3, 6 });
|
||||
});
|
||||
|
||||
AddFutureOption(_es.Symbol, optionContracts =>
|
||||
{
|
||||
_optionFilterRan = true;
|
||||
|
||||
var expiry = new HashSet<DateTime>(optionContracts.Select(x => x.Underlying.ID.Date)).SingleOrDefault();
|
||||
// Cast to IEnumerable<Symbol> because OptionFilterContract overrides some LINQ operators like `Select` and `Where`
|
||||
// and cause it to mutate the underlying Symbol collection when using those operators.
|
||||
var symbol = new HashSet<Symbol>(((IEnumerable<Symbol>)optionContracts).Select(x => x.Underlying)).SingleOrDefault();
|
||||
|
||||
if (expiry == null || symbol == null)
|
||||
{
|
||||
throw new InvalidOperationException("Expected a single Option contract in the chain, found 0 contracts");
|
||||
}
|
||||
|
||||
var enumerator = optionContracts.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
_expectedSymbolsReceived.Add(enumerator.Current);
|
||||
}
|
||||
|
||||
return optionContracts;
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (!data.HasData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_onDataReached = true;
|
||||
|
||||
var hasOptionQuoteBars = false;
|
||||
foreach (var qb in data.QuoteBars.Values)
|
||||
{
|
||||
if (qb.Symbol.SecurityType != SecurityType.FutureOption)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
hasOptionQuoteBars = true;
|
||||
|
||||
_symbolsReceived.Add(qb.Symbol);
|
||||
if (!_dataReceived.ContainsKey(qb.Symbol))
|
||||
{
|
||||
_dataReceived[qb.Symbol] = new List<QuoteBar>();
|
||||
}
|
||||
|
||||
_dataReceived[qb.Symbol].Add(qb);
|
||||
}
|
||||
|
||||
if (_invested || !hasOptionQuoteBars)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var chain in data.OptionChains.Values)
|
||||
{
|
||||
var futureInvested = false;
|
||||
var optionInvested = false;
|
||||
|
||||
foreach (var option in chain.Contracts.Keys)
|
||||
{
|
||||
if (futureInvested && optionInvested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var future = option.Underlying;
|
||||
|
||||
if (!optionInvested && data.ContainsKey(option))
|
||||
{
|
||||
MarketOrder(option, 1);
|
||||
_invested = true;
|
||||
optionInvested = true;
|
||||
}
|
||||
if (!futureInvested && data.ContainsKey(future))
|
||||
{
|
||||
MarketOrder(future, 1);
|
||||
_invested = true;
|
||||
futureInvested = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
base.OnEndOfAlgorithm();
|
||||
|
||||
if (!_optionFilterRan)
|
||||
{
|
||||
throw new InvalidOperationException("Option chain filter was never ran");
|
||||
}
|
||||
if (!_onDataReached)
|
||||
{
|
||||
throw new Exception("OnData() was never called.");
|
||||
}
|
||||
if (_symbolsReceived.Count != _expectedSymbolsReceived.Count)
|
||||
{
|
||||
throw new AggregateException($"Expected {_expectedSymbolsReceived.Count} option contracts Symbols, found {_symbolsReceived.Count}");
|
||||
}
|
||||
|
||||
var missingSymbols = new List<Symbol>();
|
||||
foreach (var expectedSymbol in _expectedSymbolsReceived)
|
||||
{
|
||||
if (!_symbolsReceived.Contains(expectedSymbol))
|
||||
{
|
||||
missingSymbols.Add(expectedSymbol);
|
||||
}
|
||||
}
|
||||
|
||||
if (missingSymbols.Count > 0)
|
||||
{
|
||||
throw new Exception($"Symbols: \"{string.Join(", ", missingSymbols)}\" were not found in OnData");
|
||||
}
|
||||
|
||||
foreach (var expectedSymbol in _expectedSymbolsReceived)
|
||||
{
|
||||
var data = _dataReceived[expectedSymbol];
|
||||
var nonDupeDataCount = data.Select(x =>
|
||||
{
|
||||
x.EndTime = default(DateTime);
|
||||
return x;
|
||||
}).Distinct().Count();
|
||||
|
||||
if (nonDupeDataCount < 1000)
|
||||
{
|
||||
throw new Exception($"Received too few data points. Expected >=1000, found {nonDupeDataCount} for {expectedSymbol}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "-15.625%"},
|
||||
{"Drawdown", "0.200%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "-0.093%"},
|
||||
{"Sharpe Ratio", "-11.181"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.002"},
|
||||
{"Beta", "-0.016"},
|
||||
{"Annual Standard Deviation", "0.001"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-14.343"},
|
||||
{"Tracking Error", "0.044"},
|
||||
{"Treynor Ratio", "0.479"},
|
||||
{"Total Fees", "$3.70"},
|
||||
{"Fitness Score", "0.41"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-185.654"},
|
||||
{"Portfolio Turnover", "0.821"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1532330301"}
|
||||
};
|
||||
}
|
||||
}
|
||||
164
Algorithm.CSharp/AddOptionContractExpiresRegressionAlgorithm.cs
Normal file
164
Algorithm.CSharp/AddOptionContractExpiresRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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 QuantConnect.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// We add an option contract using <see cref="QCAlgorithm.AddOptionContract"/> and place a trade and wait till it expires
|
||||
/// later will liquidate the resulting equity position and assert both option and underlying get removed
|
||||
/// </summary>
|
||||
public class AddOptionContractExpiresRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private DateTime _expiration = new DateTime(2014, 06, 21);
|
||||
private Symbol _option;
|
||||
private Symbol _twx;
|
||||
private bool _traded;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2014, 06, 05);
|
||||
SetEndDate(2014, 06, 30);
|
||||
|
||||
_twx = QuantConnect.Symbol.Create("TWX", SecurityType.Equity, Market.USA);
|
||||
|
||||
AddUniverse("my-daily-universe-name", time => new List<string> { "AAPL" });
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (_option == null)
|
||||
{
|
||||
var option = OptionChainProvider.GetOptionContractList(_twx, Time)
|
||||
.OrderBy(symbol => symbol.ID.Symbol)
|
||||
.FirstOrDefault(optionContract => optionContract.ID.Date == _expiration
|
||||
&& optionContract.ID.OptionRight == OptionRight.Call
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American);
|
||||
if (option != null)
|
||||
{
|
||||
_option = AddOptionContract(option).Symbol;
|
||||
}
|
||||
}
|
||||
|
||||
if (_option != null && Securities[_option].Price != 0 && !_traded)
|
||||
{
|
||||
_traded = true;
|
||||
Buy(_option, 1);
|
||||
|
||||
foreach (var symbol in new [] { _option, _option.Underlying })
|
||||
{
|
||||
var config = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol).ToList();
|
||||
|
||||
if (!config.Any())
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {symbol}");
|
||||
}
|
||||
if (config.Any(dataConfig => dataConfig.DataNormalizationMode != DataNormalizationMode.Raw))
|
||||
{
|
||||
throw new Exception($"Was expecting DataNormalizationMode.Raw configurations for {symbol}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Time.Date > _expiration)
|
||||
{
|
||||
if (SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_option).Any())
|
||||
{
|
||||
throw new Exception($"Unexpected configurations for {_option} after it has been delisted");
|
||||
}
|
||||
|
||||
if (Securities[_twx].Invested)
|
||||
{
|
||||
if (!SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_twx).Any())
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {_twx}");
|
||||
}
|
||||
|
||||
// first we liquidate the option exercised position
|
||||
Liquidate(_twx);
|
||||
}
|
||||
}
|
||||
else if (Time.Date > _expiration && !Securities[_twx].Invested)
|
||||
{
|
||||
if (SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_twx).Any())
|
||||
{
|
||||
throw new Exception($"Unexpected configurations for {_twx} after it has been liquidated");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "3"},
|
||||
{"Average Win", "2.73%"},
|
||||
{"Average Loss", "-2.98%"},
|
||||
{"Compounding Annual Return", "-4.619%"},
|
||||
{"Drawdown", "0.300%"},
|
||||
{"Expectancy", "-0.042"},
|
||||
{"Net Profit", "-0.332%"},
|
||||
{"Sharpe Ratio", "-3.7"},
|
||||
{"Probabilistic Sharpe Ratio", "0.563%"},
|
||||
{"Loss Rate", "50%"},
|
||||
{"Win Rate", "50%"},
|
||||
{"Profit-Loss Ratio", "0.92"},
|
||||
{"Alpha", "-0.023"},
|
||||
{"Beta", "0.005"},
|
||||
{"Annual Standard Deviation", "0.006"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-3.424"},
|
||||
{"Tracking Error", "0.057"},
|
||||
{"Treynor Ratio", "-4.775"},
|
||||
{"Total Fees", "$2.00"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-43.418"},
|
||||
{"Return Over Maximum Drawdown", "-14.274"},
|
||||
{"Portfolio Turnover", "0.007"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1185639451"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// We add an option contract using <see cref="QCAlgorithm.AddOptionContract"/> and place a trade, the underlying
|
||||
/// gets deselected from the universe selection but should still be present since we manually added the option contract.
|
||||
/// Later we call <see cref="QCAlgorithm.RemoveOptionContract"/> and expect both option and underlying to be removed.
|
||||
/// </summary>
|
||||
public class AddOptionContractFromUniverseRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private DateTime _expiration = new DateTime(2014, 06, 21);
|
||||
private SecurityChanges _securityChanges = SecurityChanges.None;
|
||||
private Symbol _option;
|
||||
private Symbol _aapl;
|
||||
private Symbol _twx;
|
||||
private bool _traded;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_twx = QuantConnect.Symbol.Create("TWX", SecurityType.Equity, Market.USA);
|
||||
_aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
|
||||
UniverseSettings.Resolution = Resolution.Minute;
|
||||
UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw;
|
||||
|
||||
SetStartDate(2014, 06, 05);
|
||||
SetEndDate(2014, 06, 09);
|
||||
|
||||
AddUniverse(enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl },
|
||||
enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl });
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (_option != null && Securities[_option].Price != 0 && !_traded)
|
||||
{
|
||||
_traded = true;
|
||||
Buy(_option, 1);
|
||||
}
|
||||
|
||||
if (Time.Date > new DateTime(2014, 6, 5))
|
||||
{
|
||||
if (Time < new DateTime(2014, 6, 6, 14, 0, 0))
|
||||
{
|
||||
var configs = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(_twx);
|
||||
// assert underlying still there after the universe selection removed it, still used by the manually added option contract
|
||||
if (!configs.Any())
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {_twx}" +
|
||||
$" even after it has been deselected from coarse universe because we still have the option contract.");
|
||||
}
|
||||
}
|
||||
else if (Time == new DateTime(2014, 6, 6, 14, 0, 0))
|
||||
{
|
||||
// liquidate & remove the option
|
||||
RemoveOptionContract(_option);
|
||||
}
|
||||
// assert underlying was finally removed
|
||||
else if(Time > new DateTime(2014, 6, 6, 14, 0, 0))
|
||||
{
|
||||
foreach (var symbol in new[] { _option, _option.Underlying })
|
||||
{
|
||||
var configs = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol);
|
||||
if (configs.Any())
|
||||
{
|
||||
throw new Exception($"Unexpected configuration for {symbol} after it has been deselected from coarse universe and option contract is removed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSecuritiesChanged(SecurityChanges changes)
|
||||
{
|
||||
if (_securityChanges.RemovedSecurities.Intersect(changes.RemovedSecurities).Any())
|
||||
{
|
||||
throw new Exception($"SecurityChanges.RemovedSecurities intersect {changes.RemovedSecurities}. We expect no duplicate!");
|
||||
}
|
||||
if (_securityChanges.AddedSecurities.Intersect(changes.AddedSecurities).Any())
|
||||
{
|
||||
throw new Exception($"SecurityChanges.AddedSecurities intersect {changes.RemovedSecurities}. We expect no duplicate!");
|
||||
}
|
||||
// keep track of all removed and added securities
|
||||
_securityChanges += changes;
|
||||
|
||||
if (changes.AddedSecurities.Any(security => security.Symbol.SecurityType == SecurityType.Option))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var addedSecurity in changes.AddedSecurities)
|
||||
{
|
||||
var option = OptionChainProvider.GetOptionContractList(addedSecurity.Symbol, Time)
|
||||
.OrderBy(symbol => symbol.ID.Symbol)
|
||||
.First(optionContract => optionContract.ID.Date == _expiration
|
||||
&& optionContract.ID.OptionRight == OptionRight.Call
|
||||
&& optionContract.ID.OptionStyle == OptionStyle.American);
|
||||
AddOptionContract(option);
|
||||
|
||||
foreach (var symbol in new[] { option, option.Underlying })
|
||||
{
|
||||
var config = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(symbol).ToList();
|
||||
|
||||
if (!config.Any())
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {symbol}");
|
||||
}
|
||||
if (config.Any(dataConfig => dataConfig.DataNormalizationMode != DataNormalizationMode.Raw))
|
||||
{
|
||||
throw new Exception($"Was expecting DataNormalizationMode.Raw configurations for {symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
// just keep the first we got
|
||||
if (_option == null)
|
||||
{
|
||||
_option = option;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (SubscriptionManager.Subscriptions.Any(dataConfig => dataConfig.Symbol == _twx || dataConfig.Symbol.Underlying == _twx))
|
||||
{
|
||||
throw new Exception($"Was NOT expecting any configurations for {_twx} or it's options, since we removed the contract");
|
||||
}
|
||||
|
||||
if (SubscriptionManager.Subscriptions.All(dataConfig => dataConfig.Symbol != _aapl))
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {_aapl}");
|
||||
}
|
||||
if (SubscriptionManager.Subscriptions.All(dataConfig => dataConfig.Symbol.Underlying != _aapl))
|
||||
{
|
||||
throw new Exception($"Was expecting options configurations for {_aapl}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-0.23%"},
|
||||
{"Compounding Annual Return", "-15.596%"},
|
||||
{"Drawdown", "0.200%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-0.232%"},
|
||||
{"Sharpe Ratio", "-7.739"},
|
||||
{"Probabilistic Sharpe Ratio", "1.216%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.027"},
|
||||
{"Beta", "-0.174"},
|
||||
{"Annual Standard Deviation", "0.006"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-11.586"},
|
||||
{"Tracking Error", "0.042"},
|
||||
{"Treynor Ratio", "0.286"},
|
||||
{"Total Fees", "$2.00"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-19.883"},
|
||||
{"Return Over Maximum Drawdown", "-67.224"},
|
||||
{"Portfolio Turnover", "0.014"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "721476625"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Custom.Quiver;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp.AltData
|
||||
{
|
||||
/// <summary>
|
||||
/// Quiver Quantitative is a provider of alternative data.
|
||||
/// This algorithm shows how to consume the <see cref="QuiverWallStreetBets"/>
|
||||
/// </summary>
|
||||
public class QuiverWallStreetBetsDataAlgorithm : QCAlgorithm
|
||||
{
|
||||
/// <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(2019, 1, 1);
|
||||
SetEndDate(2020, 6, 1);
|
||||
SetCash(100000);
|
||||
|
||||
var aapl = AddEquity("AAPL", Resolution.Daily).Symbol;
|
||||
var quiverWSBSymbol = AddData<QuiverWallStreetBets>(aapl).Symbol;
|
||||
var history = History<QuiverWallStreetBets>(quiverWSBSymbol, 60, Resolution.Daily);
|
||||
|
||||
Debug($"We got {history.Count()} items from our history request");
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
var points = data.Get<QuiverWallStreetBets>();
|
||||
foreach (var point in points.Values)
|
||||
{
|
||||
// Go long in the stock if it was mentioned more than 5 times in the WallStreetBets daily discussion
|
||||
if (point.Mentions > 5)
|
||||
{
|
||||
SetHoldings(point.Symbol.Underlying, 1);
|
||||
}
|
||||
// Go short in the stock if it was mentioned less than 5 times in the WallStreetBets daily discussion
|
||||
if (point.Mentions < 5)
|
||||
{
|
||||
SetHoldings(point.Symbol.Underlying, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
{
|
||||
if (!_equityBought && data.ContainsKey(_spy)) {
|
||||
//Buy our Equity
|
||||
var quantity = CalculateOrderQuantity(_spy, .1m);
|
||||
@@ -114,7 +114,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
/// <param name="orderEvent">OrderEvent object that contains all the information about the event</param>
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
{
|
||||
// Get the order from our transactions
|
||||
var order = Transactions.GetOrderById(orderEvent.OrderId);
|
||||
|
||||
@@ -147,7 +147,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
// All PartiallyFilled orders should have a LastFillTime
|
||||
case OrderStatus.PartiallyFilled:
|
||||
if (order.LastFillTime == null)
|
||||
if (order.LastFillTime == null)
|
||||
{
|
||||
throw new Exception("LastFillTime should not be null");
|
||||
}
|
||||
@@ -183,9 +183,9 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
throw new Exception("OptionExercise order price should be strike price!!");
|
||||
}
|
||||
|
||||
if (orderEvent.Quantity != 1)
|
||||
if (orderEvent.Quantity != -1)
|
||||
{
|
||||
throw new Exception("OrderEvent Quantity should be 1");
|
||||
throw new Exception("OrderEvent Quantity should be -1");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Treynor Ratio", "-0.018"},
|
||||
{"Total Fees", "$2.00"},
|
||||
{"Fitness Score", "0.213"},
|
||||
{"OrderListHash", "-1514011542"}
|
||||
{"OrderListHash", "904167951"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "498372354"}
|
||||
{"OrderListHash", "-1575550889"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,4 +119,4 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"OrderListHash", "491919591"}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,12 +222,12 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$85.33"},
|
||||
{"Total Fees", "$85.34"},
|
||||
{"Fitness Score", "0.5"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-43.937"},
|
||||
{"Return Over Maximum Drawdown", "-43.943"},
|
||||
{"Portfolio Turnover", "1.028"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
@@ -242,7 +242,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1073240275"}
|
||||
{"OrderListHash", "956597072"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,7 +141,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1935621950"}
|
||||
{"OrderListHash", "687310345"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +151,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-30.28"},
|
||||
{"Portfolio Turnover", "1.029"},
|
||||
{"Return Over Maximum Drawdown", "-30.158"},
|
||||
{"Portfolio Turnover", "1.033"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
@@ -166,7 +166,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1708974186"}
|
||||
{"OrderListHash", "1349023435"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Demonstration of how to chain a coarse and fine universe selection with an option chain universe selection model
|
||||
/// that will add and remove an <see cref="OptionChainUniverse"/> for each symbol selected on fine
|
||||
/// </summary>
|
||||
public class CoarseFineOptionUniverseChainRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
// initialize our changes to nothing
|
||||
private SecurityChanges _changes = SecurityChanges.None;
|
||||
private int _optionCount;
|
||||
private Symbol _lastEquityAdded;
|
||||
private Symbol _aapl;
|
||||
private Symbol _twx;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_twx = QuantConnect.Symbol.Create("TWX", SecurityType.Equity, Market.USA);
|
||||
_aapl = QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, Market.USA);
|
||||
UniverseSettings.Resolution = Resolution.Minute;
|
||||
|
||||
SetStartDate(2014, 06, 05);
|
||||
SetEndDate(2014, 06, 06);
|
||||
|
||||
var selectionUniverse = AddUniverse(enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl },
|
||||
enumerable => new[] { Time.Date <= new DateTime(2014, 6, 5) ? _twx : _aapl });
|
||||
|
||||
AddUniverseOptions(selectionUniverse, universe =>
|
||||
{
|
||||
if (universe.Underlying == null)
|
||||
{
|
||||
throw new Exception("Underlying data point is null! This shouldn't happen, each OptionChainUniverse handles and should provide this");
|
||||
}
|
||||
return universe.IncludeWeeklys()
|
||||
.FrontMonth()
|
||||
.Contracts(universe.Take(5));
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
// if we have no changes, do nothing
|
||||
if (_changes == SecurityChanges.None ||
|
||||
_changes.AddedSecurities.Any(security => security.Price == 0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// liquidate removed securities
|
||||
foreach (var security in _changes.RemovedSecurities)
|
||||
{
|
||||
if (security.Invested)
|
||||
{
|
||||
Liquidate(security.Symbol);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var security in _changes.AddedSecurities)
|
||||
{
|
||||
if (!security.Symbol.HasUnderlying)
|
||||
{
|
||||
_lastEquityAdded = security.Symbol;
|
||||
}
|
||||
else
|
||||
{
|
||||
// options added should all match prev added security
|
||||
if (security.Symbol.Underlying != _lastEquityAdded)
|
||||
{
|
||||
throw new Exception($"Unexpected symbol added {security.Symbol}");
|
||||
}
|
||||
|
||||
_optionCount++;
|
||||
}
|
||||
|
||||
SetHoldings(security.Symbol, 0.05m);
|
||||
|
||||
var config = SubscriptionManager.SubscriptionDataConfigService.GetSubscriptionDataConfigs(security.Symbol).ToList();
|
||||
|
||||
if (!config.Any())
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {security.Symbol}");
|
||||
}
|
||||
if (config.Any(dataConfig => dataConfig.DataNormalizationMode != DataNormalizationMode.Raw))
|
||||
{
|
||||
throw new Exception($"Was expecting DataNormalizationMode.Raw configurations for {security.Symbol}");
|
||||
}
|
||||
}
|
||||
_changes = SecurityChanges.None;
|
||||
}
|
||||
|
||||
public override void OnSecuritiesChanged(SecurityChanges changes)
|
||||
{
|
||||
_changes += changes;
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
var config = SubscriptionManager.Subscriptions.ToList();
|
||||
if (config.Any(dataConfig => dataConfig.Symbol == _twx || dataConfig.Symbol.Underlying == _twx))
|
||||
{
|
||||
throw new Exception($"Was NOT expecting any configurations for {_twx} or it's options, since coarse/fine should have deselected it");
|
||||
}
|
||||
|
||||
if (_optionCount == 0)
|
||||
{
|
||||
throw new Exception("Option universe chain did not add any option!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "13"},
|
||||
{"Average Win", "0.65%"},
|
||||
{"Average Loss", "-0.05%"},
|
||||
{"Compounding Annual Return", "3216040423556140000000000%"},
|
||||
{"Drawdown", "0.500%"},
|
||||
{"Expectancy", "1.393"},
|
||||
{"Net Profit", "32.840%"},
|
||||
{"Sharpe Ratio", "7.14272222483913E+15"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "83%"},
|
||||
{"Win Rate", "17%"},
|
||||
{"Profit-Loss Ratio", "13.36"},
|
||||
{"Alpha", "2.59468989671647E+16"},
|
||||
{"Beta", "67.661"},
|
||||
{"Annual Standard Deviation", "3.633"},
|
||||
{"Annual Variance", "13.196"},
|
||||
{"Information Ratio", "7.24987266907741E+15"},
|
||||
{"Tracking Error", "3.579"},
|
||||
{"Treynor Ratio", "383485597312030"},
|
||||
{"Total Fees", "$13.00"},
|
||||
{"Fitness Score", "0.232"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "0.232"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1630141557"}
|
||||
};
|
||||
}
|
||||
}
|
||||
138
Algorithm.CSharp/CustomBuyingPowerModelAlgorithm.cs
Normal file
138
Algorithm.CSharp/CustomBuyingPowerModelAlgorithm.cs
Normal file
@@ -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 QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Demonstration of using custom buying power model in backtesting.
|
||||
/// QuantConnect allows you to model all orders as deeply and accurately as you need.
|
||||
/// </summary>
|
||||
/// <meta name="tag" content="trading and orders" />
|
||||
/// <meta name="tag" content="transaction fees and slippage" />
|
||||
/// <meta name="tag" content="custom buying power models" />
|
||||
public class CustomBuyingPowerModelAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _spy;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 01);
|
||||
SetEndDate(2013, 10, 31);
|
||||
var security = AddEquity("SPY", Resolution.Hour);
|
||||
_spy = security.Symbol;
|
||||
|
||||
// set the buying power model
|
||||
security.SetBuyingPowerModel(new CustomBuyingPowerModel());
|
||||
}
|
||||
|
||||
public void OnData(Slice slice)
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var quantity = CalculateOrderQuantity(_spy, 1m);
|
||||
if (quantity % 100 != 0)
|
||||
{
|
||||
throw new Exception($"CustomBuyingPowerModel only allow quantity that is multiple of 100 and {quantity} was found");
|
||||
}
|
||||
|
||||
// We normally get insufficient buying power model, but the
|
||||
// CustomBuyingPowerModel always says that there is sufficient buying power for the orders
|
||||
MarketOrder(_spy, quantity * 10);
|
||||
}
|
||||
|
||||
public class CustomBuyingPowerModel : BuyingPowerModel
|
||||
{
|
||||
public override GetMaximumOrderQuantityResult GetMaximumOrderQuantityForTargetBuyingPower(
|
||||
GetMaximumOrderQuantityForTargetBuyingPowerParameters parameters)
|
||||
{
|
||||
var quantity = base.GetMaximumOrderQuantityForTargetBuyingPower(parameters).Quantity;
|
||||
quantity = Math.Floor(quantity / 100) * 100;
|
||||
return new GetMaximumOrderQuantityResult(quantity);
|
||||
}
|
||||
|
||||
public override HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(
|
||||
HasSufficientBuyingPowerForOrderParameters parameters)
|
||||
{
|
||||
return new HasSufficientBuyingPowerForOrderResult(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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "5672.520%"},
|
||||
{"Drawdown", "22.500%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "40.601%"},
|
||||
{"Sharpe Ratio", "40.201"},
|
||||
{"Probabilistic Sharpe Ratio", "77.339%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "41.848"},
|
||||
{"Beta", "9.224"},
|
||||
{"Annual Standard Deviation", "1.164"},
|
||||
{"Annual Variance", "1.355"},
|
||||
{"Information Ratio", "44.459"},
|
||||
{"Tracking Error", "1.04"},
|
||||
{"Treynor Ratio", "5.073"},
|
||||
{"Total Fees", "$30.00"},
|
||||
{"Fitness Score", "0.418"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "113.05"},
|
||||
{"Return Over Maximum Drawdown", "442.81"},
|
||||
{"Portfolio Turnover", "0.418"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "639761089"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,12 @@ using QuantConnect.Securities;
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Demonstration of using custom fee, slippage and fill models for modelling transactions in backtesting.
|
||||
/// Demonstration of using custom fee, slippage, fill, and buying power models for modelling transactions in backtesting.
|
||||
/// QuantConnect allows you to model all orders as deeply and accurately as you need.
|
||||
/// </summary>
|
||||
/// <meta name="tag" content="trading and orders" />
|
||||
/// <meta name="tag" content="transaction fees and slippage" />
|
||||
/// <meta name="tag" content="custom buying power models" />
|
||||
/// <meta name="tag" content="custom transaction models" />
|
||||
/// <meta name="tag" content="custom slippage models" />
|
||||
/// <meta name="tag" content="custom fee models" />
|
||||
@@ -50,6 +51,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
_security.SetFeeModel(new CustomFeeModel(this));
|
||||
_security.SetFillModel(new CustomFillModel(this));
|
||||
_security.SetSlippageModel(new CustomSlippageModel(this));
|
||||
_security.SetBuyingPowerModel(new CustomBuyingPowerModel(this));
|
||||
}
|
||||
|
||||
public void OnData(TradeBars data)
|
||||
@@ -60,13 +62,13 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
if (Time.Day > 10 && _security.Holdings.Quantity <= 0)
|
||||
{
|
||||
var quantity = CalculateOrderQuantity(_spy, .5m);
|
||||
Log("MarketOrder: " + quantity);
|
||||
Log($"MarketOrder: {quantity}");
|
||||
MarketOrder(_spy, quantity, asynchronous: true); // async needed for partial fill market orders
|
||||
}
|
||||
else if (Time.Day > 20 && _security.Holdings.Quantity >= 0)
|
||||
{
|
||||
var quantity = CalculateOrderQuantity(_spy, -.5m);
|
||||
Log("MarketOrder: " + quantity);
|
||||
Log($"MarketOrder: {quantity}");
|
||||
MarketOrder(_spy, quantity, asynchronous: true); // async needed for partial fill market orders
|
||||
}
|
||||
}
|
||||
@@ -109,7 +111,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
fill.Status = OrderStatus.PartiallyFilled;
|
||||
}
|
||||
|
||||
_algorithm.Log("CustomFillModel: " + fill);
|
||||
_algorithm.Log($"CustomFillModel: {fill}");
|
||||
|
||||
return fill;
|
||||
}
|
||||
@@ -131,7 +133,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
1m,
|
||||
parameters.Security.Price*parameters.Order.AbsoluteQuantity*0.00001m);
|
||||
|
||||
_algorithm.Log("CustomFeeModel: " + fee);
|
||||
_algorithm.Log($"CustomFeeModel: {fee}");
|
||||
return new OrderFee(new CashAmount(fee, "USD"));
|
||||
}
|
||||
}
|
||||
@@ -150,11 +152,31 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
// custom slippage math
|
||||
var slippage = asset.Price*0.0001m*(decimal) Math.Log10(2*(double) order.AbsoluteQuantity);
|
||||
|
||||
_algorithm.Log("CustomSlippageModel: " + slippage);
|
||||
_algorithm.Log($"CustomSlippageModel: {slippage}");
|
||||
return slippage;
|
||||
}
|
||||
}
|
||||
|
||||
public class CustomBuyingPowerModel : BuyingPowerModel
|
||||
{
|
||||
private readonly QCAlgorithm _algorithm;
|
||||
|
||||
public CustomBuyingPowerModel(QCAlgorithm algorithm)
|
||||
{
|
||||
_algorithm = algorithm;
|
||||
}
|
||||
|
||||
public override HasSufficientBuyingPowerForOrderResult HasSufficientBuyingPowerForOrder(
|
||||
HasSufficientBuyingPowerForOrderParameters parameters)
|
||||
{
|
||||
// custom behavior: this model will assume that there is always enough buying power
|
||||
var hasSufficientBuyingPowerForOrderResult = new HasSufficientBuyingPowerForOrderResult(true);
|
||||
_algorithm.Log($"CustomBuyingPowerModel: {hasSufficientBuyingPowerForOrderResult.IsSufficient}");
|
||||
|
||||
return hasSufficientBuyingPowerForOrderResult;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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>
|
||||
/// Algorithm simply fetch one-day history prior current time.
|
||||
/// </summary>
|
||||
public class DailyHistoryForDailyResolutionRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol[] _symbols = {
|
||||
QuantConnect.Symbol.Create("GBPUSD", SecurityType.Forex, market: Market.FXCM),
|
||||
QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, market: Market.Oanda),
|
||||
QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, market: Market.USA),
|
||||
QuantConnect.Symbol.Create("BTCUSD", SecurityType.Crypto, market: Market.GDAX),
|
||||
QuantConnect.Symbol.Create("XAUUSD", SecurityType.Cfd, market: Market.Oanda)
|
||||
};
|
||||
|
||||
private HashSet<Symbol> _received = new HashSet<Symbol>();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2018, 3, 26);
|
||||
SetEndDate(2018, 4, 10);
|
||||
foreach (var symbol in _symbols)
|
||||
{
|
||||
AddSecurity(symbol, Resolution.Daily);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
using (var enumerator = data.GetEnumerator())
|
||||
{
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var current = enumerator.Current;
|
||||
var symbol = current.Key;
|
||||
_received.Add(symbol);
|
||||
|
||||
List<BaseData> history;
|
||||
|
||||
if (current.Value.DataType == MarketDataType.QuoteBar)
|
||||
{
|
||||
history = History(1, Resolution.Daily).Get<QuoteBar>(symbol).Cast<BaseData>().ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
history = History(1, Resolution.Daily).Get<TradeBar>(symbol).Cast<BaseData>().ToList();
|
||||
}
|
||||
|
||||
if (!history.Any()) throw new Exception($"No {symbol} data on the eve of {Time} {Time.DayOfWeek}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (_received.Count != _symbols.Length)
|
||||
{
|
||||
throw new Exception($"Data for symbols {string.Join(",", _symbols.Except(_received))} were not 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 Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe 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.084"},
|
||||
{"Tracking Error", "0.183"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "0"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "371857150"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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>
|
||||
/// Algorithm simply fetch one-day history prior current time.
|
||||
/// </summary>
|
||||
public class DailyHistoryForMinuteResolutionRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol[] _symbols = {
|
||||
QuantConnect.Symbol.Create("GBPUSD", SecurityType.Forex, market: Market.FXCM),
|
||||
QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, market: Market.Oanda),
|
||||
QuantConnect.Symbol.Create("AAPL", SecurityType.Equity, market: Market.USA),
|
||||
QuantConnect.Symbol.Create("BTCUSD", SecurityType.Crypto, market: Market.GDAX),
|
||||
QuantConnect.Symbol.Create("XAUUSD", SecurityType.Cfd, market: Market.Oanda)
|
||||
};
|
||||
|
||||
private HashSet<Symbol> _received = new HashSet<Symbol>();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2018, 3, 26);
|
||||
SetEndDate(2018, 4, 10);
|
||||
foreach (var symbol in _symbols)
|
||||
{
|
||||
AddSecurity(symbol, Resolution.Minute);
|
||||
}
|
||||
|
||||
Schedule.On(DateRules.EveryDay(), TimeRules.Every(TimeSpan.FromHours(1)), MakeHistoryCall);
|
||||
}
|
||||
|
||||
private void MakeHistoryCall()
|
||||
{
|
||||
foreach (var symbol in _symbols)
|
||||
{
|
||||
_received.Add(symbol);
|
||||
|
||||
bool hasHistory = false;
|
||||
|
||||
foreach (var dataType in SubscriptionManager.AvailableDataTypes[symbol.SecurityType])
|
||||
{
|
||||
if (dataType == TickType.Quote)
|
||||
{
|
||||
hasHistory |= History(1, Resolution.Daily).Get<QuoteBar>(symbol).Any();
|
||||
}
|
||||
else
|
||||
{
|
||||
hasHistory |= History(1, Resolution.Daily).Get<TradeBar>(symbol).Any();
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasHistory) throw new Exception($"No {symbol} data on the eve of {Time} {Time.DayOfWeek}");
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (_received.Count != _symbols.Length)
|
||||
{
|
||||
throw new Exception($"Data for symbols {string.Join(",", _symbols.Except(_received))} were not 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 Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "0"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe 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.096"},
|
||||
{"Tracking Error", "0.212"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "0"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "371857150"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// Regression test algorithm simply fetch history on boarder of Daylight Saving Time shift
|
||||
/// </summary>
|
||||
public class DaylightSavingTimeHistoryRegressionTest : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
public class DaylightSavingTimeHistoryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol[] _symbols = new[]
|
||||
{
|
||||
@@ -187,12 +187,12 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Trades", "6441"},
|
||||
{"Average Win", "0.07%"},
|
||||
{"Average Loss", "-0.07%"},
|
||||
{"Compounding Annual Return", "13.284%"},
|
||||
{"Compounding Annual Return", "13.331%"},
|
||||
{"Drawdown", "10.700%"},
|
||||
{"Expectancy", "0.061"},
|
||||
{"Net Profit", "13.284%"},
|
||||
{"Sharpe Ratio", "0.96"},
|
||||
{"Probabilistic Sharpe Ratio", "46.111%"},
|
||||
{"Net Profit", "13.331%"},
|
||||
{"Sharpe Ratio", "0.963"},
|
||||
{"Probabilistic Sharpe Ratio", "46.232%"},
|
||||
{"Loss Rate", "46%"},
|
||||
{"Win Rate", "54%"},
|
||||
{"Profit-Loss Ratio", "0.97"},
|
||||
@@ -200,15 +200,15 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Beta", "-0.066"},
|
||||
{"Annual Standard Deviation", "0.121"},
|
||||
{"Annual Variance", "0.015"},
|
||||
{"Information Ratio", "0.004"},
|
||||
{"Information Ratio", "0.006"},
|
||||
{"Tracking Error", "0.171"},
|
||||
{"Treynor Ratio", "-1.754"},
|
||||
{"Total Fees", "$8669.33"},
|
||||
{"Treynor Ratio", "-1.761"},
|
||||
{"Total Fees", "$8669.41"},
|
||||
{"Fitness Score", "0.675"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "1.124"},
|
||||
{"Return Over Maximum Drawdown", "1.242"},
|
||||
{"Sortino Ratio", "1.127"},
|
||||
{"Return Over Maximum Drawdown", "1.246"},
|
||||
{"Portfolio Turnover", "1.64"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
@@ -223,7 +223,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1120327913"}
|
||||
{"OrderListHash", "-75671425"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,12 +160,12 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Trades", "5059"},
|
||||
{"Average Win", "0.08%"},
|
||||
{"Average Loss", "-0.08%"},
|
||||
{"Compounding Annual Return", "14.901%"},
|
||||
{"Compounding Annual Return", "14.950%"},
|
||||
{"Drawdown", "10.600%"},
|
||||
{"Expectancy", "0.075"},
|
||||
{"Net Profit", "14.901%"},
|
||||
{"Sharpe Ratio", "1.068"},
|
||||
{"Probabilistic Sharpe Ratio", "50.201%"},
|
||||
{"Net Profit", "14.950%"},
|
||||
{"Sharpe Ratio", "1.072"},
|
||||
{"Probabilistic Sharpe Ratio", "50.327%"},
|
||||
{"Loss Rate", "45%"},
|
||||
{"Win Rate", "55%"},
|
||||
{"Profit-Loss Ratio", "0.97"},
|
||||
@@ -173,15 +173,15 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Beta", "-0.066"},
|
||||
{"Annual Standard Deviation", "0.121"},
|
||||
{"Annual Variance", "0.015"},
|
||||
{"Information Ratio", "0.08"},
|
||||
{"Information Ratio", "0.083"},
|
||||
{"Tracking Error", "0.171"},
|
||||
{"Treynor Ratio", "-1.963"},
|
||||
{"Total Fees", "$6806.57"},
|
||||
{"Treynor Ratio", "-1.971"},
|
||||
{"Total Fees", "$6806.67"},
|
||||
{"Fitness Score", "0.694"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "1.261"},
|
||||
{"Return Over Maximum Drawdown", "1.404"},
|
||||
{"Sortino Ratio", "1.265"},
|
||||
{"Return Over Maximum Drawdown", "1.409"},
|
||||
{"Portfolio Turnover", "1.296"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
@@ -196,7 +196,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "974523768"}
|
||||
{"OrderListHash", "1142077166"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,13 +90,13 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$14.91"},
|
||||
{"Total Fees", "$14.92"},
|
||||
{"Fitness Score", "0.258"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-27.251"},
|
||||
{"Portfolio Turnover", "0.515"},
|
||||
{"Return Over Maximum Drawdown", "-27.228"},
|
||||
{"Portfolio Turnover", "0.516"},
|
||||
{"Total Insights Generated", "1"},
|
||||
{"Total Insights Closed", "1"},
|
||||
{"Total Insights Analysis Completed", "1"},
|
||||
@@ -110,7 +110,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "221046152"}
|
||||
{"OrderListHash", "1296183675"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks that the Tick BidPrice and AskPrices are adjusted like Value.
|
||||
/// </summary>
|
||||
public class EquityTickQuoteAdjustedModeRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _ibm;
|
||||
private bool _bought;
|
||||
private bool _sold;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 7);
|
||||
SetEndDate(2013, 10, 11);
|
||||
SetCash(100000);
|
||||
|
||||
_ibm = AddEquity("IBM", Resolution.Tick).Symbol;
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (!data.Ticks.ContainsKey(_ibm))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var security = Securities[_ibm];
|
||||
if (!security.HasData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var tick in data.Ticks[_ibm])
|
||||
{
|
||||
if (tick.BidPrice != 0 && !_bought && ((tick.Value - tick.BidPrice) <= 0.05m))
|
||||
{
|
||||
SetHoldings(_ibm, 1);
|
||||
_bought = true;
|
||||
return;
|
||||
}
|
||||
if (tick.AskPrice != 0 && _bought && !_sold && Math.Abs((double)tick.Value - (double)tick.AskPrice) <= 0.05)
|
||||
{
|
||||
Liquidate(_ibm);
|
||||
_sold = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-0.01%"},
|
||||
{"Compounding Annual Return", "-0.500%"},
|
||||
{"Drawdown", "0.000%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-0.006%"},
|
||||
{"Sharpe Ratio", "0"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0"},
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-8.769"},
|
||||
{"Tracking Error", "0.22"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$6.41"},
|
||||
{"Fitness Score", "0.248"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-82.815"},
|
||||
{"Portfolio Turnover", "0.497"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1213851303"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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 QuantConnect.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm testing doing some history requests outside market hours, reproducing GH issue #4783
|
||||
/// </summary>
|
||||
public class ExtendedMarketHoursHistoryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private int _minuteHistoryCount;
|
||||
private int _hourHistoryCount;
|
||||
private int _dailyHistoryCount;
|
||||
|
||||
/// <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, 09);
|
||||
SetCash(100000);
|
||||
|
||||
AddEquity("SPY", Resolution.Minute, extendedMarketHours:true, fillDataForward:false);
|
||||
|
||||
Schedule.On("RunHistoryCall", DateRules.EveryDay(), TimeRules.Every(TimeSpan.FromHours(1)), RunHistoryCall);
|
||||
}
|
||||
|
||||
private void RunHistoryCall()
|
||||
{
|
||||
var spy = Securities["SPY"];
|
||||
var regularHours = spy.Exchange.Hours.IsOpen(Time, false);
|
||||
var extendedHours = !regularHours && spy.Exchange.Hours.IsOpen(Time, true);
|
||||
|
||||
if (regularHours)
|
||||
{
|
||||
_minuteHistoryCount++;
|
||||
var history = History(spy.Symbol, 5, Resolution.Minute).Count();
|
||||
if (history != 5)
|
||||
{
|
||||
throw new Exception($"Unexpected Minute data count: {history}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (extendedHours)
|
||||
{
|
||||
_hourHistoryCount++;
|
||||
var history = History(spy.Symbol, 5, Resolution.Hour).Count();
|
||||
if (history != 5)
|
||||
{
|
||||
throw new Exception($"Unexpected Hour data count {history}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_dailyHistoryCount++;
|
||||
var history = History(spy.Symbol, 5, Resolution.Daily).Count();
|
||||
if (history != 5)
|
||||
{
|
||||
throw new Exception($"Unexpected Daily data count {history}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 data)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
SetHoldings("SPY", 1);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (_minuteHistoryCount != 3 * 6)
|
||||
{
|
||||
throw new Exception($"Unexpected minute history requests count {_minuteHistoryCount}");
|
||||
}
|
||||
// 6 pre market from 4am to 9am + 4 post market 4pm to 7pm
|
||||
if (_hourHistoryCount != 3 * 10)
|
||||
{
|
||||
throw new Exception($"Unexpected hour history requests count {_hourHistoryCount}");
|
||||
}
|
||||
// 0am to 3am + 8pm to 11pm, last day ends at 8pm
|
||||
if (_dailyHistoryCount != (2 * 8 + 5))
|
||||
{
|
||||
throw new Exception($"Unexpected Daily history requests count: {_dailyHistoryCount}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "20"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0.00%"},
|
||||
{"Compounding Annual Return", "-74.182%"},
|
||||
{"Drawdown", "2.200%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-1.046%"},
|
||||
{"Sharpe Ratio", "-8.269"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "-0.19"},
|
||||
{"Beta", "0.579"},
|
||||
{"Annual Standard Deviation", "0.065"},
|
||||
{"Annual Variance", "0.004"},
|
||||
{"Information Ratio", "1.326"},
|
||||
{"Tracking Error", "0.049"},
|
||||
{"Treynor Ratio", "-0.934"},
|
||||
{"Total Fees", "$22.26"},
|
||||
{"Fitness Score", "0.002"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-11.855"},
|
||||
{"Return Over Maximum Drawdown", "-70.945"},
|
||||
{"Portfolio Turnover", "0.342"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1961710414"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "699698796"}
|
||||
{"OrderListHash", "1717552327"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-15.574"},
|
||||
{"Portfolio Turnover", "2.056"},
|
||||
{"Portfolio Turnover", "2.057"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
@@ -193,7 +193,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1311542155"}
|
||||
{"OrderListHash", "-1116140375"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
private readonly Dictionary<Symbol, int> _dataPointsPerSymbol = new Dictionary<Symbol, int>();
|
||||
private bool _added;
|
||||
private Symbol _eurusd;
|
||||
private DateTime lastDataTime = DateTime.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
|
||||
@@ -51,6 +52,13 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (lastDataTime == data.Time)
|
||||
{
|
||||
throw new Exception("Duplicate time for current data and last data slice");
|
||||
}
|
||||
|
||||
lastDataTime = data.Time;
|
||||
|
||||
if (_added)
|
||||
{
|
||||
var eurUsdSubscription = SubscriptionManager.SubscriptionDataConfigService
|
||||
@@ -94,7 +102,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
var expectedDataPointsPerSymbol = new Dictionary<string, int>
|
||||
{
|
||||
{ "EURGBP", 3 },
|
||||
{ "EURUSD", 29 }
|
||||
{ "EURUSD", 28 }
|
||||
};
|
||||
|
||||
foreach (var kvp in _dataPointsPerSymbol)
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
private readonly Dictionary<Symbol, int> _dataPointsPerSymbol = new Dictionary<Symbol, int>();
|
||||
private bool _added;
|
||||
private Symbol _eurusd;
|
||||
private DateTime lastDataTime = DateTime.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
|
||||
@@ -51,6 +52,13 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (lastDataTime == data.Time)
|
||||
{
|
||||
throw new Exception("Duplicate time for current data and last data slice");
|
||||
}
|
||||
|
||||
lastDataTime = data.Time;
|
||||
|
||||
if (_added)
|
||||
{
|
||||
var eurUsdSubscription = SubscriptionManager.SubscriptionDataConfigService
|
||||
@@ -96,7 +104,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
// normal feed
|
||||
{ "EURGBP", 3 },
|
||||
// internal feed on the first day, normal feed on the other two days
|
||||
{ "EURUSD", 3 },
|
||||
{ "EURUSD", 2 },
|
||||
// internal feed only
|
||||
{ "GBPUSD", 0 }
|
||||
};
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Trades", "6"},
|
||||
{"Average Win", "6.02%"},
|
||||
{"Average Loss", "-2.40%"},
|
||||
{"Compounding Annual Return", "915.481%"},
|
||||
{"Compounding Annual Return", "915.480%"},
|
||||
{"Drawdown", "5.500%"},
|
||||
{"Expectancy", "1.338"},
|
||||
{"Net Profit", "11.400%"},
|
||||
@@ -117,7 +117,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Information Ratio", "9.507"},
|
||||
{"Tracking Error", "0.507"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$2651.00"},
|
||||
{"Total Fees", "$2651.01"},
|
||||
{"Fitness Score", "0.467"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
@@ -137,7 +137,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1241317053"}
|
||||
{"OrderListHash", "-89452746"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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 System.Reflection;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests In The Money (ITM) future option calls across different strike prices.
|
||||
/// We expect 6 orders from the algorithm, which are:
|
||||
///
|
||||
/// * (1) Initial entry, buy ES Call Option (ES19M20 expiring ITM)
|
||||
/// * (2) Initial entry, sell ES Call Option at different strike (ES20H20 expiring ITM)
|
||||
/// * [2] Option assignment, opens a position in the underlying (ES20H20, Qty: -1)
|
||||
/// * [2] Future contract liquidation, due to impending expiry
|
||||
/// * [1] Option exercise, receive 1 ES19M20 future contract
|
||||
/// * [1] Liquidate ES19M20 contract, due to expiry
|
||||
///
|
||||
/// Additionally, we test delistings for future options and assert that our
|
||||
/// portfolio holdings reflect the orders the algorithm has submitted.
|
||||
/// </summary>
|
||||
public class FutureOptionBuySellCallIntradayRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 6, 30);
|
||||
|
||||
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
// which causes delisting events to never be processed, thus leading to options that might never
|
||||
// be exercised until the next data point arrives.
|
||||
AddEquity("AAPL", Resolution.Daily);
|
||||
|
||||
var es20h20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
new DateTime(2020, 3, 20)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
var es20m20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
new DateTime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
var esOptions = OptionChainProvider.GetOptionContractList(es20m20, Time)
|
||||
.Concat(OptionChainProvider.GetOptionContractList(es20h20, Time))
|
||||
.Where(x => x.ID.StrikePrice == 3200m && x.ID.OptionRight == OptionRight.Call)
|
||||
.Select(x => AddFutureOptionContract(x, Resolution.Minute).Symbol)
|
||||
.ToList();
|
||||
|
||||
var expectedContracts = new[]
|
||||
{
|
||||
QuantConnect.Symbol.CreateOption(es20h20, Market.CME, OptionStyle.American, OptionRight.Call, 3200m,
|
||||
new DateTime(2020, 3, 20)),
|
||||
QuantConnect.Symbol.CreateOption(es20m20, Market.CME, OptionStyle.American, OptionRight.Call, 3200m,
|
||||
new DateTime(2020, 6, 19))
|
||||
};
|
||||
|
||||
foreach (var esOption in esOptions)
|
||||
{
|
||||
if (!expectedContracts.Contains(esOption))
|
||||
{
|
||||
throw new Exception($"Contract {esOption} was not found in the chain");
|
||||
}
|
||||
}
|
||||
|
||||
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(es20m20, 1), () =>
|
||||
{
|
||||
MarketOrder(esOptions[0], 1);
|
||||
MarketOrder(esOptions[1], -1);
|
||||
});
|
||||
Schedule.On(DateRules.Tomorrow, TimeRules.Noon, () =>
|
||||
{
|
||||
Liquidate();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">The algorithm has holdings</exception>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "6"},
|
||||
{"Average Win", "2.94%"},
|
||||
{"Average Loss", "-4.15%"},
|
||||
{"Compounding Annual Return", "-5.601%"},
|
||||
{"Drawdown", "5.600%"},
|
||||
{"Expectancy", "-0.146"},
|
||||
{"Net Profit", "-2.771%"},
|
||||
{"Sharpe Ratio", "-0.49"},
|
||||
{"Probabilistic Sharpe Ratio", "10.583%"},
|
||||
{"Loss Rate", "50%"},
|
||||
{"Win Rate", "50%"},
|
||||
{"Profit-Loss Ratio", "0.71"},
|
||||
{"Alpha", "-0.043"},
|
||||
{"Beta", "-0.001"},
|
||||
{"Annual Standard Deviation", "0.087"},
|
||||
{"Annual Variance", "0.008"},
|
||||
{"Information Ratio", "0.96"},
|
||||
{"Tracking Error", "0.192"},
|
||||
{"Treynor Ratio", "58.394"},
|
||||
{"Total Fees", "$14.80"},
|
||||
{"Fitness Score", "0.018"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-0.096"},
|
||||
{"Return Over Maximum Drawdown", "-0.993"},
|
||||
{"Portfolio Turnover", "0.043"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-290004562"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
259
Algorithm.CSharp/FutureOptionCallITMExpiryRegressionAlgorithm.cs
Normal file
259
Algorithm.CSharp/FutureOptionCallITMExpiryRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* 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 System.Reflection;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests In The Money (ITM) future option expiry for calls.
|
||||
/// We expect 3 orders from the algorithm, which are:
|
||||
///
|
||||
/// * Initial entry, buy ES Call Option (expiring ITM)
|
||||
/// * Option exercise, receiving ES future contracts
|
||||
/// * Future contract liquidation, due to impending expiry
|
||||
///
|
||||
/// Additionally, we test delistings for future options and assert that our
|
||||
/// portfolio holdings reflect the orders the algorithm has submitted.
|
||||
/// </summary>
|
||||
public class FutureOptionCallITMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _es19m20;
|
||||
private Symbol _esOption;
|
||||
private Symbol _expectedOptionContract;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 6, 30);
|
||||
|
||||
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
// which causes delisting events to never be processed, thus leading to options that might never
|
||||
// be exercised until the next data point arrives.
|
||||
AddEquity("AAPL", Resolution.Daily);
|
||||
|
||||
_es19m20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
new DateTime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute).Symbol;
|
||||
|
||||
_expectedOptionContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3200m, new DateTime(2020, 6, 19));
|
||||
if (_esOption != _expectedOptionContract)
|
||||
{
|
||||
throw new Exception($"Contract {_expectedOptionContract} was not found in the chain");
|
||||
}
|
||||
|
||||
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
|
||||
{
|
||||
MarketOrder(_esOption, 1);
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
// Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
// the expected time. These assertions detect bug #4872
|
||||
foreach (var delisting in data.Delistings.Values)
|
||||
{
|
||||
if (delisting.Type == DelistingType.Warning)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 19))
|
||||
{
|
||||
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
if (delisting.Type == DelistingType.Delisted)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 20))
|
||||
{
|
||||
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
if (orderEvent.Status != OrderStatus.Filled)
|
||||
{
|
||||
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Securities.ContainsKey(orderEvent.Symbol))
|
||||
{
|
||||
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
var security = Securities[orderEvent.Symbol];
|
||||
if (security.Symbol == _es19m20)
|
||||
{
|
||||
AssertFutureOptionOrderExercise(orderEvent, security, Securities[_expectedOptionContract]);
|
||||
}
|
||||
else if (security.Symbol == _expectedOptionContract)
|
||||
{
|
||||
AssertFutureOptionContractOrder(orderEvent, security);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
Log($"{Time:yyyy-MM-dd HH:mm:ss} -- {orderEvent.Symbol} :: Price: {Securities[orderEvent.Symbol].Holdings.Price} Qty: {Securities[orderEvent.Symbol].Holdings.Quantity} Direction: {orderEvent.Direction} Msg: {orderEvent.Message}");
|
||||
}
|
||||
|
||||
private void AssertFutureOptionOrderExercise(OrderEvent orderEvent, Security future, Security optionContract)
|
||||
{
|
||||
// We expect the liquidation to occur on the day of the delisting (while the market is open),
|
||||
// but currently we liquidate at the next market open (AAPL open) which happens to be
|
||||
// at 9:30:00 Eastern Time. For unknown reasons, the delisting happens two minutes after the
|
||||
// market open.
|
||||
// Read more about the issue affecting this test here: https://github.com/QuantConnect/Lean/issues/4980
|
||||
var expectedLiquidationTimeUtc = new DateTime(2020, 6, 22, 13, 32, 0);
|
||||
|
||||
if (orderEvent.Direction == OrderDirection.Sell && future.Holdings.Quantity != 0)
|
||||
{
|
||||
// We expect the contract to have been liquidated immediately
|
||||
throw new Exception($"Did not liquidate existing holdings for Symbol {future.Symbol}");
|
||||
}
|
||||
if (orderEvent.Direction == OrderDirection.Sell && orderEvent.UtcTime != expectedLiquidationTimeUtc)
|
||||
{
|
||||
throw new Exception($"Liquidated future contract, but not at the expected time. Expected: {expectedLiquidationTimeUtc:yyyy-MM-dd HH:mm:ss} - found {orderEvent.UtcTime:yyyy-MM-dd HH:mm:ss}");
|
||||
}
|
||||
|
||||
// No way to detect option exercise orders or any other kind of special orders
|
||||
// other than matching strings, for now.
|
||||
if (orderEvent.Message.Contains("Option Exercise"))
|
||||
{
|
||||
if (orderEvent.FillPrice != 3200m)
|
||||
{
|
||||
throw new Exception("Option did not exercise at expected strike price (3200)");
|
||||
}
|
||||
if (future.Holdings.Quantity != 1)
|
||||
{
|
||||
// Here, we expect to have some holdings in the underlying, but not in the future option anymore.
|
||||
throw new Exception($"Exercised option contract, but we have no holdings for Future {future.Symbol}");
|
||||
}
|
||||
|
||||
if (optionContract.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception($"Exercised option contract, but we have holdings for Option contract {optionContract.Symbol}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
|
||||
{
|
||||
if (orderEvent.Direction == OrderDirection.Buy && option.Holdings.Quantity != 1)
|
||||
{
|
||||
throw new Exception($"No holdings were created for option contract {option.Symbol}");
|
||||
}
|
||||
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception($"Holdings were found after a filled option exercise");
|
||||
}
|
||||
if (orderEvent.Message.Contains("Exercise") && option.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception($"Holdings were found after exercising option contract {option.Symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">The algorithm has holdings</exception>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "3"},
|
||||
{"Average Win", "1.25%"},
|
||||
{"Average Loss", "-7.42%"},
|
||||
{"Compounding Annual Return", "-12.413%"},
|
||||
{"Drawdown", "6.300%"},
|
||||
{"Expectancy", "-0.416"},
|
||||
{"Net Profit", "-6.257%"},
|
||||
{"Sharpe Ratio", "-1.325"},
|
||||
{"Probabilistic Sharpe Ratio", "0.004%"},
|
||||
{"Loss Rate", "50%"},
|
||||
{"Win Rate", "50%"},
|
||||
{"Profit-Loss Ratio", "0.17"},
|
||||
{"Alpha", "-0.102"},
|
||||
{"Beta", "-0.003"},
|
||||
{"Annual Standard Deviation", "0.076"},
|
||||
{"Annual Variance", "0.006"},
|
||||
{"Information Ratio", "0.673"},
|
||||
{"Tracking Error", "0.188"},
|
||||
{"Treynor Ratio", "33.559"},
|
||||
{"Total Fees", "$7.40"},
|
||||
{"Fitness Score", "0.008"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-0.205"},
|
||||
{"Return Over Maximum Drawdown", "-1.983"},
|
||||
{"Portfolio Turnover", "0.023"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "23301049"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Option;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests In The Money (ITM) future option expiry for calls.
|
||||
/// We test to make sure that FOPs have greeks enabled, same as equity options.
|
||||
/// </summary>
|
||||
public class FutureOptionCallITMGreeksExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private bool _invested;
|
||||
private int _onDataCalls;
|
||||
private Symbol _es19m20;
|
||||
private Option _esOption;
|
||||
private Symbol _expectedOptionContract;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 6, 30);
|
||||
|
||||
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
// which causes delisting events to never be processed, thus leading to options that might never
|
||||
// be exercised until the next data point arrives.
|
||||
AddEquity("AAPL", Resolution.Daily);
|
||||
|
||||
_es19m20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
new DateTime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, new DateTime(2020, 1, 5))
|
||||
.Where(x => x.ID.StrikePrice <= 3200m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute);
|
||||
|
||||
_esOption.PriceModel = OptionPriceModels.BjerksundStensland();
|
||||
|
||||
_expectedOptionContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3200m, new DateTime(2020, 6, 19));
|
||||
if (_esOption.Symbol != _expectedOptionContract)
|
||||
{
|
||||
throw new Exception($"Contract {_expectedOptionContract} was not found in the chain");
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
// Let the algo warmup, but without using SetWarmup. Otherwise, we get
|
||||
// no contracts in the option chain
|
||||
if (_invested || _onDataCalls++ < 40)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.OptionChains.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (data.OptionChains.Values.All(o => o.Contracts.Values.Any(c => !data.ContainsKey(c.Symbol))))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (data.OptionChains.Values.First().Contracts.Count == 0)
|
||||
{
|
||||
throw new Exception($"No contracts found in the option {data.OptionChains.Keys.First()}");
|
||||
}
|
||||
|
||||
var deltas = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Delta).ToList();
|
||||
var gammas = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Gamma).ToList();
|
||||
var lambda = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Lambda).ToList();
|
||||
var rho = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Rho).ToList();
|
||||
var theta = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Theta).ToList();
|
||||
var vega = data.OptionChains.Values.OrderByDescending(y => y.Contracts.Values.Sum(x => x.Volume)).First().Contracts.Values.Select(x => x.Greeks.Vega).ToList();
|
||||
|
||||
// The commented out test cases all return zero.
|
||||
// This is because of failure to evaluate the greeks in the option pricing model.
|
||||
// For now, let's skip those.
|
||||
if (deltas.Any(d => d == 0))
|
||||
{
|
||||
throw new AggregateException("Option contract Delta was equal to zero");
|
||||
}
|
||||
if (gammas.Any(g => g == 0))
|
||||
{
|
||||
throw new AggregateException("Option contract Gamma was equal to zero");
|
||||
}
|
||||
//if (lambda.Any(l => l == 0))
|
||||
//{
|
||||
// throw new AggregateException("Option contract Lambda was equal to zero");
|
||||
//}
|
||||
if (rho.Any(r => r == 0))
|
||||
{
|
||||
throw new AggregateException("Option contract Rho was equal to zero");
|
||||
}
|
||||
//if (theta.Any(t => t == 0))
|
||||
//{
|
||||
// throw new AggregateException("Option contract Theta was equal to zero");
|
||||
//}
|
||||
//if (vega.Any(v => v == 0))
|
||||
//{
|
||||
// throw new AggregateException("Option contract Vega was equal to zero");
|
||||
//}
|
||||
|
||||
if (!_invested)
|
||||
{
|
||||
SetHoldings(data.OptionChains.Values.First().Contracts.Values.First().Symbol, 1);
|
||||
_invested = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">The algorithm has holdings</exception>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
|
||||
}
|
||||
if (!_invested)
|
||||
{
|
||||
throw new Exception($"Never checked greeks, maybe we have no option 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 Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "3"},
|
||||
{"Average Win", "28.04%"},
|
||||
{"Average Loss", "-62.81%"},
|
||||
{"Compounding Annual Return", "-78.165%"},
|
||||
{"Drawdown", "52.400%"},
|
||||
{"Expectancy", "-0.277"},
|
||||
{"Net Profit", "-52.379%"},
|
||||
{"Sharpe Ratio", "-0.865"},
|
||||
{"Probabilistic Sharpe Ratio", "0.019%"},
|
||||
{"Loss Rate", "50%"},
|
||||
{"Win Rate", "50%"},
|
||||
{"Profit-Loss Ratio", "0.45"},
|
||||
{"Alpha", "-0.596"},
|
||||
{"Beta", "-0.031"},
|
||||
{"Annual Standard Deviation", "0.681"},
|
||||
{"Annual Variance", "0.463"},
|
||||
{"Information Ratio", "-0.514"},
|
||||
{"Tracking Error", "0.703"},
|
||||
{"Treynor Ratio", "18.748"},
|
||||
{"Total Fees", "$66.60"},
|
||||
{"Fitness Score", "0.157"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-0.133"},
|
||||
{"Return Over Maximum Drawdown", "-1.492"},
|
||||
{"Portfolio Turnover", "0.411"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "151392833"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
226
Algorithm.CSharp/FutureOptionCallOTMExpiryRegressionAlgorithm.cs
Normal file
226
Algorithm.CSharp/FutureOptionCallOTMExpiryRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* 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 System.Reflection;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests Out of The Money (OTM) future option expiry for calls.
|
||||
/// We expect 1 order from the algorithm, which are:
|
||||
///
|
||||
/// * Initial entry, buy ES Call Option (expiring OTM)
|
||||
/// - contract expires worthless, not exercised, so never opened a position in the underlying
|
||||
///
|
||||
/// Additionally, we test delistings for future options and assert that our
|
||||
/// portfolio holdings reflect the orders the algorithm has submitted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Total Trades in regression algorithm should be 1, but expiration is counted as a trade.
|
||||
/// See related issue: https://github.com/QuantConnect/Lean/issues/4854
|
||||
/// </remarks>
|
||||
public class FutureOptionCallOTMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _es19m20;
|
||||
private Symbol _esOption;
|
||||
private Symbol _expectedContract;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 6, 30);
|
||||
|
||||
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
// which causes delisting events to never be processed, thus leading to options that might never
|
||||
// be exercised until the next data point arrives.
|
||||
AddEquity("AAPL", Resolution.Daily);
|
||||
|
||||
_es19m20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
new DateTime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option call expiring OTM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
.Where(x => x.ID.StrikePrice >= 3300m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute).Symbol;
|
||||
|
||||
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3300m, new DateTime(2020, 6, 19));
|
||||
if (_esOption != _expectedContract)
|
||||
{
|
||||
throw new Exception($"Contract {_expectedContract} was not found in the chain");
|
||||
}
|
||||
|
||||
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
|
||||
{
|
||||
MarketOrder(_esOption, 1);
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
// Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
// the expected time. These assertions detect bug #4872
|
||||
foreach (var delisting in data.Delistings.Values)
|
||||
{
|
||||
if (delisting.Type == DelistingType.Warning)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 19))
|
||||
{
|
||||
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
if (delisting.Type == DelistingType.Delisted)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 20))
|
||||
{
|
||||
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
if (orderEvent.Status != OrderStatus.Filled)
|
||||
{
|
||||
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Securities.ContainsKey(orderEvent.Symbol))
|
||||
{
|
||||
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
var security = Securities[orderEvent.Symbol];
|
||||
if (security.Symbol == _es19m20)
|
||||
{
|
||||
throw new Exception("Invalid state: did not expect a position for the underlying to be opened, since this contract expires OTM");
|
||||
}
|
||||
if (security.Symbol == _expectedContract)
|
||||
{
|
||||
AssertFutureOptionContractOrder(orderEvent, security);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
Log($"{orderEvent}");
|
||||
}
|
||||
|
||||
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
|
||||
{
|
||||
if (orderEvent.Direction == OrderDirection.Buy && option.Holdings.Quantity != 1)
|
||||
{
|
||||
throw new Exception($"No holdings were created for option contract {option.Symbol}");
|
||||
}
|
||||
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception("Holdings were found after a filled option exercise");
|
||||
}
|
||||
if (orderEvent.Direction == OrderDirection.Sell && !orderEvent.Message.Contains("OTM"))
|
||||
{
|
||||
throw new Exception("Contract did not expire OTM");
|
||||
}
|
||||
if (orderEvent.Message.Contains("Exercise"))
|
||||
{
|
||||
throw new Exception("Exercised option, even though it expires OTM");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">The algorithm has holdings</exception>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-4.03%"},
|
||||
{"Compounding Annual Return", "-8.088%"},
|
||||
{"Drawdown", "4.000%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-4.029%"},
|
||||
{"Sharpe Ratio", "-1.274"},
|
||||
{"Probabilistic Sharpe Ratio", "0.015%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "-0.066"},
|
||||
{"Beta", "-0.002"},
|
||||
{"Annual Standard Deviation", "0.052"},
|
||||
{"Annual Variance", "0.003"},
|
||||
{"Information Ratio", "0.9"},
|
||||
{"Tracking Error", "0.179"},
|
||||
{"Treynor Ratio", "28.537"},
|
||||
{"Total Fees", "$3.70"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-0.183"},
|
||||
{"Return Over Maximum Drawdown", "-2.007"},
|
||||
{"Portfolio Turnover", "0"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1116221764"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
259
Algorithm.CSharp/FutureOptionPutITMExpiryRegressionAlgorithm.cs
Normal file
259
Algorithm.CSharp/FutureOptionPutITMExpiryRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,259 @@
|
||||
/*
|
||||
* 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 System.Reflection;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests In The Money (ITM) future option expiry for puts.
|
||||
/// We expect 3 orders from the algorithm, which are:
|
||||
///
|
||||
/// * Initial entry, buy ES Put Option (expiring ITM) (buy, qty 1)
|
||||
/// * Option exercise, receiving short ES future contracts (sell, qty -1)
|
||||
/// * Future contract liquidation, due to impending expiry (buy qty 1)
|
||||
///
|
||||
/// Additionally, we test delistings for future options and assert that our
|
||||
/// portfolio holdings reflect the orders the algorithm has submitted.
|
||||
/// </summary>
|
||||
public class FutureOptionPutITMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _es19m20;
|
||||
private Symbol _esOption;
|
||||
private Symbol _expectedContract;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 6, 30);
|
||||
|
||||
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
// which causes delisting events to never be processed, thus leading to options that might never
|
||||
// be exercised until the next data point arrives.
|
||||
AddEquity("AAPL", Resolution.Daily);
|
||||
|
||||
_es19m20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
new DateTime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
.Where(x => x.ID.StrikePrice >= 3300m && x.ID.OptionRight == OptionRight.Put)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute).Symbol;
|
||||
|
||||
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3300m, new DateTime(2020, 6, 19));
|
||||
if (_esOption != _expectedContract)
|
||||
{
|
||||
throw new Exception($"Contract {_expectedContract} was not found in the chain");
|
||||
}
|
||||
|
||||
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
|
||||
{
|
||||
MarketOrder(_esOption, 1);
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
// Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
// the expected time. These assertions detect bug #4872
|
||||
foreach (var delisting in data.Delistings.Values)
|
||||
{
|
||||
if (delisting.Type == DelistingType.Warning)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 19))
|
||||
{
|
||||
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
if (delisting.Type == DelistingType.Delisted)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 20))
|
||||
{
|
||||
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
if (orderEvent.Status != OrderStatus.Filled)
|
||||
{
|
||||
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Securities.ContainsKey(orderEvent.Symbol))
|
||||
{
|
||||
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
var security = Securities[orderEvent.Symbol];
|
||||
if (security.Symbol == _es19m20)
|
||||
{
|
||||
AssertFutureOptionOrderExercise(orderEvent, security, Securities[_expectedContract]);
|
||||
}
|
||||
else if (security.Symbol == _expectedContract)
|
||||
{
|
||||
AssertFutureOptionContractOrder(orderEvent, security);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
Log($"{Time:yyyy-MM-dd HH:mm:ss} -- {orderEvent.Symbol} :: Price: {Securities[orderEvent.Symbol].Holdings.Price} Qty: {Securities[orderEvent.Symbol].Holdings.Quantity} Direction: {orderEvent.Direction} Msg: {orderEvent.Message}");
|
||||
}
|
||||
|
||||
private void AssertFutureOptionOrderExercise(OrderEvent orderEvent, Security future, Security optionContract)
|
||||
{
|
||||
// We expect the liquidation to occur on the day of the delisting (while the market is open),
|
||||
// but currently we liquidate at the next market open (AAPL open) which happens to be
|
||||
// at 9:30:00 Eastern Time. For unknown reasons, the delisting happens two minutes after the
|
||||
// market open.
|
||||
// Read more about the issue affecting this test here: https://github.com/QuantConnect/Lean/issues/4980
|
||||
var expectedLiquidationTimeUtc = new DateTime(2020, 6, 22, 13, 32, 0);
|
||||
|
||||
if (orderEvent.Direction == OrderDirection.Buy && future.Holdings.Quantity != 0)
|
||||
{
|
||||
// We expect the contract to have been liquidated immediately
|
||||
throw new Exception($"Did not liquidate existing holdings for Symbol {future.Symbol}");
|
||||
}
|
||||
if (orderEvent.Direction == OrderDirection.Buy && orderEvent.UtcTime != expectedLiquidationTimeUtc)
|
||||
{
|
||||
throw new Exception($"Liquidated future contract, but not at the expected time. Expected: {expectedLiquidationTimeUtc:yyyy-MM-dd HH:mm:ss} - found {orderEvent.UtcTime:yyyy-MM-dd HH:mm:ss}");
|
||||
}
|
||||
|
||||
// No way to detect option exercise orders or any other kind of special orders
|
||||
// other than matching strings, for now.
|
||||
if (orderEvent.Message.Contains("Option Exercise"))
|
||||
{
|
||||
if (orderEvent.FillPrice != 3300m)
|
||||
{
|
||||
throw new Exception("Option did not exercise at expected strike price (3300)");
|
||||
}
|
||||
if (future.Holdings.Quantity != -1)
|
||||
{
|
||||
// Here, we expect to have some holdings in the underlying, but not in the future option anymore.
|
||||
throw new Exception($"Exercised option contract, but we have no holdings for Future {future.Symbol}");
|
||||
}
|
||||
|
||||
if (optionContract.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception($"Exercised option contract, but we have holdings for Option contract {optionContract.Symbol}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
|
||||
{
|
||||
if (orderEvent.Direction == OrderDirection.Buy && option.Holdings.Quantity != 1)
|
||||
{
|
||||
throw new Exception($"No holdings were created for option contract {option.Symbol}");
|
||||
}
|
||||
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception($"Holdings were found after a filled option exercise");
|
||||
}
|
||||
if (orderEvent.Message.Contains("Exercise") && option.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception($"Holdings were found after exercising option contract {option.Symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">The algorithm has holdings</exception>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "3"},
|
||||
{"Average Win", "4.18%"},
|
||||
{"Average Loss", "-8.27%"},
|
||||
{"Compounding Annual Return", "-8.879%"},
|
||||
{"Drawdown", "4.400%"},
|
||||
{"Expectancy", "-0.247"},
|
||||
{"Net Profit", "-4.432%"},
|
||||
{"Sharpe Ratio", "-1.391"},
|
||||
{"Probabilistic Sharpe Ratio", "0.002%"},
|
||||
{"Loss Rate", "50%"},
|
||||
{"Win Rate", "50%"},
|
||||
{"Profit-Loss Ratio", "0.51"},
|
||||
{"Alpha", "-0.073"},
|
||||
{"Beta", "-0.002"},
|
||||
{"Annual Standard Deviation", "0.052"},
|
||||
{"Annual Variance", "0.003"},
|
||||
{"Information Ratio", "0.863"},
|
||||
{"Tracking Error", "0.179"},
|
||||
{"Treynor Ratio", "38.46"},
|
||||
{"Total Fees", "$7.40"},
|
||||
{"Fitness Score", "0.008"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-0.224"},
|
||||
{"Return Over Maximum Drawdown", "-2.003"},
|
||||
{"Portfolio Turnover", "0.023"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-675079082"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
225
Algorithm.CSharp/FutureOptionPutOTMExpiryRegressionAlgorithm.cs
Normal file
225
Algorithm.CSharp/FutureOptionPutOTMExpiryRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* 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 System.Reflection;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests Out of The Money (OTM) future option expiry for puts.
|
||||
/// We expect 1 order from the algorithm, which are:
|
||||
///
|
||||
/// * Initial entry, buy ES Put Option (expiring OTM)
|
||||
/// - contract expires worthless, not exercised, so never opened a position in the underlying
|
||||
///
|
||||
/// Additionally, we test delistings for future options and assert that our
|
||||
/// portfolio holdings reflect the orders the algorithm has submitted.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Total Trades in regression algorithm should be 1, but expiration is counted as a trade.
|
||||
/// </remarks>
|
||||
public class FutureOptionPutOTMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _es19m20;
|
||||
private Symbol _esOption;
|
||||
private Symbol _expectedContract;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 6, 30);
|
||||
|
||||
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
// which causes delisting events to never be processed, thus leading to options that might never
|
||||
// be exercised until the next data point arrives.
|
||||
AddEquity("AAPL", Resolution.Daily);
|
||||
|
||||
_es19m20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
new DateTime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
.Where(x => x.ID.StrikePrice <= 3150m && x.ID.OptionRight == OptionRight.Put)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute).Symbol;
|
||||
|
||||
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3150m, new DateTime(2020, 6, 19));
|
||||
if (_esOption != _expectedContract)
|
||||
{
|
||||
throw new Exception($"Contract {_expectedContract} was not found in the chain");
|
||||
}
|
||||
|
||||
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
|
||||
{
|
||||
MarketOrder(_esOption, 1);
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
// Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
// the expected time. These assertions detect bug #4872
|
||||
foreach (var delisting in data.Delistings.Values)
|
||||
{
|
||||
if (delisting.Type == DelistingType.Warning)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 19))
|
||||
{
|
||||
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
if (delisting.Type == DelistingType.Delisted)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 20))
|
||||
{
|
||||
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
if (orderEvent.Status != OrderStatus.Filled)
|
||||
{
|
||||
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Securities.ContainsKey(orderEvent.Symbol))
|
||||
{
|
||||
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
var security = Securities[orderEvent.Symbol];
|
||||
if (security.Symbol == _es19m20)
|
||||
{
|
||||
throw new Exception("Invalid state: did not expect a position for the underlying to be opened, since this contract expires OTM");
|
||||
}
|
||||
if (security.Symbol == _expectedContract)
|
||||
{
|
||||
AssertFutureOptionContractOrder(orderEvent, security);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
Log($"{orderEvent}");
|
||||
}
|
||||
|
||||
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
|
||||
{
|
||||
if (orderEvent.Direction == OrderDirection.Buy && option.Holdings.Quantity != 1)
|
||||
{
|
||||
throw new Exception($"No holdings were created for option contract {option.Symbol}");
|
||||
}
|
||||
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception("Holdings were found after a filled option exercise");
|
||||
}
|
||||
if (orderEvent.Direction == OrderDirection.Sell && !orderEvent.Message.Contains("OTM"))
|
||||
{
|
||||
throw new Exception("Contract did not expire OTM");
|
||||
}
|
||||
if (orderEvent.Message.Contains("Exercise"))
|
||||
{
|
||||
throw new Exception("Exercised option, even though it expires OTM");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">The algorithm has holdings</exception>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-5.12%"},
|
||||
{"Compounding Annual Return", "-10.212%"},
|
||||
{"Drawdown", "5.100%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-5.116%"},
|
||||
{"Sharpe Ratio", "-1.26"},
|
||||
{"Probabilistic Sharpe Ratio", "0.016%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "-0.084"},
|
||||
{"Beta", "-0.003"},
|
||||
{"Annual Standard Deviation", "0.066"},
|
||||
{"Annual Variance", "0.004"},
|
||||
{"Information Ratio", "0.785"},
|
||||
{"Tracking Error", "0.184"},
|
||||
{"Treynor Ratio", "28.158"},
|
||||
{"Total Fees", "$3.70"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-0.181"},
|
||||
{"Return Over Maximum Drawdown", "-1.995"},
|
||||
{"Portfolio Turnover", "0"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "515984318"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
* 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 System.Reflection;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests In The Money (ITM) future option expiry for short calls.
|
||||
/// We expect 3 orders from the algorithm, which are:
|
||||
///
|
||||
/// * Initial entry, sell ES Call Option (expiring ITM)
|
||||
/// * Option assignment, sell 1 contract of the underlying (ES)
|
||||
/// * Future contract expiry, liquidation (buy 1 ES future)
|
||||
///
|
||||
/// Additionally, we test delistings for future options and assert that our
|
||||
/// portfolio holdings reflect the orders the algorithm has submitted.
|
||||
/// </summary>
|
||||
public class FutureOptionShortCallITMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _es19m20;
|
||||
private Symbol _esOption;
|
||||
private Symbol _expectedContract;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 6, 30);
|
||||
|
||||
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
// which causes delisting events to never be processed, thus leading to options that might never
|
||||
// be exercised until the next data point arrives.
|
||||
AddEquity("AAPL", Resolution.Daily);
|
||||
|
||||
_es19m20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
new DateTime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
.Where(x => x.ID.StrikePrice <= 3100m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute).Symbol;
|
||||
|
||||
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3100m, new DateTime(2020, 6, 19));
|
||||
if (_esOption != _expectedContract)
|
||||
{
|
||||
throw new Exception($"Contract {_expectedContract} was not found in the chain");
|
||||
}
|
||||
|
||||
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
|
||||
{
|
||||
MarketOrder(_esOption, -1);
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
// Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
// the expected time. These assertions detect bug #4872
|
||||
foreach (var delisting in data.Delistings.Values)
|
||||
{
|
||||
if (delisting.Type == DelistingType.Warning)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 19))
|
||||
{
|
||||
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
if (delisting.Type == DelistingType.Delisted)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 20))
|
||||
{
|
||||
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
if (orderEvent.Status != OrderStatus.Filled)
|
||||
{
|
||||
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Securities.ContainsKey(orderEvent.Symbol))
|
||||
{
|
||||
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
var security = Securities[orderEvent.Symbol];
|
||||
if (security.Symbol == _es19m20)
|
||||
{
|
||||
AssertFutureOptionOrderExercise(orderEvent, security, Securities[_expectedContract]);
|
||||
}
|
||||
else if (security.Symbol == _expectedContract)
|
||||
{
|
||||
AssertFutureOptionContractOrder(orderEvent, security);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
Log($"{orderEvent}");
|
||||
}
|
||||
|
||||
private void AssertFutureOptionOrderExercise(OrderEvent orderEvent, Security future, Security optionContract)
|
||||
{
|
||||
if (orderEvent.Message.Contains("Assignment"))
|
||||
{
|
||||
if (orderEvent.FillPrice != 3100m)
|
||||
{
|
||||
throw new Exception("Option was not assigned at expected strike price (3100)");
|
||||
}
|
||||
if (orderEvent.Direction != OrderDirection.Sell || future.Holdings.Quantity != -1)
|
||||
{
|
||||
throw new Exception($"Expected Qty: -1 futures holdings for assigned future {future.Symbol}, found {future.Holdings.Quantity}");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (orderEvent.Direction == OrderDirection.Buy && future.Holdings.Quantity != 0)
|
||||
{
|
||||
// We buy back the underlying at expiration, so we expect a neutral position then
|
||||
throw new Exception($"Expected no holdings when liquidating future contract {future.Symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
|
||||
{
|
||||
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != -1)
|
||||
{
|
||||
throw new Exception($"No holdings were created for option contract {option.Symbol}");
|
||||
}
|
||||
if (orderEvent.IsAssignment && option.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception($"Holdings were found after option contract was assigned: {option.Symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">The algorithm has holdings</exception>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "3"},
|
||||
{"Average Win", "10.05%"},
|
||||
{"Average Loss", "-5.60%"},
|
||||
{"Compounding Annual Return", "8.121%"},
|
||||
{"Drawdown", "0.500%"},
|
||||
{"Expectancy", "0.396"},
|
||||
{"Net Profit", "3.880%"},
|
||||
{"Sharpe Ratio", "1.192"},
|
||||
{"Probabilistic Sharpe Ratio", "58.203%"},
|
||||
{"Loss Rate", "50%"},
|
||||
{"Win Rate", "50%"},
|
||||
{"Profit-Loss Ratio", "1.79"},
|
||||
{"Alpha", "0.069"},
|
||||
{"Beta", "0.003"},
|
||||
{"Annual Standard Deviation", "0.057"},
|
||||
{"Annual Variance", "0.003"},
|
||||
{"Information Ratio", "1.641"},
|
||||
{"Tracking Error", "0.18"},
|
||||
{"Treynor Ratio", "22.101"},
|
||||
{"Total Fees", "$7.40"},
|
||||
{"Fitness Score", "0.021"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "17.255"},
|
||||
{"Portfolio Turnover", "0.021"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1118389718"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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 System.Reflection;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests Out of The Money (OTM) future option expiry for short calls.
|
||||
/// We expect 1 order from the algorithm, which are:
|
||||
///
|
||||
/// * Initial entry, sell ES Call Option (expiring OTM)
|
||||
/// - Profit the option premium, since the option was not assigned.
|
||||
///
|
||||
/// Additionally, we test delistings for future options and assert that our
|
||||
/// portfolio holdings reflect the orders the algorithm has submitted.
|
||||
/// </summary>
|
||||
public class FutureOptionShortCallOTMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _es19m20;
|
||||
private Symbol _esOption;
|
||||
private Symbol _expectedContract;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 6, 30);
|
||||
|
||||
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
// which causes delisting events to never be processed, thus leading to options that might never
|
||||
// be exercised until the next data point arrives.
|
||||
AddEquity("AAPL", Resolution.Daily);
|
||||
|
||||
_es19m20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
new DateTime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
.Where(x => x.ID.StrikePrice >= 3400m && x.ID.OptionRight == OptionRight.Call)
|
||||
.OrderBy(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute).Symbol;
|
||||
|
||||
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3400m, new DateTime(2020, 6, 19));
|
||||
if (_esOption != _expectedContract)
|
||||
{
|
||||
throw new Exception($"Contract {_expectedContract} was not found in the chain");
|
||||
}
|
||||
|
||||
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
|
||||
{
|
||||
MarketOrder(_esOption, -1);
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
// Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
// the expected time. These assertions detect bug #4872
|
||||
foreach (var delisting in data.Delistings.Values)
|
||||
{
|
||||
if (delisting.Type == DelistingType.Warning)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 19))
|
||||
{
|
||||
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
if (delisting.Type == DelistingType.Delisted)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 20))
|
||||
{
|
||||
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
if (orderEvent.Status != OrderStatus.Filled)
|
||||
{
|
||||
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Securities.ContainsKey(orderEvent.Symbol))
|
||||
{
|
||||
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
var security = Securities[orderEvent.Symbol];
|
||||
if (security.Symbol == _es19m20)
|
||||
{
|
||||
throw new Exception($"Expected no order events for underlying Symbol {security.Symbol}");
|
||||
}
|
||||
|
||||
if (security.Symbol == _expectedContract)
|
||||
{
|
||||
AssertFutureOptionContractOrder(orderEvent, security);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
Log($"{orderEvent}");
|
||||
}
|
||||
|
||||
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security optionContract)
|
||||
{
|
||||
if (orderEvent.Direction == OrderDirection.Sell && optionContract.Holdings.Quantity != -1)
|
||||
{
|
||||
throw new Exception($"No holdings were created for option contract {optionContract.Symbol}");
|
||||
}
|
||||
if (orderEvent.Direction == OrderDirection.Buy && optionContract.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception("Expected no options holdings after closing position");
|
||||
}
|
||||
if (orderEvent.IsAssignment)
|
||||
{
|
||||
throw new Exception($"Assignment was not expected for {orderEvent.Symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">The algorithm has holdings</exception>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "1.81%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "3.745%"},
|
||||
{"Drawdown", "0.000%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "1.809%"},
|
||||
{"Sharpe Ratio", "1.292"},
|
||||
{"Probabilistic Sharpe Ratio", "65.890%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "100%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.031"},
|
||||
{"Beta", "0.001"},
|
||||
{"Annual Standard Deviation", "0.024"},
|
||||
{"Annual Variance", "0.001"},
|
||||
{"Information Ratio", "1.496"},
|
||||
{"Tracking Error", "0.173"},
|
||||
{"Treynor Ratio", "27.281"},
|
||||
{"Total Fees", "$3.70"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "95.176"},
|
||||
{"Portfolio Turnover", "0"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1364902860"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
/*
|
||||
* 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 System.Reflection;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests In The Money (ITM) future option expiry for short puts.
|
||||
/// We expect 3 orders from the algorithm, which are:
|
||||
///
|
||||
/// * Initial entry, sell ES Put Option (expiring ITM)
|
||||
/// * Option assignment, buy 1 contract of the underlying (ES)
|
||||
/// * Future contract expiry, liquidation (sell 1 ES future)
|
||||
///
|
||||
/// Additionally, we test delistings for future options and assert that our
|
||||
/// portfolio holdings reflect the orders the algorithm has submitted.
|
||||
/// </summary>
|
||||
public class FutureOptionShortPutITMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _es19m20;
|
||||
private Symbol _esOption;
|
||||
private Symbol _expectedContract;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 6, 30);
|
||||
|
||||
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
// which causes delisting events to never be processed, thus leading to options that might never
|
||||
// be exercised until the next data point arrives.
|
||||
AddEquity("AAPL", Resolution.Daily);
|
||||
|
||||
_es19m20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
new DateTime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
.Where(x => x.ID.StrikePrice <= 3400m && x.ID.OptionRight == OptionRight.Put)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute).Symbol;
|
||||
|
||||
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3400m, new DateTime(2020, 6, 19));
|
||||
if (_esOption != _expectedContract)
|
||||
{
|
||||
throw new Exception($"Contract {_expectedContract} was not found in the chain");
|
||||
}
|
||||
|
||||
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
|
||||
{
|
||||
MarketOrder(_esOption, -1);
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
// Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
// the expected time. These assertions detect bug #4872
|
||||
foreach (var delisting in data.Delistings.Values)
|
||||
{
|
||||
if (delisting.Type == DelistingType.Warning)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 19))
|
||||
{
|
||||
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
if (delisting.Type == DelistingType.Delisted)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 20))
|
||||
{
|
||||
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
if (orderEvent.Status != OrderStatus.Filled)
|
||||
{
|
||||
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Securities.ContainsKey(orderEvent.Symbol))
|
||||
{
|
||||
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
var security = Securities[orderEvent.Symbol];
|
||||
if (security.Symbol == _es19m20)
|
||||
{
|
||||
AssertFutureOptionOrderExercise(orderEvent, security, Securities[_expectedContract]);
|
||||
}
|
||||
else if (security.Symbol == _expectedContract)
|
||||
{
|
||||
AssertFutureOptionContractOrder(orderEvent, security);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
Log($"{orderEvent}");
|
||||
}
|
||||
|
||||
private void AssertFutureOptionOrderExercise(OrderEvent orderEvent, Security future, Security optionContract)
|
||||
{
|
||||
if (orderEvent.Message.Contains("Assignment"))
|
||||
{
|
||||
if (orderEvent.FillPrice != 3400)
|
||||
{
|
||||
throw new Exception("Option was not assigned at expected strike price (3400)");
|
||||
}
|
||||
if (orderEvent.Direction != OrderDirection.Buy || future.Holdings.Quantity != 1)
|
||||
{
|
||||
throw new Exception($"Expected Qty: 1 futures holdings for assigned future {future.Symbol}, found {future.Holdings.Quantity}");
|
||||
}
|
||||
}
|
||||
if (!orderEvent.Message.Contains("Assignment") && orderEvent.Direction == OrderDirection.Sell && future.Holdings.Quantity != 0)
|
||||
{
|
||||
// We buy back the underlying at expiration, so we expect a neutral position then
|
||||
throw new Exception($"Expected no holdings when liquidating future contract {future.Symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
|
||||
{
|
||||
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != -1)
|
||||
{
|
||||
throw new Exception($"No holdings were created for option contract {option.Symbol}");
|
||||
}
|
||||
if (orderEvent.IsAssignment && option.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception($"Holdings were found after option contract was assigned: {option.Symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">The algorithm has holdings</exception>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "3"},
|
||||
{"Average Win", "10.18%"},
|
||||
{"Average Loss", "-8.02%"},
|
||||
{"Compounding Annual Return", "2.773%"},
|
||||
{"Drawdown", "0.500%"},
|
||||
{"Expectancy", "0.135"},
|
||||
{"Net Profit", "1.343%"},
|
||||
{"Sharpe Ratio", "0.939"},
|
||||
{"Probabilistic Sharpe Ratio", "46.842%"},
|
||||
{"Loss Rate", "50%"},
|
||||
{"Win Rate", "50%"},
|
||||
{"Profit-Loss Ratio", "1.27"},
|
||||
{"Alpha", "0.023"},
|
||||
{"Beta", "0.002"},
|
||||
{"Annual Standard Deviation", "0.025"},
|
||||
{"Annual Variance", "0.001"},
|
||||
{"Information Ratio", "1.45"},
|
||||
{"Tracking Error", "0.173"},
|
||||
{"Treynor Ratio", "14.62"},
|
||||
{"Total Fees", "$7.40"},
|
||||
{"Fitness Score", "0.021"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "5.815"},
|
||||
{"Portfolio Turnover", "0.022"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "980293281"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* 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 System.Reflection;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests Out of The Money (OTM) future option expiry for short puts.
|
||||
/// We expect 1 order from the algorithm, which are:
|
||||
///
|
||||
/// * Initial entry, sell ES Put Option (expiring OTM)
|
||||
/// - Profit the option premium, since the option was not assigned.
|
||||
///
|
||||
/// Additionally, we test delistings for future options and assert that our
|
||||
/// portfolio holdings reflect the orders the algorithm has submitted.
|
||||
/// </summary>
|
||||
public class FutureOptionShortPutOTMExpiryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _es19m20;
|
||||
private Symbol _esOption;
|
||||
private Symbol _expectedContract;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 1, 5);
|
||||
SetEndDate(2020, 6, 30);
|
||||
|
||||
// We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
// which causes delisting events to never be processed, thus leading to options that might never
|
||||
// be exercised until the next data point arrives.
|
||||
AddEquity("AAPL", Resolution.Daily);
|
||||
|
||||
_es19m20 = AddFutureContract(
|
||||
QuantConnect.Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
new DateTime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol;
|
||||
|
||||
// Select a future option expiring ITM, and adds it to the algorithm.
|
||||
_esOption = AddFutureOptionContract(OptionChainProvider.GetOptionContractList(_es19m20, Time)
|
||||
.Where(x => x.ID.StrikePrice <= 3000m && x.ID.OptionRight == OptionRight.Put)
|
||||
.OrderByDescending(x => x.ID.StrikePrice)
|
||||
.Take(1)
|
||||
.Single(), Resolution.Minute).Symbol;
|
||||
|
||||
_expectedContract = QuantConnect.Symbol.CreateOption(_es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3000m, new DateTime(2020, 6, 19));
|
||||
if (_esOption != _expectedContract)
|
||||
{
|
||||
throw new Exception($"Contract {_expectedContract} was not found in the chain");
|
||||
}
|
||||
|
||||
Schedule.On(DateRules.Tomorrow, TimeRules.AfterMarketOpen(_es19m20, 1), () =>
|
||||
{
|
||||
MarketOrder(_esOption, -1);
|
||||
});
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
// Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
// the expected time. These assertions detect bug #4872
|
||||
foreach (var delisting in data.Delistings.Values)
|
||||
{
|
||||
if (delisting.Type == DelistingType.Warning)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 19))
|
||||
{
|
||||
throw new Exception($"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
if (delisting.Type == DelistingType.Delisted)
|
||||
{
|
||||
if (delisting.Time != new DateTime(2020, 6, 20))
|
||||
{
|
||||
throw new Exception($"Delisting happened at unexpected date: {delisting.Time}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
if (orderEvent.Status != OrderStatus.Filled)
|
||||
{
|
||||
// There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Securities.ContainsKey(orderEvent.Symbol))
|
||||
{
|
||||
throw new Exception($"Order event Symbol not found in Securities collection: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
var security = Securities[orderEvent.Symbol];
|
||||
if (security.Symbol == _es19m20)
|
||||
{
|
||||
throw new Exception($"Expected no order events for underlying Symbol {security.Symbol}");
|
||||
}
|
||||
if (security.Symbol == _expectedContract)
|
||||
{
|
||||
AssertFutureOptionContractOrder(orderEvent, security);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"Received order event for unknown Symbol: {orderEvent.Symbol}");
|
||||
}
|
||||
|
||||
Log($"{orderEvent}");
|
||||
}
|
||||
|
||||
private void AssertFutureOptionContractOrder(OrderEvent orderEvent, Security option)
|
||||
{
|
||||
if (orderEvent.Direction == OrderDirection.Sell && option.Holdings.Quantity != -1)
|
||||
{
|
||||
throw new Exception($"No holdings were created for option contract {option.Symbol}");
|
||||
}
|
||||
if (orderEvent.Direction == OrderDirection.Buy && option.Holdings.Quantity != 0)
|
||||
{
|
||||
throw new Exception("Expected no options holdings after closing position");
|
||||
}
|
||||
if (orderEvent.IsAssignment)
|
||||
{
|
||||
throw new Exception($"Assignment was not expected for {orderEvent.Symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ran at the end of the algorithm to ensure the algorithm has no holdings
|
||||
/// </summary>
|
||||
/// <exception cref="Exception">The algorithm has holdings</exception>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (Portfolio.Invested)
|
||||
{
|
||||
throw new Exception($"Expected no holdings at end of algorithm, but are invested in: {string.Join(", ", Portfolio.Keys)}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp, Language.Python };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Average Win", "3.28%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "6.852%"},
|
||||
{"Drawdown", "0.000%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "3.284%"},
|
||||
{"Sharpe Ratio", "1.319"},
|
||||
{"Probabilistic Sharpe Ratio", "66.574%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "100%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.058"},
|
||||
{"Beta", "0.002"},
|
||||
{"Annual Standard Deviation", "0.043"},
|
||||
{"Annual Variance", "0.002"},
|
||||
{"Information Ratio", "1.614"},
|
||||
{"Tracking Error", "0.176"},
|
||||
{"Treynor Ratio", "28.2"},
|
||||
{"Total Fees", "$3.70"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "150.252"},
|
||||
{"Portfolio Turnover", "0"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-418839052"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
}
|
||||
|
||||
var firstBar = history.First().Bars.GetValue(symbol);
|
||||
if (firstBar.EndTime != new DateTime(1998, 3, 3) || firstBar.Close != 26.3607004m)
|
||||
if (firstBar.EndTime != new DateTime(1998, 3, 3) || firstBar.Close != 25.11427695m)
|
||||
{
|
||||
throw new Exception("First History bar - unexpected data received");
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1459983342"}
|
||||
{"OrderListHash", "187652813"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,32 +65,32 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "2"},
|
||||
{"Total Trades", "5"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-0.98%"},
|
||||
{"Compounding Annual Return", "-53.792%"},
|
||||
{"Drawdown", "1.500%"},
|
||||
{"Average Loss", "-0.52%"},
|
||||
{"Compounding Annual Return", "246.602%"},
|
||||
{"Drawdown", "2.300%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-0.982%"},
|
||||
{"Sharpe Ratio", "-5.949"},
|
||||
{"Probabilistic Sharpe Ratio", "1.216%"},
|
||||
{"Net Profit", "1.602%"},
|
||||
{"Sharpe Ratio", "8.065"},
|
||||
{"Probabilistic Sharpe Ratio", "65.943%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "-0.973"},
|
||||
{"Beta", "0.268"},
|
||||
{"Annual Standard Deviation", "0.077"},
|
||||
{"Annual Variance", "0.006"},
|
||||
{"Information Ratio", "-14.167"},
|
||||
{"Tracking Error", "0.168"},
|
||||
{"Treynor Ratio", "-1.705"},
|
||||
{"Total Fees", "$6.51"},
|
||||
{"Fitness Score", "0.249"},
|
||||
{"Alpha", "-0.157"},
|
||||
{"Beta", "1.015"},
|
||||
{"Annual Standard Deviation", "0.223"},
|
||||
{"Annual Variance", "0.05"},
|
||||
{"Information Ratio", "-27.079"},
|
||||
{"Tracking Error", "0.005"},
|
||||
{"Treynor Ratio", "1.772"},
|
||||
{"Total Fees", "$16.28"},
|
||||
{"Fitness Score", "0.999"},
|
||||
{"Kelly Criterion Estimate", "38.64"},
|
||||
{"Kelly Criterion Probability Value", "0.229"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-55.465"},
|
||||
{"Portfolio Turnover", "0.498"},
|
||||
{"Return Over Maximum Drawdown", "78.607"},
|
||||
{"Portfolio Turnover", "1.246"},
|
||||
{"Total Insights Generated", "100"},
|
||||
{"Total Insights Closed", "99"},
|
||||
{"Total Insights Analysis Completed", "99"},
|
||||
@@ -104,7 +104,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "54.5455%"},
|
||||
{"Rolling Averaged Population Direction", "59.8056%"},
|
||||
{"Rolling Averaged Population Magnitude", "59.8056%"},
|
||||
{"OrderListHash", "160051570"}
|
||||
{"OrderListHash", "-1552239367"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
82
Algorithm.CSharp/OnOrderEventExceptionRegression.cs
Normal file
82
Algorithm.CSharp/OnOrderEventExceptionRegression.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression Algorithm for testing engine behavior with throwing errors in OnOrderEvent
|
||||
/// Should result in a RunTimeError status.
|
||||
/// Reference GH Issue #4947
|
||||
/// </summary>
|
||||
public class OnOrderEventExceptionRegression : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _spy = QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA);
|
||||
|
||||
/// <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", Resolution.Minute);
|
||||
}
|
||||
|
||||
/// <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 data)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
SetHoldings(_spy, 1);
|
||||
Debug("Purchased Stock");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OnOrderEvent is called whenever an order is updated
|
||||
/// </summary>
|
||||
/// <param name="orderEvent">Order Event</param>
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
throw new Exception("OnOrderEvent exception");
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <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>
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
||||
122
Algorithm.CSharp/OptionAssignmentRegressionAlgorithm.cs
Normal file
122
Algorithm.CSharp/OptionAssignmentRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm verifies automatic option contract assignment behavior.
|
||||
/// </summary>
|
||||
/// <meta name="tag" content="regression test" />
|
||||
/// <meta name="tag" content="options" />
|
||||
/// <meta name="tag" content="using data" />
|
||||
/// <meta name="tag" content="filter selection" />
|
||||
public class OptionAssignmentRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Security Stock;
|
||||
|
||||
private Security CallOption;
|
||||
private Symbol CallOptionSymbol;
|
||||
|
||||
private Security PutOption;
|
||||
private Symbol PutOptionSymbol;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2015, 12, 23);
|
||||
SetEndDate(2015, 12, 24);
|
||||
SetCash(100000);
|
||||
Stock = AddEquity("GOOG", Resolution.Minute);
|
||||
|
||||
var contracts = OptionChainProvider.GetOptionContractList(Stock.Symbol, UtcTime).ToList();
|
||||
|
||||
PutOptionSymbol = contracts
|
||||
.Where(c => c.ID.OptionRight == OptionRight.Put)
|
||||
.OrderBy(c => c.ID.Date)
|
||||
.First(c => c.ID.StrikePrice == 800m);
|
||||
|
||||
CallOptionSymbol = contracts
|
||||
.Where(c => c.ID.OptionRight == OptionRight.Call)
|
||||
.OrderBy(c => c.ID.Date)
|
||||
.First(c => c.ID.StrikePrice == 600m);
|
||||
|
||||
PutOption = AddOptionContract(PutOptionSymbol);
|
||||
CallOption = AddOptionContract(CallOptionSymbol);
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (!Portfolio.Invested && Stock.Price != 0 && PutOption.Price != 0 && CallOption.Price != 0)
|
||||
{
|
||||
// this gets executed on start and after each auto-assignment, finally ending with expiration assignment
|
||||
MarketOrder(PutOptionSymbol, -1);
|
||||
MarketOrder(CallOptionSymbol, -1);
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanRunLocally { get; } = true;
|
||||
public Language[] Languages { get; } = {Language.CSharp};
|
||||
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "22"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0%"},
|
||||
{"Drawdown", "0%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0%"},
|
||||
{"Sharpe 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", "$12.00"},
|
||||
{"Fitness Score", "0.5"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-50.218"},
|
||||
{"Portfolio Turnover", "6.713"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1597098916"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ using QuantConnect.Interfaces;
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Demonstration of the Option Chain Provider -- a much faster mechanism for manually specifying the option contracts you'd like to recieve
|
||||
/// Demonstration of the Option Chain Provider -- a much faster mechanism for manually specifying the option contracts you'd like to receive
|
||||
/// data for and manually subscribing to them.
|
||||
/// </summary>
|
||||
/// <meta name="tag" content="strategy example" />
|
||||
|
||||
@@ -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.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Algorithm.Framework.Selection;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm making sure that the added universe selection does not remove the option chain during it's daily refresh
|
||||
/// </summary>
|
||||
public class OptionChainedAndUniverseSelectionRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol _aaplOption;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
UniverseSettings.Resolution = Resolution.Minute;
|
||||
|
||||
SetStartDate(2014, 06, 05);
|
||||
SetEndDate(2014, 06, 09);
|
||||
|
||||
_aaplOption = AddOption("AAPL").Symbol;
|
||||
AddUniverseSelection(new DailyUniverseSelectionModel("MyCustomSelectionModel", time => new[] { "AAPL" }, this));
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
Buy("AAPL", 1);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
var config = SubscriptionManager.Subscriptions.ToList();
|
||||
if (config.All(dataConfig => dataConfig.Symbol != "AAPL"))
|
||||
{
|
||||
throw new Exception("Was expecting configurations for AAPL");
|
||||
}
|
||||
if (config.All(dataConfig => dataConfig.Symbol.SecurityType != SecurityType.Option))
|
||||
{
|
||||
throw new Exception($"Was expecting configurations for {_aaplOption}");
|
||||
}
|
||||
}
|
||||
|
||||
private class DailyUniverseSelectionModel : CustomUniverseSelectionModel
|
||||
{
|
||||
private DateTime _lastRefresh;
|
||||
private IAlgorithm _algorithm;
|
||||
|
||||
public DailyUniverseSelectionModel(string name, Func<DateTime, IEnumerable<string>> selector, IAlgorithm algorithm) : base(name, selector)
|
||||
{
|
||||
_algorithm = algorithm;
|
||||
}
|
||||
|
||||
public override DateTime GetNextRefreshTimeUtc()
|
||||
{
|
||||
if (_lastRefresh != _algorithm.Time.Date)
|
||||
{
|
||||
_lastRefresh = _algorithm.Time.Date;
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
return DateTime.MaxValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "0.678%"},
|
||||
{"Drawdown", "3.700%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0.009%"},
|
||||
{"Sharpe Ratio", "7.969"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.046"},
|
||||
{"Beta", "-0.032"},
|
||||
{"Annual Standard Deviation", "0.001"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-24.461"},
|
||||
{"Tracking Error", "0.044"},
|
||||
{"Treynor Ratio", "-0.336"},
|
||||
{"Total Fees", "$1.00"},
|
||||
{"Fitness Score", "0.003"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "0.003"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1779427412"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1038273097"}
|
||||
{"OrderListHash", "-1726463684"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,6 +56,13 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <param name="slice">The current slice of data keyed by symbol string</param>
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
foreach (var dividend in slice.Dividends.Values)
|
||||
{
|
||||
if (dividend.ReferencePrice != 32.59m || dividend.Distribution != 3.82m)
|
||||
{
|
||||
throw new Exception($"{Time} - Invalid dividend {dividend}");
|
||||
}
|
||||
}
|
||||
if (!Portfolio.Invested)
|
||||
{
|
||||
if (Time.Day == 28 && Time.Hour > 9 && Time.Minute > 0)
|
||||
@@ -139,11 +146,11 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Trades", "4"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "-0.02%"},
|
||||
{"Compounding Annual Return", "-0.453%"},
|
||||
{"Compounding Annual Return", "-0.492%"},
|
||||
{"Drawdown", "0.000%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-0.006%"},
|
||||
{"Sharpe Ratio", "-3.554"},
|
||||
{"Sharpe Ratio", "-3.943"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
@@ -152,15 +159,15 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0.002"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-3.554"},
|
||||
{"Information Ratio", "-3.943"},
|
||||
{"Tracking Error", "0.002"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$4.00"},
|
||||
{"Fitness Score", "0.001"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-1.768"},
|
||||
{"Return Over Maximum Drawdown", "-2.808"},
|
||||
{"Portfolio Turnover", "0.001"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
|
||||
@@ -142,6 +142,31 @@
|
||||
<Link>Properties\SharedAssemblyInfo.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="AddAlphaModelAlgorithm.cs" />
|
||||
<Compile Include="CustomBuyingPowerModelAlgorithm.cs" />
|
||||
<Compile Include="AddFutureOptionContractDataStreamingRegressionAlgorithm.cs" />
|
||||
<Compile Include="AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm.cs" />
|
||||
<Compile Include="AddOptionContractExpiresRegressionAlgorithm.cs" />
|
||||
<Compile Include="AltData\QuiverWallStreetBetsDataAlgorithm.cs" />
|
||||
<Compile Include="FutureOptionCallITMGreeksExpiryRegressionAlgorithm.cs" />
|
||||
<Compile Include="OnOrderEventExceptionRegression.cs" />
|
||||
<Compile Include="FutureOptionCallITMExpiryRegressionAlgorithm.cs" />
|
||||
<Compile Include="FutureOptionCallOTMExpiryRegressionAlgorithm.cs" />
|
||||
<Compile Include="FutureOptionPutITMExpiryRegressionAlgorithm.cs" />
|
||||
<Compile Include="FutureOptionPutOTMExpiryRegressionAlgorithm.cs" />
|
||||
<Compile Include="FutureOptionBuySellCallIntradayRegressionAlgorithm.cs" />
|
||||
<Compile Include="FutureOptionShortCallITMExpiryRegressionAlgorithm.cs" />
|
||||
<Compile Include="FutureOptionShortCallOTMExpiryRegressionAlgorithm.cs" />
|
||||
<Compile Include="FutureOptionShortPutOTMExpiryRegressionAlgorithm.cs" />
|
||||
<Compile Include="FutureOptionShortPutITMExpiryRegressionAlgorithm.cs" />
|
||||
<Compile Include="ScaledFillForwardDataRegressionAlgorithm.cs" />
|
||||
<Compile Include="DailyHistoryForDailyResolutionRegressionAlgorithm.cs" />
|
||||
<Compile Include="DailyHistoryForMinuteResolutionRegressionAlgorithm.cs" />
|
||||
<Compile Include="ExtendedMarketHoursHistoryRegressionAlgorithm.cs" />
|
||||
<Compile Include="EquityTickQuoteAdjustedModeRegressionAlgorithm.cs" />
|
||||
<Compile Include="AddOptionContractFromUniverseRegressionAlgorithm.cs" />
|
||||
<Compile Include="CoarseFineOptionUniverseChainRegressionAlgorithm.cs" />
|
||||
<Compile Include="OptionChainedAndUniverseSelectionRegressionAlgorithm.cs" />
|
||||
<Compile Include="OptionAssignmentRegressionAlgorithm.cs" />
|
||||
<Compile Include="SwitchDataModeRegressionAlgorithm.cs" />
|
||||
<Compile Include="AddRemoveOptionUniverseRegressionAlgorithm.cs" />
|
||||
<Compile Include="AddRemoveSecurityRegressionAlgorithm.cs" />
|
||||
@@ -356,6 +381,7 @@
|
||||
<Compile Include="USEnergyInformationAdministrationAlgorithm.cs" />
|
||||
<Compile Include="UserDefinedUniverseAlgorithm.cs" />
|
||||
<Compile Include="VolumeWeightedAveragePriceExecutionModelRegressionAlgorithm.cs" />
|
||||
<Compile Include="WarmUpAfterIntializeRegression.cs" />
|
||||
<Compile Include="WarmupAlgorithm.cs" />
|
||||
<Compile Include="WarmupConversionRatesRegressionAlgorithm.cs" />
|
||||
<Compile Include="WarmupHistoryAlgorithm.cs" />
|
||||
@@ -431,7 +457,7 @@
|
||||
<Analyzer Include="..\packages\Microsoft.NetFramework.Analyzers.2.9.3\analyzers\dotnet\cs\Microsoft.NetFramework.CSharp.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DaylightSavingTimeHistoryRegressionTest.cs" />
|
||||
<Compile Include="DaylightSavingTimeHistoryRegressionAlgorithm.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
|
||||
143
Algorithm.CSharp/ScaledFillForwardDataRegressionAlgorithm.cs
Normal file
143
Algorithm.CSharp/ScaledFillForwardDataRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression test algorithm reproduces issue https://github.com/QuantConnect/Lean/issues/4834
|
||||
/// fixed in PR https://github.com/QuantConnect/Lean/pull/4836
|
||||
/// Adjusted data of fill forward bars should use original scale factor
|
||||
/// </summary>
|
||||
public class ScaledFillForwardDataRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private TradeBar _lastRealBar;
|
||||
private Symbol _twx;
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2014, 6, 5);
|
||||
SetEndDate(2014, 6, 9);
|
||||
|
||||
_twx = AddEquity("TWX", Resolution.Minute, extendedMarketHours: true).Symbol;
|
||||
Schedule.On(DateRules.EveryDay(_twx), TimeRules.Every(TimeSpan.FromHours(1)), PlotPrice);
|
||||
}
|
||||
|
||||
private void PlotPrice()
|
||||
{
|
||||
Plot($"{_twx}", "Ask", Securities[_twx].AskPrice);
|
||||
Plot($"{_twx}", "Bid", Securities[_twx].BidPrice);
|
||||
Plot($"{_twx}", "Price", Securities[_twx].Price);
|
||||
Plot("Portfolio.TPV", "Value", Portfolio.TotalPortfolioValue);
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
var current = data.Bars.FirstOrDefault().Value;
|
||||
if (current != null)
|
||||
{
|
||||
if (Time == new DateTime(2014, 06, 09, 4, 1, 0) && !Portfolio.Invested)
|
||||
{
|
||||
if (!current.IsFillForward)
|
||||
{
|
||||
throw new Exception($"Was expecting a first fill forward bar {Time}");
|
||||
}
|
||||
|
||||
// trade on the first bar after a factor price scale change. +10 so we fill ASAP. Limit so it fills in extended market hours
|
||||
LimitOrder(_twx, 1000, _lastRealBar.Close + 10);
|
||||
}
|
||||
|
||||
if (_lastRealBar == null || !current.IsFillForward)
|
||||
{
|
||||
_lastRealBar = current;
|
||||
}
|
||||
else if (_lastRealBar.Close != current.Close)
|
||||
{
|
||||
throw new Exception($"FillForwarded data point at {Time} was scaled. Actual: {current.Close}; Expected: {_lastRealBar.Close}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (_lastRealBar == null)
|
||||
{
|
||||
throw new Exception($"Not all expected data points were 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 Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "32.825%"},
|
||||
{"Drawdown", "0.800%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0.377%"},
|
||||
{"Sharpe Ratio", "8.953"},
|
||||
{"Probabilistic Sharpe Ratio", "95.977%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.314"},
|
||||
{"Beta", "-0.104"},
|
||||
{"Annual Standard Deviation", "0.03"},
|
||||
{"Annual Variance", "0.001"},
|
||||
{"Information Ratio", "-3.498"},
|
||||
{"Tracking Error", "0.05"},
|
||||
{"Treynor Ratio", "-2.573"},
|
||||
{"Total Fees", "$5.00"},
|
||||
{"Fitness Score", "0.158"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
|
||||
{"Portfolio Turnover", "0.158"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
{"Long Insight Count", "0"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$0"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$0"},
|
||||
{"Mean Population Estimated Insight Value", "$0"},
|
||||
{"Mean Population Direction", "0%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "960108217"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -254,13 +254,13 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Information Ratio", "0"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$48.56"},
|
||||
{"Total Fees", "$48.58"},
|
||||
{"Fitness Score", "0.5"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-141.917"},
|
||||
{"Portfolio Turnover", "2.001"},
|
||||
{"Return Over Maximum Drawdown", "-141.877"},
|
||||
{"Portfolio Turnover", "2.002"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
@@ -274,7 +274,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-22119963"}
|
||||
{"OrderListHash", "-263077697"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
if (_expectedCloseValues.Count > 0)
|
||||
{
|
||||
throw new Exception($"Not all expected data points were recieved.");
|
||||
throw new Exception($"Not all expected data points were received.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-569072921"}
|
||||
{"OrderListHash", "359885308"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,28 +117,28 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Trades", "3528"},
|
||||
{"Average Win", "0.67%"},
|
||||
{"Average Loss", "-0.71%"},
|
||||
{"Compounding Annual Return", "17.318%"},
|
||||
{"Compounding Annual Return", "17.227%"},
|
||||
{"Drawdown", "63.700%"},
|
||||
{"Expectancy", "0.020"},
|
||||
{"Net Profit", "17.318%"},
|
||||
{"Sharpe Ratio", "0.836"},
|
||||
{"Probabilistic Sharpe Ratio", "33.715%"},
|
||||
{"Net Profit", "17.227%"},
|
||||
{"Sharpe Ratio", "0.834"},
|
||||
{"Probabilistic Sharpe Ratio", "33.688%"},
|
||||
{"Loss Rate", "48%"},
|
||||
{"Win Rate", "52%"},
|
||||
{"Profit-Loss Ratio", "0.95"},
|
||||
{"Alpha", "0.826"},
|
||||
{"Alpha", "0.825"},
|
||||
{"Beta", "-0.34"},
|
||||
{"Annual Standard Deviation", "0.945"},
|
||||
{"Annual Variance", "0.893"},
|
||||
{"Information Ratio", "0.714"},
|
||||
{"Information Ratio", "0.713"},
|
||||
{"Tracking Error", "0.957"},
|
||||
{"Treynor Ratio", "-2.325"},
|
||||
{"Total Fees", "$24713.42"},
|
||||
{"Treynor Ratio", "-2.323"},
|
||||
{"Total Fees", "$24760.85"},
|
||||
{"Fitness Score", "0.54"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "0.24"},
|
||||
{"Return Over Maximum Drawdown", "0.272"},
|
||||
{"Sortino Ratio", "0.238"},
|
||||
{"Return Over Maximum Drawdown", "0.27"},
|
||||
{"Portfolio Turnover", "7.204"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
@@ -153,7 +153,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1547947497"}
|
||||
{"OrderListHash", "843493486"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,32 +172,32 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "5"},
|
||||
{"Total Trades", "4"},
|
||||
{"Average Win", "0.64%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "-74.197%"},
|
||||
{"Drawdown", "6.600%"},
|
||||
{"Compounding Annual Return", "-56.577%"},
|
||||
{"Drawdown", "3.800%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "-6.115%"},
|
||||
{"Sharpe Ratio", "-2.281"},
|
||||
{"Probabilistic Sharpe Ratio", "11.870%"},
|
||||
{"Net Profit", "-3.811%"},
|
||||
{"Sharpe Ratio", "-2.773"},
|
||||
{"Probabilistic Sharpe Ratio", "13.961%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "100%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "-0.684"},
|
||||
{"Beta", "-0.113"},
|
||||
{"Annual Standard Deviation", "0.292"},
|
||||
{"Annual Variance", "0.085"},
|
||||
{"Information Ratio", "-1.606"},
|
||||
{"Tracking Error", "0.312"},
|
||||
{"Treynor Ratio", "5.866"},
|
||||
{"Total Fees", "$5.00"},
|
||||
{"Fitness Score", "0.017"},
|
||||
{"Alpha", "-0.504"},
|
||||
{"Beta", "-0.052"},
|
||||
{"Annual Standard Deviation", "0.179"},
|
||||
{"Annual Variance", "0.032"},
|
||||
{"Information Ratio", "-1.599"},
|
||||
{"Tracking Error", "0.207"},
|
||||
{"Treynor Ratio", "9.508"},
|
||||
{"Total Fees", "$4.00"},
|
||||
{"Fitness Score", "0.008"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-2.584"},
|
||||
{"Return Over Maximum Drawdown", "-11.287"},
|
||||
{"Portfolio Turnover", "0.177"},
|
||||
{"Sortino Ratio", "-3.791"},
|
||||
{"Return Over Maximum Drawdown", "-14.846"},
|
||||
{"Portfolio Turnover", "0.136"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
@@ -211,7 +211,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1386253041"}
|
||||
{"OrderListHash", "1484950465"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
private Symbol _spy;
|
||||
private int _reselectedSpy = -1;
|
||||
private DateTime lastDataTime = DateTime.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
|
||||
@@ -57,6 +58,13 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (lastDataTime == data.Time)
|
||||
{
|
||||
throw new Exception("Duplicate time for current data and last data slice");
|
||||
}
|
||||
|
||||
lastDataTime = data.Time;
|
||||
|
||||
if (_reselectedSpy == 0)
|
||||
{
|
||||
if (!Securities[_spy].IsTradable)
|
||||
@@ -111,29 +119,29 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Trades", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "75.079%"},
|
||||
{"Drawdown", "2.200%"},
|
||||
{"Compounding Annual Return", "69.904%"},
|
||||
{"Drawdown", "2.000%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "4.711%"},
|
||||
{"Sharpe Ratio", "5.067"},
|
||||
{"Probabilistic Sharpe Ratio", "84.391%"},
|
||||
{"Net Profit", "4.453%"},
|
||||
{"Sharpe Ratio", "4.805"},
|
||||
{"Probabilistic Sharpe Ratio", "83.459%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.562"},
|
||||
{"Beta", "0.02"},
|
||||
{"Annual Standard Deviation", "0.113"},
|
||||
{"Annual Variance", "0.013"},
|
||||
{"Information Ratio", "0.511"},
|
||||
{"Tracking Error", "0.159"},
|
||||
{"Treynor Ratio", "28.945"},
|
||||
{"Total Fees", "$3.22"},
|
||||
{"Fitness Score", "0.037"},
|
||||
{"Alpha", "0.501"},
|
||||
{"Beta", "0.068"},
|
||||
{"Annual Standard Deviation", "0.111"},
|
||||
{"Annual Variance", "0.012"},
|
||||
{"Information Ratio", "0.284"},
|
||||
{"Tracking Error", "0.153"},
|
||||
{"Treynor Ratio", "7.844"},
|
||||
{"Total Fees", "$3.23"},
|
||||
{"Fitness Score", "0.038"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "17.868"},
|
||||
{"Return Over Maximum Drawdown", "34.832"},
|
||||
{"Portfolio Turnover", "0.037"},
|
||||
{"Sortino Ratio", "16.857"},
|
||||
{"Return Over Maximum Drawdown", "34.897"},
|
||||
{"Portfolio Turnover", "0.038"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
@@ -147,7 +155,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "836605283"}
|
||||
{"OrderListHash", "1664042885"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
61
Algorithm.CSharp/WarmUpAfterIntializeRegression.cs
Normal file
61
Algorithm.CSharp/WarmUpAfterIntializeRegression.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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 QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression algorithm to test warming up after initialize behavior, should throw if used outside of initialize
|
||||
/// Reference GH Issue #4939
|
||||
/// </summary>
|
||||
public class WarmUpAfterIntializeRegression : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 07); //Set Start Date
|
||||
SetEndDate(2013, 10, 11); //Set End Date
|
||||
SetCash(100000);
|
||||
var equity = AddEquity("SPY");
|
||||
}
|
||||
|
||||
public override void OnData(Slice slice)
|
||||
{
|
||||
// Should throw and set Algorithm status to be runtime error
|
||||
SetWarmUp(TimeSpan.FromDays(2));
|
||||
}
|
||||
|
||||
/// <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 Language[] Languages { get; } = { Language.CSharp };
|
||||
|
||||
/// <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>
|
||||
{
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -105,8 +105,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-315.532"},
|
||||
{"Portfolio Turnover", "0.998"},
|
||||
{"Return Over Maximum Drawdown", "-315.48"},
|
||||
{"Portfolio Turnover", "0.999"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
@@ -120,7 +120,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1318619937"}
|
||||
{"OrderListHash", "1703396395"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,9 +67,11 @@ namespace QuantConnect.Algorithm.Framework.Risk
|
||||
}
|
||||
|
||||
var pnl = GetTotalDrawdownPercent(currentValue);
|
||||
if (pnl < _maximumDrawdownPercent)
|
||||
if (pnl < _maximumDrawdownPercent && targets.Length != 0)
|
||||
{
|
||||
foreach(var target in targets)
|
||||
// reset the trailing high value for restart investing on next rebalcing period
|
||||
_initialised = false;
|
||||
foreach (var target in targets)
|
||||
yield return new PortfolioTarget(target.Symbol, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,10 +54,11 @@ class MaximumDrawdownPercentPortfolio(RiskManagementModel):
|
||||
return [] # return if new high reached
|
||||
|
||||
pnl = self.GetTotalDrawdownPercent(currentValue)
|
||||
if pnl < self.maximumDrawdownPercent:
|
||||
if pnl < self.maximumDrawdownPercent and len(targets) != 0:
|
||||
self.initialised = False # reset the trailing high value for restart investing on next rebalcing period
|
||||
return [ PortfolioTarget(target.Symbol, 0) for target in targets ]
|
||||
|
||||
return []
|
||||
|
||||
def GetTotalDrawdownPercent(self, currentValue):
|
||||
return (float(currentValue) / float(self.portfolioHigh)) - 1.0
|
||||
return (float(currentValue) / float(self.portfolioHigh)) - 1.0
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace QuantConnect.Algorithm.Framework.Selection
|
||||
/// </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).ExchangeHours)));
|
||||
return filter.Contracts(FilterByOpenInterest(filter.ToDictionary(x => x, x => _marketHoursDatabase.GetEntry(x.ID.Market, x, x.ID.SecurityType))));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,7 +69,7 @@ namespace QuantConnect.Algorithm.Framework.Selection
|
||||
/// </summary>
|
||||
/// <param name="contracts">Contracts to filter</param>
|
||||
/// <returns>Filtered set</returns>
|
||||
public IEnumerable<Symbol> FilterByOpenInterest(IReadOnlyDictionary<Symbol, SecurityExchangeHours> contracts)
|
||||
public IEnumerable<Symbol> FilterByOpenInterest(IReadOnlyDictionary<Symbol, MarketHoursDatabase.Entry> contracts)
|
||||
{
|
||||
var symbols = new List<Symbol>(_chainContractsLookupLimit.HasValue ? contracts.Keys.OrderBy(x => x.ID.Date).Take(_chainContractsLookupLimit.Value) : contracts.Keys);
|
||||
var openInterest = symbols.GroupBy(x => contracts[x]).SelectMany(g => GetOpenInterest(g.Key, g.Select(i => i))).ToDictionary(x => x.Key, x => x.Value);
|
||||
@@ -91,11 +91,12 @@ namespace QuantConnect.Algorithm.Framework.Selection
|
||||
return filtered;
|
||||
}
|
||||
|
||||
private Dictionary<Symbol, decimal> GetOpenInterest(SecurityExchangeHours exchangeHours, IEnumerable<Symbol> symbols)
|
||||
private Dictionary<Symbol, decimal> GetOpenInterest(MarketHoursDatabase.Entry marketHours, IEnumerable<Symbol> symbols)
|
||||
{
|
||||
var current = _algorithm.UtcTime;
|
||||
var exchangeHours = marketHours.ExchangeHours;
|
||||
var endTime = Instant.FromDateTimeUtc(_algorithm.UtcTime).InZone(exchangeHours.TimeZone).ToDateTimeUnspecified();
|
||||
var previousDay = Time.GetStartTimeForTradeBars(exchangeHours, endTime, Time.OneDay, 1, true);
|
||||
var previousDay = Time.GetStartTimeForTradeBars(exchangeHours, endTime, Time.OneDay, 1, true, marketHours.DataTimeZone);
|
||||
var requests = symbols.Select(
|
||||
symbol => new HistoryRequest(
|
||||
previousDay,
|
||||
|
||||
@@ -15,12 +15,9 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Auxiliary;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Option;
|
||||
|
||||
namespace QuantConnect.Algorithm.Framework.Selection
|
||||
{
|
||||
@@ -99,61 +96,19 @@ namespace QuantConnect.Algorithm.Framework.Selection
|
||||
var uniqueUnderlyingSymbols = new HashSet<Symbol>();
|
||||
foreach (var optionSymbol in _optionChainSymbolSelector(algorithm.UtcTime))
|
||||
{
|
||||
if (optionSymbol.SecurityType != SecurityType.Option)
|
||||
if (optionSymbol.SecurityType != SecurityType.Option && optionSymbol.SecurityType != SecurityType.FutureOption)
|
||||
{
|
||||
throw new ArgumentException("optionChainSymbolSelector must return option symbols.");
|
||||
throw new ArgumentException("optionChainSymbolSelector must return option or futures options symbols.");
|
||||
}
|
||||
|
||||
// prevent creating duplicate option chains -- one per underlying
|
||||
if (uniqueUnderlyingSymbols.Add(optionSymbol.Underlying))
|
||||
{
|
||||
yield return CreateOptionChain(algorithm, optionSymbol);
|
||||
yield return algorithm.CreateOptionChain(optionSymbol, Filter, _universeSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the canonical <see cref="Option"/> chain security for a given symbol
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The algorithm instance to create universes for</param>
|
||||
/// <param name="symbol">Symbol of the option</param>
|
||||
/// <param name="settings">Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed</param>
|
||||
/// <param name="initializer">Performs extra initialization (such as setting models) after we create a new security object</param>
|
||||
/// <returns><see cref="Option"/> for the given symbol</returns>
|
||||
[Obsolete("This method is obsolete because SecurityInitializer is obsolete and will not be used.")]
|
||||
protected virtual Option CreateOptionChainSecurity(QCAlgorithm algorithm, Symbol symbol, UniverseSettings settings, ISecurityInitializer initializer)
|
||||
{
|
||||
return CreateOptionChainSecurity(
|
||||
algorithm.SubscriptionManager.SubscriptionDataConfigService,
|
||||
symbol,
|
||||
settings,
|
||||
algorithm.Securities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the canonical <see cref="Option"/> chain security for a given symbol
|
||||
/// </summary>
|
||||
/// <param name="subscriptionDataConfigService">The service used to create new <see cref="SubscriptionDataConfig"/></param>
|
||||
/// <param name="symbol">Symbol of the option</param>
|
||||
/// <param name="settings">Universe settings define attributes of created subscriptions, such as their resolution and the minimum time in universe before they can be removed</param>
|
||||
/// <param name="securityManager">Used to create new <see cref="Security"/></param>
|
||||
/// <returns><see cref="Option"/> for the given symbol</returns>
|
||||
protected virtual Option CreateOptionChainSecurity(
|
||||
ISubscriptionDataConfigService subscriptionDataConfigService,
|
||||
Symbol symbol,
|
||||
UniverseSettings settings,
|
||||
SecurityManager securityManager)
|
||||
{
|
||||
var config = subscriptionDataConfigService.Add(
|
||||
typeof(ZipEntryName),
|
||||
symbol,
|
||||
settings.Resolution,
|
||||
settings.FillForward,
|
||||
settings.ExtendedMarketHours,
|
||||
false);
|
||||
return (Option)securityManager.CreateSecurity(symbol, config, settings.Leverage, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the option chain universe filter
|
||||
/// </summary>
|
||||
@@ -162,55 +117,5 @@ namespace QuantConnect.Algorithm.Framework.Selection
|
||||
// NOP
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="OptionChainUniverse"/> for a given symbol
|
||||
/// </summary>
|
||||
/// <param name="algorithm">The algorithm instance to create universes for</param>
|
||||
/// <param name="symbol">Symbol of the option</param>
|
||||
/// <returns><see cref="OptionChainUniverse"/> for the given symbol</returns>
|
||||
private OptionChainUniverse CreateOptionChain(QCAlgorithm algorithm, Symbol symbol)
|
||||
{
|
||||
if (symbol.SecurityType != SecurityType.Option)
|
||||
{
|
||||
throw new ArgumentException("CreateOptionChain requires an option symbol.");
|
||||
}
|
||||
|
||||
// rewrite non-canonical symbols to be canonical
|
||||
var market = symbol.ID.Market;
|
||||
var underlying = symbol.Underlying;
|
||||
if (!symbol.IsCanonical())
|
||||
{
|
||||
var alias = $"?{underlying.Value}";
|
||||
symbol = Symbol.Create(underlying.Value, SecurityType.Option, market, alias);
|
||||
}
|
||||
|
||||
// resolve defaults if not specified
|
||||
var settings = _universeSettings ?? algorithm.UniverseSettings;
|
||||
|
||||
// create canonical security object, but don't duplicate if it already exists
|
||||
Security security;
|
||||
Option optionChain;
|
||||
if (!algorithm.Securities.TryGetValue(symbol, out security))
|
||||
{
|
||||
optionChain = CreateOptionChainSecurity(
|
||||
algorithm.SubscriptionManager.SubscriptionDataConfigService,
|
||||
symbol,
|
||||
settings,
|
||||
algorithm.Securities);
|
||||
}
|
||||
else
|
||||
{
|
||||
optionChain = (Option)security;
|
||||
}
|
||||
|
||||
// set the option chain contract filter function
|
||||
optionChain.SetFilter(Filter);
|
||||
|
||||
// force option chain security to not be directly tradable AFTER it's configured to ensure it's not overwritten
|
||||
optionChain.IsTradable = false;
|
||||
|
||||
return new OptionChainUniverse(optionChain, settings, algorithm.LiveMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,8 @@ class OptionUniverseSelectionModel(UniverseSelectionModel):
|
||||
|
||||
uniqueUnderlyingSymbols = set()
|
||||
for optionSymbol in self.optionChainSymbolSelector(algorithm.UtcTime):
|
||||
if optionSymbol.SecurityType != SecurityType.Option:
|
||||
raise ValueError("optionChainSymbolSelector must return option symbols.")
|
||||
if optionSymbol.SecurityType != SecurityType.Option and optionSymbol.SecurityType != SecurityType.FutureOption:
|
||||
raise ValueError("optionChainSymbolSelector must return option or futures options symbols.")
|
||||
|
||||
# prevent creating duplicate option chains -- one per underlying
|
||||
if optionSymbol.Underlying not in uniqueUnderlyingSymbols:
|
||||
@@ -73,7 +73,7 @@ class OptionUniverseSelectionModel(UniverseSelectionModel):
|
||||
symbol: Symbol of the option
|
||||
Returns:
|
||||
OptionChainUniverse for the given symbol'''
|
||||
if symbol.SecurityType != SecurityType.Option:
|
||||
if symbol.SecurityType != SecurityType.Option and symbol.SecurityType != SecurityType.FutureOption:
|
||||
raise ValueError("CreateOptionChain requires an option symbol.")
|
||||
|
||||
# rewrite non-canonical symbols to be canonical
|
||||
@@ -122,4 +122,4 @@ class OptionUniverseSelectionModel(UniverseSelectionModel):
|
||||
def Filter(self, filter):
|
||||
'''Defines the option chain universe filter'''
|
||||
# NOP
|
||||
return filter
|
||||
return filter
|
||||
|
||||
10
Algorithm.Python/.idea/Algorithm.Python.iml
generated
10
Algorithm.Python/.idea/Algorithm.Python.iml
generated
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/stubs" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
4
Algorithm.Python/.idea/misc.xml
generated
4
Algorithm.Python/.idea/misc.xml
generated
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.6" project-jdk-type="Python SDK" />
|
||||
</project>
|
||||
8
Algorithm.Python/.idea/modules.xml
generated
8
Algorithm.Python/.idea/modules.xml
generated
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/Algorithm.Python.iml" filepath="$PROJECT_DIR$/.idea/Algorithm.Python.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
5
Algorithm.Python/.vscode/settings.json
vendored
5
Algorithm.Python/.vscode/settings.json
vendored
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"python.autoComplete.extraPaths": [
|
||||
"stubs"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
# 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, timedelta
|
||||
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
from QuantConnect import Market
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm tests that we receive the expected data when
|
||||
### we add future option contracts individually using <see cref="AddFutureOptionContract"/>
|
||||
### </summary>
|
||||
class AddFutureOptionContractDataStreamingRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
self.onDataReached = False
|
||||
self.invested = False
|
||||
self.symbolsReceived = []
|
||||
self.expectedSymbolsReceived = []
|
||||
self.dataReceived = {}
|
||||
|
||||
self.SetStartDate(2020, 1, 5)
|
||||
self.SetEndDate(2020, 1, 6)
|
||||
|
||||
self.es20h20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, datetime(2020, 3, 20)),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
self.es19m20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, datetime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
optionChains = self.OptionChainProvider.GetOptionContractList(self.es20h20, self.Time)
|
||||
optionChains += self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time)
|
||||
|
||||
for optionContract in optionChains:
|
||||
self.expectedSymbolsReceived.append(self.AddFutureOptionContract(optionContract, Resolution.Minute).Symbol)
|
||||
|
||||
def OnData(self, data: Slice):
|
||||
if not data.HasData:
|
||||
return
|
||||
|
||||
self.onDataReached = True
|
||||
hasOptionQuoteBars = False
|
||||
|
||||
for qb in data.QuoteBars.Values:
|
||||
if qb.Symbol.SecurityType != SecurityType.FutureOption:
|
||||
continue
|
||||
|
||||
hasOptionQuoteBars = True
|
||||
|
||||
self.symbolsReceived.append(qb.Symbol)
|
||||
if qb.Symbol not in self.dataReceived:
|
||||
self.dataReceived[qb.Symbol] = []
|
||||
|
||||
self.dataReceived[qb.Symbol].append(qb)
|
||||
|
||||
if self.invested or not hasOptionQuoteBars:
|
||||
return
|
||||
|
||||
if data.ContainsKey(self.es20h20) and data.ContainsKey(self.es19m20):
|
||||
self.SetHoldings(self.es20h20, 0.2)
|
||||
self.SetHoldings(self.es19m20, 0.2)
|
||||
|
||||
self.invested = True
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
super().OnEndOfAlgorithm()
|
||||
|
||||
self.symbolsReceived = list(set(self.symbolsReceived))
|
||||
self.expectedSymbolsReceived = list(set(self.expectedSymbolsReceived))
|
||||
|
||||
if not self.onDataReached:
|
||||
raise AssertionError("OnData() was never called.")
|
||||
if len(self.symbolsReceived) != len(self.expectedSymbolsReceived):
|
||||
raise AssertionError(f"Expected {len(self.expectedSymbolsReceived)} option contracts Symbols, found {len(self.symbolsReceived)}")
|
||||
|
||||
missingSymbols = [expectedSymbol for expectedSymbol in self.expectedSymbolsReceived if expectedSymbol not in self.symbolsReceived]
|
||||
if any(missingSymbols):
|
||||
raise AssertionError(f'Symbols: "{", ".join(missingSymbols)}" were not found in OnData')
|
||||
|
||||
for expectedSymbol in self.expectedSymbolsReceived:
|
||||
data = self.dataReceived[expectedSymbol]
|
||||
for dataPoint in data:
|
||||
dataPoint.EndTime = datetime(1970, 1, 1)
|
||||
|
||||
nonDupeDataCount = len(set(data))
|
||||
if nonDupeDataCount < 1000:
|
||||
raise AssertionError(f"Received too few data points. Expected >=1000, found {nonDupeDataCount} for {expectedSymbol}")
|
||||
@@ -0,0 +1,127 @@
|
||||
# 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, timedelta
|
||||
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
from QuantConnect import *
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm tests that we only receive the option chain for a single future contract
|
||||
### in the option universe filter.
|
||||
### </summary>
|
||||
class AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
self.invested = False
|
||||
self.onDataReached = False
|
||||
self.optionFilterRan = False
|
||||
self.symbolsReceived = []
|
||||
self.expectedSymbolsReceived = []
|
||||
self.dataReceived = {}
|
||||
|
||||
self.SetStartDate(2020, 1, 5)
|
||||
self.SetEndDate(2020, 1, 6)
|
||||
|
||||
self.es = self.AddFuture(Futures.Indices.SP500EMini, Resolution.Minute, Market.CME)
|
||||
self.es.SetFilter(lambda futureFilter: futureFilter.Expiration(0, 365).ExpirationCycle([3, 6]))
|
||||
|
||||
self.AddFutureOption(self.es.Symbol, self.OptionContractUniverseFilterFunction)
|
||||
|
||||
def OptionContractUniverseFilterFunction(self, optionContracts: OptionFilterUniverse) -> OptionFilterUniverse:
|
||||
self.optionFilterRan = True
|
||||
|
||||
expiry = list(set([x.Underlying.ID.Date for x in optionContracts]))
|
||||
expiry = None if not any(expiry) else expiry[0]
|
||||
|
||||
symbol = [x.Underlying for x in optionContracts]
|
||||
symbol = None if not any(symbol) else symbol[0]
|
||||
|
||||
if expiry is None or symbol is None:
|
||||
raise AssertionError("Expected a single Option contract in the chain, found 0 contracts")
|
||||
|
||||
enumerator = optionContracts.GetEnumerator()
|
||||
while enumerator.MoveNext():
|
||||
self.expectedSymbolsReceived.append(enumerator.Current)
|
||||
|
||||
return optionContracts
|
||||
|
||||
def OnData(self, data: Slice):
|
||||
if not data.HasData:
|
||||
return
|
||||
|
||||
self.onDataReached = True
|
||||
hasOptionQuoteBars = False
|
||||
|
||||
for qb in data.QuoteBars.Values:
|
||||
if qb.Symbol.SecurityType != SecurityType.FutureOption:
|
||||
continue
|
||||
|
||||
hasOptionQuoteBars = True
|
||||
|
||||
self.symbolsReceived.append(qb.Symbol)
|
||||
if qb.Symbol not in self.dataReceived:
|
||||
self.dataReceived[qb.Symbol] = []
|
||||
|
||||
self.dataReceived[qb.Symbol].append(qb)
|
||||
|
||||
if self.invested or not hasOptionQuoteBars:
|
||||
return
|
||||
|
||||
for chain in data.OptionChains.Values:
|
||||
futureInvested = False
|
||||
optionInvested = False
|
||||
|
||||
for option in chain.Contracts.Keys:
|
||||
if futureInvested and optionInvested:
|
||||
return
|
||||
|
||||
future = option.Underlying
|
||||
|
||||
if not optionInvested and data.ContainsKey(option):
|
||||
self.MarketOrder(option, 1)
|
||||
self.invested = True
|
||||
optionInvested = True
|
||||
|
||||
if not futureInvested and data.ContainsKey(future):
|
||||
self.MarketOrder(future, 1)
|
||||
self.invested = True
|
||||
futureInvested = True
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
super().OnEndOfAlgorithm()
|
||||
self.symbolsReceived = list(set(self.symbolsReceived))
|
||||
self.expectedSymbolsReceived = list(set(self.expectedSymbolsReceived))
|
||||
|
||||
if not self.optionFilterRan:
|
||||
raise AssertionError("Option chain filter was never ran")
|
||||
if not self.onDataReached:
|
||||
raise AssertionError("OnData() was never called.")
|
||||
if len(self.symbolsReceived) != len(self.expectedSymbolsReceived):
|
||||
raise AssertionError(f"Expected {len(self.expectedSymbolsReceived)} option contracts Symbols, found {len(self.symbolsReceived)}")
|
||||
|
||||
missingSymbols = [expectedSymbol for expectedSymbol in self.expectedSymbolsReceived if expectedSymbol not in self.symbolsReceived]
|
||||
if any(missingSymbols):
|
||||
raise AssertionError(f'Symbols: "{", ".join(missingSymbols)}" were not found in OnData')
|
||||
|
||||
for expectedSymbol in self.expectedSymbolsReceived:
|
||||
data = self.dataReceived[expectedSymbol]
|
||||
for dataPoint in data:
|
||||
dataPoint.EndTime = datetime(1970, 1, 1)
|
||||
|
||||
nonDupeDataCount = len(set(data))
|
||||
if nonDupeDataCount < 1000:
|
||||
raise AssertionError(f"Received too few data points. Expected >=1000, found {nonDupeDataCount} for {expectedSymbol}")
|
||||
@@ -0,0 +1,67 @@
|
||||
# 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 clr import AddReference
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from datetime import *
|
||||
|
||||
### <summary>
|
||||
### We add an option contract using 'QCAlgorithm.AddOptionContract' and place a trade, the underlying
|
||||
### gets deselected from the universe selection but should still be present since we manually added the option contract.
|
||||
### Later we call 'QCAlgorithm.RemoveOptionContract' and expect both option and underlying to be removed.
|
||||
### </summary>
|
||||
class AddOptionContractExpiresRegressionAlgorithm(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.SetStartDate(2014, 6, 5)
|
||||
self.SetEndDate(2014, 6, 30)
|
||||
|
||||
self._expiration = datetime(2014, 6, 21)
|
||||
self._option = None
|
||||
self._traded = False
|
||||
|
||||
self._twx = Symbol.Create("TWX", SecurityType.Equity, Market.USA)
|
||||
|
||||
self.AddUniverse("my-daily-universe-name", self.Selector)
|
||||
|
||||
def Selector(self, time):
|
||||
return [ "AAPL" ]
|
||||
|
||||
def OnData(self, data):
|
||||
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
|
||||
Arguments:
|
||||
data: Slice object keyed by symbol containing the stock data
|
||||
'''
|
||||
if self._option == None:
|
||||
options = self.OptionChainProvider.GetOptionContractList(self._twx, self.Time)
|
||||
options = sorted(options, key=lambda x: x.ID.Symbol)
|
||||
|
||||
option = next((option for option in options if option.ID.Date == self._expiration and option.ID.OptionRight == OptionRight.Call and option.ID.OptionStyle == OptionStyle.American), None)
|
||||
if option != None:
|
||||
self._option = self.AddOptionContract(option).Symbol;
|
||||
|
||||
if self._option != None and self.Securities[self._option].Price != 0 and not self._traded:
|
||||
self._traded = True;
|
||||
self.Buy(self._option, 1);
|
||||
|
||||
if self.Time > self._expiration and self.Securities[self._twx].Invested:
|
||||
# we liquidate the option exercised position
|
||||
self.Liquidate(self._twx);
|
||||
@@ -0,0 +1,87 @@
|
||||
# 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 clr import AddReference
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from datetime import *
|
||||
|
||||
### <summary>
|
||||
### We add an option contract using 'QCAlgorithm.AddOptionContract' and place a trade, the underlying
|
||||
### gets deselected from the universe selection but should still be present since we manually added the option contract.
|
||||
### Later we call 'QCAlgorithm.RemoveOptionContract' and expect both option and underlying to be removed.
|
||||
### </summary>
|
||||
class AddOptionContractFromUniverseRegressionAlgorithm(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.SetStartDate(2014, 6, 5)
|
||||
self.SetEndDate(2014, 6, 9)
|
||||
|
||||
self._expiration = datetime(2014, 6, 21)
|
||||
self._securityChanges = None
|
||||
self._option = None
|
||||
self._traded = False
|
||||
|
||||
self._twx = Symbol.Create("TWX", SecurityType.Equity, Market.USA)
|
||||
self._aapl = Symbol.Create("AAPL", SecurityType.Equity, Market.USA)
|
||||
self.UniverseSettings.Resolution = Resolution.Minute
|
||||
self.UniverseSettings.DataNormalizationMode = DataNormalizationMode.Raw
|
||||
|
||||
self.AddUniverse(self.Selector, self.Selector)
|
||||
|
||||
def Selector(self, fundamental):
|
||||
if self.Time <= datetime(2014, 6, 5):
|
||||
return [ self._twx ]
|
||||
return [ self._aapl ]
|
||||
|
||||
def OnData(self, data):
|
||||
'''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
|
||||
|
||||
Arguments:
|
||||
data: Slice object keyed by symbol containing the stock data
|
||||
'''
|
||||
if self._option != None and self.Securities[self._option].Price != 0 and not self._traded:
|
||||
self._traded = True;
|
||||
self.Buy(self._option, 1);
|
||||
|
||||
if self.Time == datetime(2014, 6, 6, 14, 0, 0):
|
||||
# liquidate & remove the option
|
||||
self.RemoveOptionContract(self._option)
|
||||
|
||||
def OnSecuritiesChanged(self, changes):
|
||||
# keep track of all removed and added securities
|
||||
if self._securityChanges == None:
|
||||
self._securityChanges = changes
|
||||
else:
|
||||
self._securityChanges.op_Addition(self._securityChanges, changes)
|
||||
|
||||
if any(security.Symbol.SecurityType == SecurityType.Option for security in changes.AddedSecurities):
|
||||
return
|
||||
|
||||
for addedSecurity in changes.AddedSecurities:
|
||||
options = self.OptionChainProvider.GetOptionContractList(addedSecurity.Symbol, self.Time)
|
||||
options = sorted(options, key=lambda x: x.ID.Symbol)
|
||||
|
||||
option = next((option for option in options if option.ID.Date == self._expiration and option.ID.OptionRight == OptionRight.Call and option.ID.OptionStyle == OptionStyle.American), None)
|
||||
|
||||
self.AddOptionContract(option)
|
||||
|
||||
# just keep the first we got
|
||||
if self._option == None:
|
||||
self._option = option
|
||||
@@ -0,0 +1,50 @@
|
||||
# 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 clr import AddReference
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Custom.Quiver import *
|
||||
|
||||
### <summary>
|
||||
### Quiver Quantitative is a provider of alternative data.
|
||||
### This algorithm shows how to consume the 'QuiverWallStreetBets'
|
||||
### </summary>
|
||||
class QuiverWallStreetBetsDataAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2019, 1, 1)
|
||||
self.SetEndDate(2020, 6, 1)
|
||||
self.SetCash(100000)
|
||||
|
||||
aapl = self.AddEquity("AAPL", Resolution.Daily).Symbol
|
||||
quiverWSBSymbol = self.AddData(QuiverWallStreetBets, aapl).Symbol
|
||||
history = self.History(QuiverWallStreetBets, quiverWSBSymbol, 60, Resolution.Daily)
|
||||
|
||||
self.Debug(f"We got {len(history)} items from our history request");
|
||||
|
||||
def OnData(self, data):
|
||||
points = data.Get(QuiverWallStreetBets)
|
||||
for point in points.Values:
|
||||
# Go long in the stock if it was mentioned more than 5 times in the WallStreetBets daily discussion
|
||||
if point.Mentions > 5:
|
||||
self.SetHoldings(point.Symbol.Underlying, 1)
|
||||
|
||||
# Go short in the stock if it was mentioned less than 5 times in the WallStreetBets daily discussion
|
||||
if point.Mentions < 5:
|
||||
self.SetHoldings(point.Symbol.Underlying, -1)
|
||||
@@ -0,0 +1,99 @@
|
||||
# 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 clr import AddReference
|
||||
AddReference("System.Core")
|
||||
AddReference("System.Collections")
|
||||
AddReference("QuantConnect.Common")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data.UniverseSelection import *
|
||||
from datetime import *
|
||||
|
||||
### <summary>
|
||||
### Demonstration of how to chain a coarse and fine universe selection with an option chain universe selection model
|
||||
### that will add and remove an'OptionChainUniverse' for each symbol selected on fine
|
||||
### </summary>
|
||||
class CoarseFineOptionUniverseChainRegressionAlgorithm(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.SetStartDate(2014,6,5) #Set Start Date
|
||||
self.SetEndDate(2014,6,6) #Set End Date
|
||||
|
||||
self.UniverseSettings.Resolution = Resolution.Minute
|
||||
self._twx = Symbol.Create("TWX", SecurityType.Equity, Market.USA)
|
||||
self._aapl = Symbol.Create("AAPL", SecurityType.Equity, Market.USA)
|
||||
self._lastEquityAdded = None
|
||||
self._changes = None
|
||||
self._optionCount = 0
|
||||
|
||||
universe = self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction)
|
||||
|
||||
self.AddUniverseOptions(universe, self.OptionFilterFunction)
|
||||
|
||||
def OptionFilterFunction(self, universe):
|
||||
universe.IncludeWeeklys().FrontMonth()
|
||||
|
||||
contracts = list()
|
||||
for symbol in universe:
|
||||
if len(contracts) == 5:
|
||||
break
|
||||
contracts.append(symbol)
|
||||
return universe.Contracts(contracts)
|
||||
|
||||
def CoarseSelectionFunction(self, coarse):
|
||||
if self.Time <= datetime(2014,6,5):
|
||||
return [ self._twx ]
|
||||
return [ self._aapl ]
|
||||
|
||||
def FineSelectionFunction(self, fine):
|
||||
if self.Time <= datetime(2014,6,5):
|
||||
return [ self._twx ]
|
||||
return [ self._aapl ]
|
||||
|
||||
def OnData(self, data):
|
||||
if self._changes == None or any(security.Price == 0 for security in self._changes.AddedSecurities):
|
||||
return
|
||||
|
||||
# liquidate removed securities
|
||||
for security in self._changes.RemovedSecurities:
|
||||
if security.Invested:
|
||||
self.Liquidate(security.Symbol);
|
||||
|
||||
for security in self._changes.AddedSecurities:
|
||||
if not security.Symbol.HasUnderlying:
|
||||
self._lastEquityAdded = security.Symbol;
|
||||
else:
|
||||
# options added should all match prev added security
|
||||
if security.Symbol.Underlying != self._lastEquityAdded:
|
||||
raise ValueError(f"Unexpected symbol added {security.Symbol}")
|
||||
self._optionCount += 1
|
||||
|
||||
self.SetHoldings(security.Symbol, 0.05)
|
||||
self._changes = None
|
||||
|
||||
# this event fires whenever we have changes to our universe
|
||||
def OnSecuritiesChanged(self, changes):
|
||||
if self._changes == None:
|
||||
self._changes = changes
|
||||
return
|
||||
self._changes = self._changes.op_Addition(self._changes, changes)
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if self._optionCount == 0:
|
||||
raise ValueError("Option universe chain did not add any option!")
|
||||
@@ -22,7 +22,6 @@ from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Indicators import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Data.Consolidators import *
|
||||
from CustomDataRegressionAlgorithm import Bitcoin
|
||||
from datetime import timedelta
|
||||
|
||||
65
Algorithm.Python/CustomBuyingPowerModelAlgorithm.py
Normal file
65
Algorithm.Python/CustomBuyingPowerModelAlgorithm.py
Normal file
@@ -0,0 +1,65 @@
|
||||
# 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 clr import AddReference
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Securities import *
|
||||
import numpy as np
|
||||
|
||||
### <summary>
|
||||
### Demonstration of using custom buying power model in backtesting.
|
||||
### QuantConnect allows you to model all orders as deeply and accurately as you need.
|
||||
### </summary>
|
||||
### <meta name="tag" content="trading and orders" />
|
||||
### <meta name="tag" content="transaction fees and slippage" />
|
||||
### <meta name="tag" content="custom buying power models" />
|
||||
class CustomBuyingPowerModelAlgorithm(QCAlgorithm):
|
||||
'''Demonstration of using custom buying power model in backtesting.
|
||||
QuantConnect allows you to model all orders as deeply and accurately as you need.'''
|
||||
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2013,10,1) # Set Start Date
|
||||
self.SetEndDate(2013,10,31) # Set End Date
|
||||
security = self.AddEquity("SPY", Resolution.Hour)
|
||||
self.spy = security.Symbol
|
||||
|
||||
# set the buying power model
|
||||
security.SetBuyingPowerModel(CustomBuyingPowerModel())
|
||||
|
||||
def OnData(self, slice):
|
||||
if self.Portfolio.Invested:
|
||||
return
|
||||
|
||||
quantity = self.CalculateOrderQuantity(self.spy, 1)
|
||||
if quantity % 100 != 0:
|
||||
raise Exception(f'CustomBuyingPowerModel only allow quantity that is multiple of 100 and {quantity} was found')
|
||||
|
||||
# We normally get insufficient buying power model, but the
|
||||
# CustomBuyingPowerModel always says that there is sufficient buying power for the orders
|
||||
self.MarketOrder(self.spy, quantity * 10)
|
||||
|
||||
|
||||
class CustomBuyingPowerModel(BuyingPowerModel):
|
||||
def GetMaximumOrderQuantityForTargetBuyingPower(self, parameters):
|
||||
quantity = super().GetMaximumOrderQuantityForTargetBuyingPower(parameters).Quantity
|
||||
quantity = np.floor(quantity / 100) * 100
|
||||
return GetMaximumOrderQuantityResult(quantity)
|
||||
|
||||
def HasSufficientBuyingPowerForOrder(self, parameters):
|
||||
return HasSufficientBuyingPowerForOrderResult(True)
|
||||
@@ -13,19 +13,18 @@
|
||||
|
||||
from clr import AddReference
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Common")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Indicators")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Python import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Algorithm.Framework.Selection import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Data.Consolidators import *
|
||||
from QuantConnect.Indicators import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from System import *
|
||||
from datetime import *
|
||||
|
||||
class CustomConsolidatorRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
@@ -21,6 +21,7 @@ from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Algorithm.Framework.Selection import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Custom import *
|
||||
from QuantConnect.Data.Custom.SEC import *
|
||||
from QuantConnect.Data.UniverseSelection import *
|
||||
|
||||
|
||||
@@ -27,16 +27,17 @@ import numpy as np
|
||||
import random
|
||||
|
||||
### <summary>
|
||||
### Demonstration of using custom fee, slippage and fill models for modelling transactions in backtesting.
|
||||
### Demonstration of using custom fee, slippage, fill, and buying power models for modelling transactions in backtesting.
|
||||
### QuantConnect allows you to model all orders as deeply and accurately as you need.
|
||||
### </summary>
|
||||
### <meta name="tag" content="trading and orders" />
|
||||
### <meta name="tag" content="transaction fees and slippage" />
|
||||
### <meta name="tag" content="custom buying power models" />
|
||||
### <meta name="tag" content="custom transaction models" />
|
||||
### <meta name="tag" content="custom slippage models" />
|
||||
### <meta name="tag" content="custom fee models" />
|
||||
class CustomModelsAlgorithm(QCAlgorithm):
|
||||
'''Demonstration of using custom fee, slippage and fill models for modelling transactions in backtesting.
|
||||
'''Demonstration of using custom fee, slippage, fill, and buying power models for modelling transactions in backtesting.
|
||||
QuantConnect allows you to model all orders as deeply and accurately as you need.'''
|
||||
|
||||
def Initialize(self):
|
||||
@@ -49,6 +50,7 @@ class CustomModelsAlgorithm(QCAlgorithm):
|
||||
self.security.SetFeeModel(CustomFeeModel(self))
|
||||
self.security.SetFillModel(CustomFillModel(self))
|
||||
self.security.SetSlippageModel(CustomSlippageModel(self))
|
||||
self.security.SetBuyingPowerModel(CustomBuyingPowerModel(self))
|
||||
|
||||
|
||||
def OnData(self, data):
|
||||
@@ -57,12 +59,12 @@ class CustomModelsAlgorithm(QCAlgorithm):
|
||||
|
||||
if self.Time.day > 10 and self.security.Holdings.Quantity <= 0:
|
||||
quantity = self.CalculateOrderQuantity(self.spy, .5)
|
||||
self.Log("MarketOrder: " + str(quantity))
|
||||
self.Log(f"MarketOrder: {quantity}")
|
||||
self.MarketOrder(self.spy, quantity, True) # async needed for partial fill market orders
|
||||
|
||||
elif self.Time.day > 20 and self.security.Holdings.Quantity >= 0:
|
||||
quantity = self.CalculateOrderQuantity(self.spy, -.5)
|
||||
self.Log("MarketOrder: " + str(quantity))
|
||||
self.Log(f"MarketOrder: {quantity}")
|
||||
self.MarketOrder(self.spy, quantity, True) # async needed for partial fill market orders
|
||||
|
||||
# If we want to use methods from other models, you need to inherit from one of them
|
||||
@@ -90,7 +92,7 @@ class CustomFillModel(ImmediateFillModel):
|
||||
absoluteRemaining = absoluteRemaining - absoluteFillQuantity
|
||||
self.absoluteRemainingByOrderId[order.Id] = absoluteRemaining
|
||||
fill.Status = OrderStatus.PartiallyFilled
|
||||
self.algorithm.Log("CustomFillModel: " + str(fill))
|
||||
self.algorithm.Log(f"CustomFillModel: {fill}")
|
||||
return fill
|
||||
|
||||
class CustomFeeModel(FeeModel):
|
||||
@@ -102,7 +104,7 @@ class CustomFeeModel(FeeModel):
|
||||
fee = max(1, parameters.Security.Price
|
||||
* parameters.Order.AbsoluteQuantity
|
||||
* 0.00001)
|
||||
self.algorithm.Log("CustomFeeModel: " + str(fee))
|
||||
self.algorithm.Log(f"CustomFeeModel: {fee}")
|
||||
return OrderFee(CashAmount(fee, "USD"))
|
||||
|
||||
class CustomSlippageModel:
|
||||
@@ -112,5 +114,15 @@ class CustomSlippageModel:
|
||||
def GetSlippageApproximation(self, asset, order):
|
||||
# custom slippage math
|
||||
slippage = asset.Price * 0.0001 * np.log10(2*float(order.AbsoluteQuantity))
|
||||
self.algorithm.Log("CustomSlippageModel: " + str(slippage))
|
||||
return slippage
|
||||
self.algorithm.Log(f"CustomSlippageModel: {slippage}")
|
||||
return slippage
|
||||
|
||||
class CustomBuyingPowerModel(BuyingPowerModel):
|
||||
def __init__(self, algorithm):
|
||||
self.algorithm = algorithm
|
||||
|
||||
def HasSufficientBuyingPowerForOrder(self, parameters):
|
||||
# custom behavior: this model will assume that there is always enough buying power
|
||||
hasSufficientBuyingPowerForOrderResult = HasSufficientBuyingPowerForOrderResult(True)
|
||||
self.algorithm.Log(f"CustomBuyingPowerModel: {hasSufficientBuyingPowerForOrderResult.IsSufficient}")
|
||||
return hasSufficientBuyingPowerForOrderResult
|
||||
@@ -0,0 +1,97 @@
|
||||
# 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, timedelta
|
||||
|
||||
import clr
|
||||
from System import *
|
||||
from System.Reflection import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Orders import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
from QuantConnect import Market
|
||||
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm tests In The Money (ITM) future option calls across different strike prices.
|
||||
### We expect 6 orders from the algorithm, which are:
|
||||
###
|
||||
### * (1) Initial entry, buy ES Call Option (ES19M20 expiring ITM)
|
||||
### * (2) Initial entry, sell ES Call Option at different strike (ES20H20 expiring ITM)
|
||||
### * [2] Option assignment, opens a position in the underlying (ES20H20, Qty: -1)
|
||||
### * [2] Future contract liquidation, due to impending expiry
|
||||
### * [1] Option exercise, receive 1 ES19M20 future contract
|
||||
### * [1] Liquidate ES19M20 contract, due to expiry
|
||||
###
|
||||
### Additionally, we test delistings for future options and assert that our
|
||||
### portfolio holdings reflect the orders the algorithm has submitted.
|
||||
### </summary>
|
||||
class FutureOptionBuySellCallIntradayRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2020, 1, 5)
|
||||
self.SetEndDate(2020, 6, 30)
|
||||
|
||||
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
# which causes delisting events to never be processed, thus leading to options that might never
|
||||
# be exercised until the next data point arrives.
|
||||
self.AddEquity("AAPL", Resolution.Daily)
|
||||
|
||||
self.es20h20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
datetime(2020, 3, 20)
|
||||
),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
self.es19m20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
datetime(2020, 6, 19)
|
||||
),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.esOptions = [
|
||||
self.AddFutureOptionContract(i, Resolution.Minute).Symbol for i in (self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) + self.OptionChainProvider.GetOptionContractList(self.es20h20, self.Time)) if i.ID.StrikePrice == 3200.0 and i.ID.OptionRight == OptionRight.Call
|
||||
]
|
||||
|
||||
self.expectedContracts = [
|
||||
Symbol.CreateOption(self.es20h20, Market.CME, OptionStyle.American, OptionRight.Call, 3200.0, datetime(2020, 3, 20)),
|
||||
Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3200.0, datetime(2020, 6, 19))
|
||||
]
|
||||
|
||||
for esOption in self.esOptions:
|
||||
if esOption not in self.expectedContracts:
|
||||
raise AssertionError(f"Contract {esOption} was not found in the chain")
|
||||
|
||||
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduleCallbackBuy)
|
||||
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.Noon, self.ScheduleCallbackLiquidate)
|
||||
|
||||
def ScheduleCallbackBuy(self):
|
||||
self.MarketOrder(self.esOptions[0], 1)
|
||||
self.MarketOrder(self.esOptions[1], -1)
|
||||
|
||||
def ScheduleCallbackLiquidate(self):
|
||||
self.Liquidate()
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if self.Portfolio.Invested:
|
||||
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")
|
||||
|
||||
143
Algorithm.Python/FutureOptionCallITMExpiryRegressionAlgorithm.py
Normal file
143
Algorithm.Python/FutureOptionCallITMExpiryRegressionAlgorithm.py
Normal file
@@ -0,0 +1,143 @@
|
||||
# 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, timedelta
|
||||
|
||||
import clr
|
||||
from System import *
|
||||
from System.Reflection import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Orders import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
from QuantConnect import Market
|
||||
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm tests In The Money (ITM) future option expiry for calls.
|
||||
### We expect 3 orders from the algorithm, which are:
|
||||
###
|
||||
### * Initial entry, buy ES Call Option (expiring ITM)
|
||||
### * Option exercise, receiving ES future contracts
|
||||
### * Future contract liquidation, due to impending expiry
|
||||
###
|
||||
### Additionally, we test delistings for future options and assert that our
|
||||
### portfolio holdings reflect the orders the algorithm has submitted.
|
||||
### </summary>
|
||||
class FutureOptionCallITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2020, 1, 5)
|
||||
self.SetEndDate(2020, 6, 30)
|
||||
|
||||
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
# which causes delisting events to never be processed, thus leading to options that might never
|
||||
# be exercised until the next data point arrives.
|
||||
self.AddEquity("AAPL", Resolution.Daily)
|
||||
|
||||
self.es19m20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
datetime(2020, 6, 19)
|
||||
),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.esOption = self.AddFutureOptionContract(
|
||||
list(
|
||||
sorted([x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice <= 3200.0 and x.ID.OptionRight == OptionRight.Call], key=lambda x: x.ID.StrikePrice, reverse=True)
|
||||
)[0], Resolution.Minute).Symbol
|
||||
|
||||
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3200.0, datetime(2020, 6, 19))
|
||||
if self.esOption != self.expectedContract:
|
||||
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain")
|
||||
|
||||
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduleCallback)
|
||||
|
||||
def ScheduleCallback(self):
|
||||
self.MarketOrder(self.esOption, 1)
|
||||
|
||||
def OnData(self, data: Slice):
|
||||
# Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
# the expected time. These assertions detect bug #4872
|
||||
for delisting in data.Delistings.Values:
|
||||
if delisting.Type == DelistingType.Warning:
|
||||
if delisting.Time != datetime(2020, 6, 19):
|
||||
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}")
|
||||
elif delisting.Type == DelistingType.Delisted:
|
||||
if delisting.Time != datetime(2020, 6, 20):
|
||||
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}")
|
||||
|
||||
def OnOrderEvent(self, orderEvent: OrderEvent):
|
||||
if orderEvent.Status != OrderStatus.Filled:
|
||||
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return
|
||||
|
||||
if not self.Securities.ContainsKey(orderEvent.Symbol):
|
||||
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
|
||||
|
||||
security = self.Securities[orderEvent.Symbol]
|
||||
if security.Symbol == self.es19m20:
|
||||
self.AssertFutureOptionOrderExercise(orderEvent, security, self.Securities[self.expectedContract])
|
||||
elif security.Symbol == self.expectedContract:
|
||||
# Expected contract is ES19H21 Call Option expiring ITM @ 3250
|
||||
self.AssertFutureOptionContractOrder(orderEvent, security)
|
||||
else:
|
||||
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
|
||||
|
||||
self.Log(f"{self.Time} -- {orderEvent.Symbol} :: Price: {self.Securities[orderEvent.Symbol].Holdings.Price} Qty: {self.Securities[orderEvent.Symbol].Holdings.Quantity} Direction: {orderEvent.Direction} Msg: {orderEvent.Message}")
|
||||
|
||||
def AssertFutureOptionOrderExercise(self, orderEvent: OrderEvent, future: Security, optionContract: Security):
|
||||
# We expect the liquidation to occur on the day of the delisting (while the market is open),
|
||||
# but currently we liquidate at the next market open (AAPL open) which happens to be
|
||||
# at 9:30:00 Eastern Time. For unknown reasons, the delisting happens two minutes after the
|
||||
# market open.
|
||||
# Read more about the issue affecting this test here: https://github.com/QuantConnect/Lean/issues/4980
|
||||
expectedLiquidationTimeUtc = datetime(2020, 6, 22, 13, 32, 0)
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Sell and future.Holdings.Quantity != 0:
|
||||
# We expect the contract to have been liquidated immediately
|
||||
raise AssertionError(f"Did not liquidate existing holdings for Symbol {future.Symbol}")
|
||||
if orderEvent.Direction == OrderDirection.Sell and orderEvent.UtcTime.replace(tzinfo=None) != expectedLiquidationTimeUtc:
|
||||
raise AssertionError(f"Liquidated future contract, but not at the expected time. Expected: {expectedLiquidationTimeUtc} - found {orderEvent.UtcTime.replace(tzinfo=None)}");
|
||||
|
||||
# No way to detect option exercise orders or any other kind of special orders
|
||||
# other than matching strings, for now.
|
||||
if "Option Exercise" in orderEvent.Message:
|
||||
if orderEvent.FillPrice != 3200.0:
|
||||
raise AssertionError("Option did not exercise at expected strike price (3200)")
|
||||
|
||||
if future.Holdings.Quantity != 1:
|
||||
# Here, we expect to have some holdings in the underlying, but not in the future option anymore.
|
||||
raise AssertionError(f"Exercised option contract, but we have no holdings for Future {future.Symbol}")
|
||||
|
||||
if optionContract.Holdings.Quantity != 0:
|
||||
raise AssertionError(f"Exercised option contract, but we have holdings for Option contract {optionContract.Symbol}")
|
||||
|
||||
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
|
||||
if orderEvent.Direction == OrderDirection.Buy and option.Holdings.Quantity != 1:
|
||||
raise AssertionError(f"No holdings were created for option contract {option.Symbol}")
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != 0:
|
||||
raise AssertionError(f"Holdings were found after a filled option exercise")
|
||||
|
||||
if "Exercise" in orderEvent.Message and option.Holdings.Quantity != 0:
|
||||
raise AssertionError(f"Holdings were found after exercising option contract {option.Symbol}")
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if self.Portfolio.Invested:
|
||||
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")
|
||||
127
Algorithm.Python/FutureOptionCallOTMExpiryRegressionAlgorithm.py
Normal file
127
Algorithm.Python/FutureOptionCallOTMExpiryRegressionAlgorithm.py
Normal file
@@ -0,0 +1,127 @@
|
||||
# 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, timedelta
|
||||
|
||||
import clr
|
||||
from System import *
|
||||
from System.Reflection import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Orders import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
from QuantConnect import Market
|
||||
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm tests Out of The Money (OTM) future option expiry for calls.
|
||||
### We expect 1 order from the algorithm, which are:
|
||||
###
|
||||
### * Initial entry, buy ES Call Option (expiring OTM)
|
||||
### - contract expires worthless, not exercised, so never opened a position in the underlying
|
||||
###
|
||||
### Additionally, we test delistings for future options and assert that our
|
||||
### portfolio holdings reflect the orders the algorithm has submitted.
|
||||
### </summary>
|
||||
### <remarks>
|
||||
### Total Trades in regression algorithm should be 1, but expiration is counted as a trade.
|
||||
### See related issue: https://github.com/QuantConnect/Lean/issues/4854
|
||||
### </remarks>
|
||||
class FutureOptionCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2020, 1, 5)
|
||||
self.SetEndDate(2020, 6, 30)
|
||||
|
||||
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
# which causes delisting events to never be processed, thus leading to options that might never
|
||||
# be exercised until the next data point arrives.
|
||||
self.AddEquity("AAPL", Resolution.Daily)
|
||||
|
||||
self.es19m20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
datetime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.esOption = self.AddFutureOptionContract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice >= 3300.0 and x.ID.OptionRight == OptionRight.Call],
|
||||
key=lambda x: x.ID.StrikePrice
|
||||
)
|
||||
)[0], Resolution.Minute).Symbol
|
||||
|
||||
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3300.0, datetime(2020, 6, 19))
|
||||
if self.esOption != self.expectedContract:
|
||||
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
|
||||
|
||||
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
|
||||
|
||||
def ScheduledMarketOrder(self):
|
||||
self.MarketOrder(self.esOption, 1)
|
||||
|
||||
def OnData(self, data: Slice):
|
||||
# Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
# the expected time. These assertions detect bug #4872
|
||||
for delisting in data.Delistings.Values:
|
||||
if delisting.Type == DelistingType.Warning:
|
||||
if delisting.Time != datetime(2020, 6, 19):
|
||||
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
|
||||
if delisting.Type == DelistingType.Delisted:
|
||||
if delisting.Time != datetime(2020, 6, 20):
|
||||
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
|
||||
|
||||
|
||||
def OnOrderEvent(self, orderEvent: OrderEvent):
|
||||
if orderEvent.Status != OrderStatus.Filled:
|
||||
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return
|
||||
|
||||
if not self.Securities.ContainsKey(orderEvent.Symbol):
|
||||
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
|
||||
|
||||
security = self.Securities[orderEvent.Symbol]
|
||||
if security.Symbol == self.es19m20:
|
||||
raise AssertionError("Invalid state: did not expect a position for the underlying to be opened, since this contract expires OTM")
|
||||
|
||||
# Expected contract is ES19M20 Call Option expiring OTM @ 3300
|
||||
if (security.Symbol == self.expectedContract):
|
||||
self.AssertFutureOptionContractOrder(orderEvent, security)
|
||||
else:
|
||||
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
|
||||
|
||||
self.Log(f"{orderEvent}");
|
||||
|
||||
|
||||
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
|
||||
if orderEvent.Direction == OrderDirection.Buy and option.Holdings.Quantity != 1:
|
||||
raise AssertionError(f"No holdings were created for option contract {option.Symbol}");
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != 0:
|
||||
raise AssertionError("Holdings were found after a filled option exercise");
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Sell and "OTM" not in orderEvent.Message:
|
||||
raise AssertionError("Contract did not expire OTM");
|
||||
|
||||
if "Exercise" in orderEvent.Message:
|
||||
raise AssertionError("Exercised option, even though it expires OTM");
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if self.Portfolio.Invested:
|
||||
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")
|
||||
142
Algorithm.Python/FutureOptionPutITMExpiryRegressionAlgorithm.py
Normal file
142
Algorithm.Python/FutureOptionPutITMExpiryRegressionAlgorithm.py
Normal file
@@ -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
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import clr
|
||||
from System import *
|
||||
from System.Reflection import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Orders import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
from QuantConnect import Market
|
||||
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm tests In The Money (ITM) future option expiry for puts.
|
||||
### We expect 3 orders from the algorithm, which are:
|
||||
###
|
||||
### * Initial entry, buy ES Put Option (expiring ITM) (buy, qty 1)
|
||||
### * Option exercise, receiving short ES future contracts (sell, qty -1)
|
||||
### * Future contract liquidation, due to impending expiry (buy qty 1)
|
||||
###
|
||||
### Additionally, we test delistings for future options and assert that our
|
||||
### portfolio holdings reflect the orders the algorithm has submitted.
|
||||
### </summary>
|
||||
class FutureOptionPutITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2020, 1, 5)
|
||||
self.SetEndDate(2020, 6, 30)
|
||||
|
||||
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
# which causes delisting events to never be processed, thus leading to options that might never
|
||||
# be exercised until the next data point arrives.
|
||||
self.AddEquity("AAPL", Resolution.Daily)
|
||||
|
||||
self.es19m20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
datetime(2020, 6, 19)
|
||||
),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.esOption = self.AddFutureOptionContract(
|
||||
list(
|
||||
sorted([x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice >= 3300.0 and x.ID.OptionRight == OptionRight.Put], key=lambda x: x.ID.StrikePrice)
|
||||
)[0], Resolution.Minute).Symbol
|
||||
|
||||
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3300.0, datetime(2020, 6, 19))
|
||||
if self.esOption != self.expectedContract:
|
||||
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain")
|
||||
|
||||
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduleCallback)
|
||||
|
||||
def ScheduleCallback(self):
|
||||
self.MarketOrder(self.esOption, 1)
|
||||
|
||||
def OnData(self, data: Slice):
|
||||
# Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
# the expected time. These assertions detect bug #4872
|
||||
for delisting in data.Delistings.Values:
|
||||
if delisting.Type == DelistingType.Warning:
|
||||
if delisting.Time != datetime(2020, 6, 19):
|
||||
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}")
|
||||
elif delisting.Type == DelistingType.Delisted:
|
||||
if delisting.Time != datetime(2020, 6, 20):
|
||||
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}")
|
||||
|
||||
def OnOrderEvent(self, orderEvent: OrderEvent):
|
||||
if orderEvent.Status != OrderStatus.Filled:
|
||||
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return
|
||||
|
||||
if not self.Securities.ContainsKey(orderEvent.Symbol):
|
||||
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
|
||||
|
||||
security = self.Securities[orderEvent.Symbol]
|
||||
if security.Symbol == self.es19m20:
|
||||
self.AssertFutureOptionOrderExercise(orderEvent, security, self.Securities[self.expectedContract])
|
||||
elif security.Symbol == self.expectedContract:
|
||||
# Expected contract is ES19M20 Call Option expiring ITM @ 3250
|
||||
self.AssertFutureOptionContractOrder(orderEvent, security)
|
||||
else:
|
||||
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
|
||||
|
||||
self.Log(f"{self.Time} -- {orderEvent.Symbol} :: Price: {self.Securities[orderEvent.Symbol].Holdings.Price} Qty: {self.Securities[orderEvent.Symbol].Holdings.Quantity} Direction: {orderEvent.Direction} Msg: {orderEvent.Message}")
|
||||
|
||||
def AssertFutureOptionOrderExercise(self, orderEvent: OrderEvent, future: Security, optionContract: Security):
|
||||
# We expect the liquidation to occur on the day of the delisting (while the market is open),
|
||||
# but currently we liquidate at the next market open (AAPL open) which happens to be
|
||||
# at 9:30:00 Eastern Time. For unknown reasons, the delisting happens two minutes after the
|
||||
# market open.
|
||||
# Read more about the issue affecting this test here: https://github.com/QuantConnect/Lean/issues/4980
|
||||
expectedLiquidationTimeUtc = datetime(2020, 6, 22, 13, 32, 0)
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Buy and future.Holdings.Quantity != 0:
|
||||
# We expect the contract to have been liquidated immediately
|
||||
raise AssertionError(f"Did not liquidate existing holdings for Symbol {future.Symbol}")
|
||||
if orderEvent.Direction == OrderDirection.Buy and orderEvent.UtcTime.replace(tzinfo=None) != expectedLiquidationTimeUtc:
|
||||
raise AssertionError(f"Liquidated future contract, but not at the expected time. Expected: {expectedLiquidationTimeUtc} - found {orderEvent.UtcTime.replace(tzinfo=None)}");
|
||||
|
||||
# No way to detect option exercise orders or any other kind of special orders
|
||||
# other than matching strings, for now.
|
||||
if "Option Exercise" in orderEvent.Message:
|
||||
if orderEvent.FillPrice != 3300.0:
|
||||
raise AssertionError("Option did not exercise at expected strike price (3300)")
|
||||
|
||||
if future.Holdings.Quantity != -1:
|
||||
# Here, we expect to have some holdings in the underlying, but not in the future option anymore.
|
||||
raise AssertionError(f"Exercised option contract, but we have no holdings for Future {future.Symbol}")
|
||||
|
||||
if optionContract.Holdings.Quantity != 0:
|
||||
raise AssertionError(f"Exercised option contract, but we have holdings for Option contract {optionContract.Symbol}")
|
||||
|
||||
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
|
||||
if orderEvent.Direction == OrderDirection.Buy and option.Holdings.Quantity != 1:
|
||||
raise AssertionError(f"No holdings were created for option contract {option.Symbol}")
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != 0:
|
||||
raise AssertionError(f"Holdings were found after a filled option exercise")
|
||||
|
||||
if "Exercise" in orderEvent.Message and option.Holdings.Quantity != 0:
|
||||
raise AssertionError(f"Holdings were found after exercising option contract {option.Symbol}")
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if self.Portfolio.Invested:
|
||||
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")
|
||||
127
Algorithm.Python/FutureOptionPutOTMExpiryRegressionAlgorithm.py
Normal file
127
Algorithm.Python/FutureOptionPutOTMExpiryRegressionAlgorithm.py
Normal file
@@ -0,0 +1,127 @@
|
||||
# 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, timedelta
|
||||
|
||||
import clr
|
||||
from System import *
|
||||
from System.Reflection import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Orders import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
from QuantConnect import Market
|
||||
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm tests Out of The Money (OTM) future option expiry for puts.
|
||||
### We expect 1 order from the algorithm, which are:
|
||||
###
|
||||
### * Initial entry, buy ES Put Option (expiring OTM)
|
||||
### - contract expires worthless, not exercised, so never opened a position in the underlying
|
||||
###
|
||||
### Additionally, we test delistings for future options and assert that our
|
||||
### portfolio holdings reflect the orders the algorithm has submitted.
|
||||
### </summary>
|
||||
### <remarks>
|
||||
### Total Trades in regression algorithm should be 1, but expiration is counted as a trade.
|
||||
### </remarks>
|
||||
class FutureOptionPutOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2020, 1, 5)
|
||||
self.SetEndDate(2020, 6, 30)
|
||||
|
||||
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
# which causes delisting events to never be processed, thus leading to options that might never
|
||||
# be exercised until the next data point arrives.
|
||||
self.AddEquity("AAPL", Resolution.Daily)
|
||||
|
||||
self.es19m20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
datetime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.esOption = self.AddFutureOptionContract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice <= 3150.0 and x.ID.OptionRight == OptionRight.Put],
|
||||
key=lambda x: x.ID.StrikePrice,
|
||||
reverse=True
|
||||
)
|
||||
)[0], Resolution.Minute).Symbol
|
||||
|
||||
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3150.0, datetime(2020, 6, 19))
|
||||
if self.esOption != self.expectedContract:
|
||||
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
|
||||
|
||||
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
|
||||
|
||||
def ScheduledMarketOrder(self):
|
||||
self.MarketOrder(self.esOption, 1)
|
||||
|
||||
def OnData(self, data: Slice):
|
||||
# Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
# the expected time. These assertions detect bug #4872
|
||||
for delisting in data.Delistings.Values:
|
||||
if delisting.Type == DelistingType.Warning:
|
||||
if delisting.Time != datetime(2020, 6, 19):
|
||||
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
|
||||
if delisting.Type == DelistingType.Delisted:
|
||||
if delisting.Time != datetime(2020, 6, 20):
|
||||
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
|
||||
|
||||
|
||||
def OnOrderEvent(self, orderEvent: OrderEvent):
|
||||
if orderEvent.Status != OrderStatus.Filled:
|
||||
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return
|
||||
|
||||
if not self.Securities.ContainsKey(orderEvent.Symbol):
|
||||
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
|
||||
|
||||
security = self.Securities[orderEvent.Symbol]
|
||||
if security.Symbol == self.es19m20:
|
||||
raise AssertionError("Invalid state: did not expect a position for the underlying to be opened, since this contract expires OTM")
|
||||
|
||||
# Expected contract is ES19M20 Put Option expiring OTM @ 3200
|
||||
if (security.Symbol == self.expectedContract):
|
||||
self.AssertFutureOptionContractOrder(orderEvent, security)
|
||||
else:
|
||||
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
|
||||
|
||||
self.Log(f"{orderEvent}");
|
||||
|
||||
|
||||
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
|
||||
if orderEvent.Direction == OrderDirection.Buy and option.Holdings.Quantity != 1:
|
||||
raise AssertionError(f"No holdings were created for option contract {option.Symbol}");
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != 0:
|
||||
raise AssertionError("Holdings were found after a filled option exercise");
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Sell and "OTM" not in orderEvent.Message:
|
||||
raise AssertionError("Contract did not expire OTM");
|
||||
|
||||
if "Exercise" in orderEvent.Message:
|
||||
raise AssertionError("Exercised option, even though it expires OTM");
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if self.Portfolio.Invested:
|
||||
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")
|
||||
@@ -0,0 +1,132 @@
|
||||
# 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, timedelta
|
||||
|
||||
import clr
|
||||
from System import *
|
||||
from System.Reflection import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Orders import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
from QuantConnect import Market
|
||||
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm tests In The Money (ITM) future option expiry for short calls.
|
||||
### We expect 3 orders from the algorithm, which are:
|
||||
###
|
||||
### * Initial entry, sell ES Call Option (expiring ITM)
|
||||
### * Option assignment, sell 1 contract of the underlying (ES)
|
||||
### * Future contract expiry, liquidation (buy 1 ES future)
|
||||
###
|
||||
### Additionally, we test delistings for future options and assert that our
|
||||
### portfolio holdings reflect the orders the algorithm has submitted.
|
||||
### </summary>
|
||||
class FutureOptionShortCallITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2020, 1, 5)
|
||||
self.SetEndDate(2020, 6, 30)
|
||||
|
||||
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
# which causes delisting events to never be processed, thus leading to options that might never
|
||||
# be exercised until the next data point arrives.
|
||||
self.AddEquity("AAPL", Resolution.Daily)
|
||||
|
||||
self.es19m20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
datetime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.esOption = self.AddFutureOptionContract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice <= 3100.0 and x.ID.OptionRight == OptionRight.Call],
|
||||
key=lambda x: x.ID.StrikePrice,
|
||||
reverse=True
|
||||
)
|
||||
)[0], Resolution.Minute).Symbol
|
||||
|
||||
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3100.0, datetime(2020, 6, 19))
|
||||
if self.esOption != self.expectedContract:
|
||||
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
|
||||
|
||||
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
|
||||
|
||||
def ScheduledMarketOrder(self):
|
||||
self.MarketOrder(self.esOption, -1)
|
||||
|
||||
def OnData(self, data: Slice):
|
||||
# Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
# the expected time. These assertions detect bug #4872
|
||||
for delisting in data.Delistings.Values:
|
||||
if delisting.Type == DelistingType.Warning:
|
||||
if delisting.Time != datetime(2020, 6, 19):
|
||||
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
|
||||
if delisting.Type == DelistingType.Delisted:
|
||||
if delisting.Time != datetime(2020, 6, 20):
|
||||
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
|
||||
|
||||
|
||||
def OnOrderEvent(self, orderEvent: OrderEvent):
|
||||
if orderEvent.Status != OrderStatus.Filled:
|
||||
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return
|
||||
|
||||
if not self.Securities.ContainsKey(orderEvent.Symbol):
|
||||
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
|
||||
|
||||
security = self.Securities[orderEvent.Symbol]
|
||||
if security.Symbol == self.es19m20:
|
||||
self.AssertFutureOptionOrderExercise(orderEvent, security, self.Securities[self.expectedContract])
|
||||
|
||||
elif security.Symbol == self.expectedContract:
|
||||
self.AssertFutureOptionContractOrder(orderEvent, security)
|
||||
|
||||
else:
|
||||
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
|
||||
|
||||
self.Log(f"{orderEvent}");
|
||||
|
||||
def AssertFutureOptionOrderExercise(self, orderEvent: OrderEvent, future: Security, optionContract: Security):
|
||||
if "Assignment" in orderEvent.Message:
|
||||
if orderEvent.FillPrice != 3100.0:
|
||||
raise AssertionError("Option was not assigned at expected strike price (3100)")
|
||||
|
||||
if orderEvent.Direction != OrderDirection.Sell or future.Holdings.Quantity != -1:
|
||||
raise AssertionError(f"Expected Qty: -1 futures holdings for assigned future {future.Symbol}, found {future.Holdings.Quantity}")
|
||||
|
||||
return
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Buy and future.Holdings.Quantity != 0:
|
||||
# We buy back the underlying at expiration, so we expect a neutral position then
|
||||
raise AssertionError(f"Expected no holdings when liquidating future contract {future.Symbol}")
|
||||
|
||||
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
|
||||
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != -1:
|
||||
raise AssertionError(f"No holdings were created for option contract {option.Symbol}");
|
||||
|
||||
if orderEvent.IsAssignment and option.Holdings.Quantity != 0:
|
||||
raise AssertionError(f"Holdings were found after option contract was assigned: {option.Symbol}")
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if self.Portfolio.Invested:
|
||||
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")
|
||||
@@ -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
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
import clr
|
||||
from System import *
|
||||
from System.Reflection import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Orders import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
from QuantConnect import Market
|
||||
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm tests Out of The Money (OTM) future option expiry for short calls.
|
||||
### We expect 1 order from the algorithm, which are:
|
||||
###
|
||||
### * Initial entry, sell ES Call Option (expiring OTM)
|
||||
### - Profit the option premium, since the option was not assigned.
|
||||
###
|
||||
### Additionally, we test delistings for future options and assert that our
|
||||
### portfolio holdings reflect the orders the algorithm has submitted.
|
||||
### </summary>
|
||||
class FutureOptionShortCallOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2020, 1, 5)
|
||||
self.SetEndDate(2020, 6, 30)
|
||||
|
||||
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
# which causes delisting events to never be processed, thus leading to options that might never
|
||||
# be exercised until the next data point arrives.
|
||||
self.AddEquity("AAPL", Resolution.Daily)
|
||||
|
||||
self.es19m20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
datetime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.esOption = self.AddFutureOptionContract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice >= 3400.0 and x.ID.OptionRight == OptionRight.Call],
|
||||
key=lambda x: x.ID.StrikePrice
|
||||
)
|
||||
)[0], Resolution.Minute).Symbol
|
||||
|
||||
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Call, 3400.0, datetime(2020, 6, 19))
|
||||
if self.esOption != self.expectedContract:
|
||||
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
|
||||
|
||||
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
|
||||
|
||||
def ScheduledMarketOrder(self):
|
||||
self.MarketOrder(self.esOption, -1)
|
||||
|
||||
def OnData(self, data: Slice):
|
||||
# Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
# the expected time. These assertions detect bug #4872
|
||||
for delisting in data.Delistings.Values:
|
||||
if delisting.Type == DelistingType.Warning:
|
||||
if delisting.Time != datetime(2020, 6, 19):
|
||||
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
|
||||
if delisting.Type == DelistingType.Delisted:
|
||||
if delisting.Time != datetime(2020, 6, 20):
|
||||
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
|
||||
|
||||
|
||||
def OnOrderEvent(self, orderEvent: OrderEvent):
|
||||
if orderEvent.Status != OrderStatus.Filled:
|
||||
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return
|
||||
|
||||
if not self.Securities.ContainsKey(orderEvent.Symbol):
|
||||
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
|
||||
|
||||
security = self.Securities[orderEvent.Symbol]
|
||||
if security.Symbol == self.es19m20:
|
||||
raise AssertionError(f"Expected no order events for underlying Symbol {security.Symbol}")
|
||||
|
||||
if security.Symbol == self.expectedContract:
|
||||
self.AssertFutureOptionContractOrder(orderEvent, security)
|
||||
|
||||
else:
|
||||
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
|
||||
|
||||
self.Log(f"{orderEvent}");
|
||||
|
||||
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, optionContract: Security):
|
||||
if orderEvent.Direction == OrderDirection.Sell and optionContract.Holdings.Quantity != -1:
|
||||
raise AssertionError(f"No holdings were created for option contract {optionContract.Symbol}")
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Buy and optionContract.Holdings.Quantity != 0:
|
||||
raise AssertionError("Expected no options holdings after closing position")
|
||||
|
||||
if orderEvent.IsAssignment:
|
||||
raise AssertionError(f"Assignment was not expected for {orderEvent.Symbol}")
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if self.Portfolio.Invested:
|
||||
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")
|
||||
@@ -0,0 +1,132 @@
|
||||
# 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, timedelta
|
||||
|
||||
import clr
|
||||
from System import *
|
||||
from System.Reflection import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Orders import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
from QuantConnect import Market
|
||||
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm tests In The Money (ITM) future option expiry for short puts.
|
||||
### We expect 3 orders from the algorithm, which are:
|
||||
###
|
||||
### * Initial entry, sell ES Put Option (expiring ITM)
|
||||
### * Option assignment, buy 1 contract of the underlying (ES)
|
||||
### * Future contract expiry, liquidation (sell 1 ES future)
|
||||
###
|
||||
### Additionally, we test delistings for future options and assert that our
|
||||
### portfolio holdings reflect the orders the algorithm has submitted.
|
||||
### </summary>
|
||||
class FutureOptionShortPutITMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2020, 1, 5)
|
||||
self.SetEndDate(2020, 6, 30)
|
||||
|
||||
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
# which causes delisting events to never be processed, thus leading to options that might never
|
||||
# be exercised until the next data point arrives.
|
||||
self.AddEquity("AAPL", Resolution.Daily)
|
||||
|
||||
self.es19m20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
datetime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.esOption = self.AddFutureOptionContract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice <= 3400.0 and x.ID.OptionRight == OptionRight.Put],
|
||||
key=lambda x: x.ID.StrikePrice,
|
||||
reverse=True
|
||||
)
|
||||
)[0], Resolution.Minute).Symbol
|
||||
|
||||
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3400.0, datetime(2020, 6, 19))
|
||||
if self.esOption != self.expectedContract:
|
||||
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
|
||||
|
||||
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
|
||||
|
||||
def ScheduledMarketOrder(self):
|
||||
self.MarketOrder(self.esOption, -1)
|
||||
|
||||
def OnData(self, data: Slice):
|
||||
# Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
# the expected time. These assertions detect bug #4872
|
||||
for delisting in data.Delistings.Values:
|
||||
if delisting.Type == DelistingType.Warning:
|
||||
if delisting.Time != datetime(2020, 6, 19):
|
||||
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
|
||||
if delisting.Type == DelistingType.Delisted:
|
||||
if delisting.Time != datetime(2020, 6, 20):
|
||||
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
|
||||
|
||||
|
||||
def OnOrderEvent(self, orderEvent: OrderEvent):
|
||||
if orderEvent.Status != OrderStatus.Filled:
|
||||
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return
|
||||
|
||||
if not self.Securities.ContainsKey(orderEvent.Symbol):
|
||||
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
|
||||
|
||||
security = self.Securities[orderEvent.Symbol]
|
||||
if security.Symbol == self.es19m20:
|
||||
self.AssertFutureOptionOrderExercise(orderEvent, security, self.Securities[self.expectedContract])
|
||||
|
||||
elif security.Symbol == self.expectedContract:
|
||||
self.AssertFutureOptionContractOrder(orderEvent, security)
|
||||
|
||||
else:
|
||||
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
|
||||
|
||||
self.Log(f"{orderEvent}");
|
||||
|
||||
def AssertFutureOptionOrderExercise(self, orderEvent: OrderEvent, future: Security, optionContract: Security):
|
||||
if "Assignment" in orderEvent.Message:
|
||||
if orderEvent.FillPrice != 3400.0:
|
||||
raise AssertionError("Option was not assigned at expected strike price (3400)")
|
||||
|
||||
if orderEvent.Direction != OrderDirection.Buy or future.Holdings.Quantity != 1:
|
||||
raise AssertionError(f"Expected Qty: 1 futures holdings for assigned future {future.Symbol}, found {future.Holdings.Quantity}")
|
||||
|
||||
return
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Sell and future.Holdings.Quantity != 0:
|
||||
# We buy back the underlying at expiration, so we expect a neutral position then
|
||||
raise AssertionError(f"Expected no holdings when liquidating future contract {future.Symbol}")
|
||||
|
||||
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, option: Security):
|
||||
if orderEvent.Direction == OrderDirection.Sell and option.Holdings.Quantity != -1:
|
||||
raise AssertionError(f"No holdings were created for option contract {option.Symbol}");
|
||||
|
||||
if orderEvent.IsAssignment and option.Holdings.Quantity != 0:
|
||||
raise AssertionError(f"Holdings were found after option contract was assigned: {option.Symbol}")
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if self.Portfolio.Invested:
|
||||
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")
|
||||
@@ -0,0 +1,120 @@
|
||||
# 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, timedelta
|
||||
|
||||
import clr
|
||||
from System import *
|
||||
from System.Reflection import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Orders import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Future import *
|
||||
from QuantConnect import Market
|
||||
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm tests Out of The Money (OTM) future option expiry for short puts.
|
||||
### We expect 1 order from the algorithm, which are:
|
||||
###
|
||||
### * Initial entry, sell ES Put Option (expiring OTM)
|
||||
### - Profit the option premium, since the option was not assigned.
|
||||
###
|
||||
### Additionally, we test delistings for future options and assert that our
|
||||
### portfolio holdings reflect the orders the algorithm has submitted.
|
||||
### </summary>
|
||||
class FutureOptionShortPutOTMExpiryRegressionAlgorithm(QCAlgorithm):
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2020, 1, 5)
|
||||
self.SetEndDate(2020, 6, 30)
|
||||
|
||||
# We add AAPL as a temporary workaround for https://github.com/QuantConnect/Lean/issues/4872
|
||||
# which causes delisting events to never be processed, thus leading to options that might never
|
||||
# be exercised until the next data point arrives.
|
||||
self.AddEquity("AAPL", Resolution.Daily)
|
||||
|
||||
self.es19m20 = self.AddFutureContract(
|
||||
Symbol.CreateFuture(
|
||||
Futures.Indices.SP500EMini,
|
||||
Market.CME,
|
||||
datetime(2020, 6, 19)),
|
||||
Resolution.Minute).Symbol
|
||||
|
||||
# Select a future option expiring ITM, and adds it to the algorithm.
|
||||
self.esOption = self.AddFutureOptionContract(
|
||||
list(
|
||||
sorted(
|
||||
[x for x in self.OptionChainProvider.GetOptionContractList(self.es19m20, self.Time) if x.ID.StrikePrice <= 3000.0 and x.ID.OptionRight == OptionRight.Put],
|
||||
key=lambda x: x.ID.StrikePrice,
|
||||
reverse=True
|
||||
)
|
||||
)[0], Resolution.Minute).Symbol
|
||||
|
||||
self.expectedContract = Symbol.CreateOption(self.es19m20, Market.CME, OptionStyle.American, OptionRight.Put, 3000.0, datetime(2020, 6, 19))
|
||||
if self.esOption != self.expectedContract:
|
||||
raise AssertionError(f"Contract {self.expectedContract} was not found in the chain");
|
||||
|
||||
self.Schedule.On(self.DateRules.Tomorrow, self.TimeRules.AfterMarketOpen(self.es19m20, 1), self.ScheduledMarketOrder)
|
||||
|
||||
def ScheduledMarketOrder(self):
|
||||
self.MarketOrder(self.esOption, -1)
|
||||
|
||||
def OnData(self, data: Slice):
|
||||
# Assert delistings, so that we can make sure that we receive the delisting warnings at
|
||||
# the expected time. These assertions detect bug #4872
|
||||
for delisting in data.Delistings.Values:
|
||||
if delisting.Type == DelistingType.Warning:
|
||||
if delisting.Time != datetime(2020, 6, 19):
|
||||
raise AssertionError(f"Delisting warning issued at unexpected date: {delisting.Time}");
|
||||
|
||||
if delisting.Type == DelistingType.Delisted:
|
||||
if delisting.Time != datetime(2020, 6, 20):
|
||||
raise AssertionError(f"Delisting happened at unexpected date: {delisting.Time}");
|
||||
|
||||
|
||||
def OnOrderEvent(self, orderEvent: OrderEvent):
|
||||
if orderEvent.Status != OrderStatus.Filled:
|
||||
# There's lots of noise with OnOrderEvent, but we're only interested in fills.
|
||||
return
|
||||
|
||||
if not self.Securities.ContainsKey(orderEvent.Symbol):
|
||||
raise AssertionError(f"Order event Symbol not found in Securities collection: {orderEvent.Symbol}")
|
||||
|
||||
security = self.Securities[orderEvent.Symbol]
|
||||
if security.Symbol == self.es19m20:
|
||||
raise AssertionError(f"Expected no order events for underlying Symbol {security.Symbol}")
|
||||
|
||||
if security.Symbol == self.expectedContract:
|
||||
self.AssertFutureOptionContractOrder(orderEvent, security)
|
||||
|
||||
else:
|
||||
raise AssertionError(f"Received order event for unknown Symbol: {orderEvent.Symbol}")
|
||||
|
||||
self.Log(f"{orderEvent}");
|
||||
|
||||
def AssertFutureOptionContractOrder(self, orderEvent: OrderEvent, optionContract: Security):
|
||||
if orderEvent.Direction == OrderDirection.Sell and optionContract.Holdings.Quantity != -1:
|
||||
raise AssertionError(f"No holdings were created for option contract {optionContract.Symbol}")
|
||||
|
||||
if orderEvent.Direction == OrderDirection.Buy and optionContract.Holdings.Quantity != 0:
|
||||
raise AssertionError("Expected no options holdings after closing position")
|
||||
|
||||
if orderEvent.IsAssignment:
|
||||
raise AssertionError(f"Assignment was not expected for {orderEvent.Symbol}")
|
||||
|
||||
def OnEndOfAlgorithm(self):
|
||||
if self.Portfolio.Invested:
|
||||
raise AssertionError(f"Expected no holdings at end of algorithm, but are invested in: {', '.join([str(i.ID) for i in self.Portfolio.Keys])}")
|
||||
@@ -12,15 +12,15 @@
|
||||
# limitations under the License.
|
||||
|
||||
from clr import AddReference
|
||||
AddReference("System.Core")
|
||||
AddReference("QuantConnect.Common")
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Algorithm.Framework")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import QCAlgorithm
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Algorithm.Framework.Selection import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.UniverseSelection import *
|
||||
from datetime import timedelta
|
||||
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
from clr import AddReference
|
||||
AddReference("System")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Algorithm.Framework")
|
||||
AddReference("QuantConnect.Common")
|
||||
|
||||
from QuantConnect import *
|
||||
@@ -24,7 +23,6 @@ from QuantConnect.Algorithm.Framework.Execution import *
|
||||
from QuantConnect.Algorithm.Framework.Portfolio import *
|
||||
from QuantConnect.Algorithm.Framework.Selection import *
|
||||
from QuantConnect.Brokerages import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Interfaces import *
|
||||
from QuantConnect.Orders import *
|
||||
from System import *
|
||||
|
||||
@@ -46,6 +46,10 @@
|
||||
<ItemGroup>
|
||||
<Content Include="AccumulativeInsightPortfolioRegressionAlgorithm.py" />
|
||||
<Content Include="AddAlphaModelAlgorithm.py" />
|
||||
<Content Include="AddFutureOptionContractDataStreamingRegressionAlgorithm.py" />
|
||||
<Content Include="AddFutureOptionSingleOptionChainSelectedInUniverseFilterRegressionAlgorithm.py" />
|
||||
<Content Include="AddOptionContractExpiresRegressionAlgorithm.py" />
|
||||
<Content Include="AddOptionContractFromUniverseRegressionAlgorithm.py" />
|
||||
<Content Include="AddRiskManagementAlgorithm.py" />
|
||||
<Content Include="AddUniverseSelectionModelAlgorithm.py" />
|
||||
<Content Include="Alphas\ContingentClaimsAnalysisDefaultPredictionAlpha.py" />
|
||||
@@ -63,6 +67,7 @@
|
||||
<Content Include="Alphas\VIXDualThrustAlpha.py" />
|
||||
<Content Include="AltData\CachedAlternativeDataAlgorithm.py" />
|
||||
<Content Include="AltData\BenzingaNewsAlgorithm.py" />
|
||||
<Content Include="AltData\QuiverWallStreetBetsDataAlgorithm.py" />
|
||||
<Content Include="AltData\SECReport8KAlgorithm.py" />
|
||||
<Content Include="AltData\SmartInsiderTransactionAlgorithm.py" />
|
||||
<Content Include="AltData\USTreasuryYieldCurveRateAlgorithm.py" />
|
||||
@@ -83,16 +88,27 @@
|
||||
<None Include="BasicTemplateOptionsPriceModel.py" />
|
||||
<Content Include="Benchmarks\SECReportBenchmarkAlgorithm.py" />
|
||||
<Content Include="Benchmarks\SmartInsiderEventBenchmarkAlgorithm.py" />
|
||||
<Content Include="CoarseFineOptionUniverseChainRegressionAlgorithm.py" />
|
||||
<Content Include="CoarseTiingoNewsUniverseSelectionAlgorithm.py" />
|
||||
<Content Include="ConsolidateRegressionAlgorithm.py" />
|
||||
<Content Include="CustomConsolidatorRegressionAlgorithm.py" />
|
||||
<Content Include="CustomDataAddDataOnSecuritiesChangedRegressionAlgorithm.py" />
|
||||
<Content Include="CustomDataAddDataCoarseSelectionRegressionAlgorithm.py" />
|
||||
<None Include="CustomBuyingPowerModelAlgorithm.py" />
|
||||
<Content Include="DynamicSecurityDataAlgorithm.py" />
|
||||
<Content Include="ConfidenceWeightedFrameworkAlgorithm.py" />
|
||||
<Content Include="ExtendedMarketTradingRegressionAlgorithm.py" />
|
||||
<Content Include="FilterUniverseRegressionAlgorithm.py" />
|
||||
<Content Include="FineFundamentalFilteredUniverseRegressionAlgorithm.py" />
|
||||
<Content Include="FutureOptionBuySellCallIntradayRegressionAlgorithm.py" />
|
||||
<Content Include="FutureOptionCallITMExpiryRegressionAlgorithm.py" />
|
||||
<Content Include="FutureOptionCallOTMExpiryRegressionAlgorithm.py" />
|
||||
<Content Include="FutureOptionPutITMExpiryRegressionAlgorithm.py" />
|
||||
<Content Include="FutureOptionPutOTMExpiryRegressionAlgorithm.py" />
|
||||
<Content Include="FutureOptionShortCallITMExpiryRegressionAlgorithm.py" />
|
||||
<Content Include="FutureOptionShortCallOTMExpiryRegressionAlgorithm.py" />
|
||||
<Content Include="FutureOptionShortPutITMExpiryRegressionAlgorithm.py" />
|
||||
<Content Include="FutureOptionShortPutOTMExpiryRegressionAlgorithm.py" />
|
||||
<Content Include="KerasNeuralNetworkAlgorithm.py" />
|
||||
<Content Include="CustomDataUsingMapFileRegressionAlgorithm.py" />
|
||||
<Content Include="LiquidETFUniverseFrameworkAlgorithm.py" />
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
<ProjectHome>.</ProjectHome>
|
||||
<StartupFile>
|
||||
</StartupFile>
|
||||
<SearchPath>stubs</SearchPath>
|
||||
<SearchPath>
|
||||
</SearchPath>
|
||||
<WorkingDirectory>.</WorkingDirectory>
|
||||
<OutputPath>.</OutputPath>
|
||||
<Name>QuantConnect.Algorithm.PythonTools</Name>
|
||||
@@ -45,6 +46,7 @@
|
||||
<Compile Include="ConstituentsQC500GeneratorAlgorithm.py" />
|
||||
<Compile Include="ConstituentsUniverseRegressionAlgorithm.py" />
|
||||
<Compile Include="ConvertToFrameworkAlgorithm.py" />
|
||||
<Compile Include="CustomBuyingPowerModelAlgorithm.py" />
|
||||
<Compile Include="CustomDataAddDataCoarseSelectionRegressionAlgorithm.py" />
|
||||
<Compile Include="CustomDataAddDataOnSecuritiesChangedRegressionAlgorithm.py" />
|
||||
<Compile Include="CustomDataAddDataRegressionAlgorithm.py" />
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
QuantConnect Python Algorithm Project:
|
||||
=============
|
||||
|
||||
## Local Python Autocomplete
|
||||
To enable autocomplete for your local Python IDE, install the `quantconnect-stubs` package from PyPI using the following command:
|
||||
```
|
||||
pip install quantconnect-stubs
|
||||
```
|
||||
|
||||
To update your autocomplete to the latest version, you can run the following command:
|
||||
```
|
||||
pip install --upgrade quantconnect-stubs
|
||||
```
|
||||
|
||||
Copy and paste the imports found [here](#python-autocomplete-imports) to the top of your project file to enable autocomplete.
|
||||
|
||||
In addition, you can use [Skylight](https://www.quantconnect.com/skylight) to automatically sync local changes to the cloud.
|
||||
|
||||
------
|
||||
|
||||
## Running LEAN Locally with Python
|
||||
Before we enable python support, follow the [installation instructions](https://github.com/QuantConnect/Lean#installation-instructions) to get LEAN running C# algorithms in your machine.
|
||||
|
||||
### Install Python 3.6:
|
||||
@@ -15,25 +33,25 @@ Before we enable python support, follow the [installation instructions](https://
|
||||
- Value of the variable: python installation path.
|
||||
4. Install [pandas=0.25.3](https://pandas.pydata.org/) and its [dependencies](https://pandas.pydata.org/pandas-docs/stable/install.html#dependencies).
|
||||
5. Install [wrapt=1.11.2](https://pypi.org/project/wrapt/) module.
|
||||
6. Reboot computer to ensure changes are propogated.
|
||||
6. Install [pyarrow=1.0.1](https://arrow.apache.org/install/) module.
|
||||
7. Reboot computer to ensure changes are propagated.
|
||||
|
||||
#### [macOS](https://github.com/QuantConnect/Lean#macos)
|
||||
|
||||
1. Use the macOS x86-64 package installer from [Anaconda](https://repo.anaconda.com/archive/Anaconda3-5.2.0-MacOSX-x86_64.pkg) and follow "[Installing on macOS](https://docs.anaconda.com/anaconda/install/mac-os)" instructions from Anaconda documentation page.
|
||||
2. Install [pandas=0.25.3](https://pandas.pydata.org/) and its [dependencies](https://pandas.pydata.org/pandas-docs/stable/install.html#dependencies).
|
||||
3. Install [wrapt=1.11.2](https://pypi.org/project/wrapt/) module.
|
||||
4. Install [pyarrow=1.0.1](https://arrow.apache.org/install/) module.
|
||||
|
||||
*Note:* If you encounter the "System.DllNotFoundException: python3.6m" runtime error when running Python algorithms on macOS:
|
||||
1. Find `libpython3.6m.dylib` in your Python installation folder. If you installed Python with Anaconda, it may be find at
|
||||
```
|
||||
/Users/{your_user_name}/anaconda3/lib/libpython3.6m.dylib
|
||||
```
|
||||
2. Open `Lean/Launcher/bin/Debug/Python.Runtime.dll.config`, add the following text and save:
|
||||
```
|
||||
<configuration>
|
||||
<dllmap dll="python3.6m" target="{the path in step 1 including libpython3.6m.dylib}" os="!windows"/>
|
||||
</configuration>
|
||||
```
|
||||
1. Find `libpython3.6m.dylib` in your Python installation folder. If you installed Python with Anaconda, it may be found at
|
||||
```
|
||||
/Users/{your_user_name}/anaconda3/lib/libpython3.6m.dylib
|
||||
```
|
||||
2. Open `Lean/Launcher/bin/Debug/Python.Runtime.dll.config`, add the following text under `<configuration> ... </configuration>` and save:
|
||||
```
|
||||
<dllmap dll="python3.6m" target="{the path in step 1 including libpython3.6m.dylib}" os="osx"/>
|
||||
```
|
||||
Note: Specify the install of v3.6.8 _exactly_, i.e. if with conda `conda install python=3.6.8` as this is a known compatible version and other versions may have issues as of this writing.
|
||||
|
||||
#### [Linux](https://github.com/QuantConnect/Lean#linux-debian-ubuntu)
|
||||
@@ -48,25 +66,41 @@ conda update -y python conda pip
|
||||
conda install -y cython=0.29.11
|
||||
conda install -y pandas=0.25.3
|
||||
conda install -y wrapt=1.11.2
|
||||
pip install pyarrow==1.0.1
|
||||
```
|
||||
|
||||
*Note:* There is a [known issue](https://github.com/pythonnet/pythonnet/issues/609) with python 3.6.5 that prevents pythonnet installation, please upgrade python to version 3.6.8:
|
||||
```
|
||||
conda install -y python=3.6.8
|
||||
```
|
||||
*Note 1:* There is a [known issue](https://github.com/pythonnet/pythonnet/issues/609) with python 3.6.5 that prevents pythonnet installation, please upgrade python to version 3.6.8:
|
||||
```
|
||||
conda install -y python=3.6.8
|
||||
```
|
||||
|
||||
*Note 2:* If you encounter the "System.DllNotFoundException: python3.6m" runtime error when running Python algorithms on Linux:
|
||||
1. Find `libpython3.6m.so` in your Python installation folder. If you installed Python with Miniconda, it may be found at
|
||||
```
|
||||
/home/{your_user_name}/miniconda3/envs/{qc_environment}/lib/libpython3.6m.so
|
||||
```
|
||||
Note that you can create a new virtual environment with all required dependencies by executing:
|
||||
```
|
||||
conda create -n qc_environment python=3.6.8 cython=0.29.11 pandas=0.25.3 wrapt=1.11.2
|
||||
|
||||
```
|
||||
2. Open `Lean/Launcher/bin/Debug/Python.Runtime.dll.config`, add the following text under `<configuration> ... </configuration>` and save:
|
||||
```
|
||||
<dllmap dll="python3.6m" target="{the path in step 1 including libpython3.6m.so}" os="linux"/>
|
||||
```
|
||||
### Run python algorithm
|
||||
1. Update the [config](https://github.com/QuantConnect/Lean/blob/master/Launcher/config.json) to run the python algorithm:
|
||||
```json
|
||||
"algorithm-type-name": "BasicTemplateAlgorithm",
|
||||
"algorithm-language": "Python",
|
||||
"algorithm-location": "../../../Algorithm.Python/BasicTemplateAlgorithm.py",
|
||||
```
|
||||
```json
|
||||
"algorithm-type-name": "BasicTemplateAlgorithm",
|
||||
"algorithm-language": "Python",
|
||||
"algorithm-location": "../../../Algorithm.Python/BasicTemplateAlgorithm.py",
|
||||
```
|
||||
2. Rebuild LEAN.
|
||||
3. Run LEAN. You should see the same result of the C# algorithm you tested earlier.
|
||||
|
||||
___
|
||||
#### Python.Runtime.dll compilation
|
||||
|
||||
### Python.NET development - Python.Runtime.dll compilation
|
||||
LEAN users do **not** need to compile `Python.Runtime.dll`. The information below is targeted to developers who wish to improve it.
|
||||
|
||||
Download [QuantConnect/pythonnet](https://github.com/QuantConnect/pythonnet/) github clone or downloading the zip. If downloading the zip - unzip to a local pathway.
|
||||
@@ -87,3 +121,45 @@ msbuild pythonnet.sln /nologo /v:quiet /t:Clean;Rebuild /p:Platform=x64 /p:Pytho
|
||||
```
|
||||
msbuild pythonnet.sln /nologo /v:quiet /t:Clean;Rebuild /p:Platform=x64 /p:PythonInteropFile="interop36m.cs" /p:Configuration=ReleaseMono /p:DefineConstants="PYTHON36,PYTHON3,UCS4,MONO_LINUX,PYTHON_WITH_PYMALLOC"
|
||||
```
|
||||
|
||||
# Python Autocomplete Imports
|
||||
Copy and paste these imports to the top of your Python file to enable a development experience equal to the cloud (these imports are exactly the same as the ones used in the QuantConnect Terminal).
|
||||
|
||||
```python
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Parameters import *
|
||||
from QuantConnect.Benchmarks import *
|
||||
from QuantConnect.Brokerages import *
|
||||
from QuantConnect.Util import *
|
||||
from QuantConnect.Interfaces import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Algorithm.Framework import *
|
||||
from QuantConnect.Algorithm.Framework.Selection import *
|
||||
from QuantConnect.Algorithm.Framework.Alphas import *
|
||||
from QuantConnect.Algorithm.Framework.Portfolio import *
|
||||
from QuantConnect.Algorithm.Framework.Execution import *
|
||||
from QuantConnect.Algorithm.Framework.Risk import *
|
||||
from QuantConnect.Indicators import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Consolidators import *
|
||||
from QuantConnect.Data.Custom import *
|
||||
from QuantConnect.Data.Fundamental import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Data.UniverseSelection import *
|
||||
from QuantConnect.Notifications import *
|
||||
from QuantConnect.Orders import *
|
||||
from QuantConnect.Orders.Fees import *
|
||||
from QuantConnect.Orders.Fills import *
|
||||
from QuantConnect.Orders.Slippage import *
|
||||
from QuantConnect.Scheduling import *
|
||||
from QuantConnect.Securities import *
|
||||
from QuantConnect.Securities.Equity import *
|
||||
from QuantConnect.Securities.Forex import *
|
||||
from QuantConnect.Securities.Interfaces import *
|
||||
from datetime import date, datetime, timedelta
|
||||
from QuantConnect.Python import *
|
||||
from QuantConnect.Storage import *
|
||||
QCAlgorithmFramework = QCAlgorithm
|
||||
QCAlgorithmFrameworkBridge = QCAlgorithm
|
||||
```
|
||||
|
||||
|
||||
@@ -1,402 +0,0 @@
|
||||
# encoding: utf-8
|
||||
# module QuantConnect.API calls itself API
|
||||
# from QuantConnect.Common, Version=2.4.0.0, Culture=neutral, PublicKeyToken=null
|
||||
# by generator 1.145
|
||||
# no doc
|
||||
|
||||
# imports
|
||||
import datetime
|
||||
import Newtonsoft.Json
|
||||
import Newtonsoft.Json.Linq
|
||||
import QuantConnect
|
||||
import QuantConnect.API
|
||||
import QuantConnect.Packets
|
||||
import System
|
||||
import typing
|
||||
|
||||
# no functions
|
||||
# classes
|
||||
|
||||
class BaseLiveAlgorithmSettings(System.object):
|
||||
"""
|
||||
Base class for settings that must be configured per Brokerage to create new algorithms via the API.
|
||||
|
||||
BaseLiveAlgorithmSettings(user: str, password: str, environment: BrokerageEnvironment, account: str)
|
||||
BaseLiveAlgorithmSettings(user: str, password: str)
|
||||
BaseLiveAlgorithmSettings(environment: BrokerageEnvironment, account: str)
|
||||
BaseLiveAlgorithmSettings(account: str)
|
||||
"""
|
||||
@typing.overload
|
||||
def __init__(self, user: str, password: str, environment: QuantConnect.BrokerageEnvironment, account: str) -> QuantConnect.API.BaseLiveAlgorithmSettings:
|
||||
pass
|
||||
|
||||
@typing.overload
|
||||
def __init__(self, user: str, password: str) -> QuantConnect.API.BaseLiveAlgorithmSettings:
|
||||
pass
|
||||
|
||||
@typing.overload
|
||||
def __init__(self, environment: QuantConnect.BrokerageEnvironment, account: str) -> QuantConnect.API.BaseLiveAlgorithmSettings:
|
||||
pass
|
||||
|
||||
@typing.overload
|
||||
def __init__(self, account: str) -> QuantConnect.API.BaseLiveAlgorithmSettings:
|
||||
pass
|
||||
|
||||
def __init__(self, *args) -> QuantConnect.API.BaseLiveAlgorithmSettings:
|
||||
pass
|
||||
|
||||
Account: str
|
||||
|
||||
Environment: QuantConnect.BrokerageEnvironment
|
||||
|
||||
Id: str
|
||||
|
||||
Password: str
|
||||
|
||||
User: str
|
||||
|
||||
|
||||
|
||||
class CreatedNode(QuantConnect.Api.RestResponse):
|
||||
"""
|
||||
Rest api response wrapper for node/create, reads in the nodes information into a
|
||||
node object
|
||||
|
||||
CreatedNode()
|
||||
"""
|
||||
Node: QuantConnect.API.Node
|
||||
|
||||
|
||||
|
||||
class DefaultLiveAlgorithmSettings(QuantConnect.API.BaseLiveAlgorithmSettings):
|
||||
"""
|
||||
Default live algorithm settings
|
||||
|
||||
DefaultLiveAlgorithmSettings(user: str, password: str, environment: BrokerageEnvironment, account: str)
|
||||
"""
|
||||
def __init__(self, user: str, password: str, environment: QuantConnect.BrokerageEnvironment, account: str) -> QuantConnect.API.DefaultLiveAlgorithmSettings:
|
||||
pass
|
||||
|
||||
|
||||
class Dividend(System.object):
|
||||
"""
|
||||
Dividend returned from the api
|
||||
|
||||
Dividend()
|
||||
"""
|
||||
Date: datetime.datetime
|
||||
|
||||
DividendPerShare: float
|
||||
|
||||
ReferencePrice: float
|
||||
|
||||
Symbol: QuantConnect.Symbol
|
||||
|
||||
SymbolID: str
|
||||
|
||||
|
||||
|
||||
class DividendList(QuantConnect.Api.RestResponse):
|
||||
"""
|
||||
Collection container for a list of dividend objects
|
||||
|
||||
DividendList()
|
||||
"""
|
||||
Dividends: typing.List[QuantConnect.API.Dividend]
|
||||
|
||||
|
||||
|
||||
class FXCMLiveAlgorithmSettings(QuantConnect.API.BaseLiveAlgorithmSettings):
|
||||
"""
|
||||
Algorithm setting for trading with FXCM
|
||||
|
||||
FXCMLiveAlgorithmSettings(user: str, password: str, environment: BrokerageEnvironment, account: str)
|
||||
"""
|
||||
def __init__(self, user: str, password: str, environment: QuantConnect.BrokerageEnvironment, account: str) -> QuantConnect.API.FXCMLiveAlgorithmSettings:
|
||||
pass
|
||||
|
||||
|
||||
class InteractiveBrokersLiveAlgorithmSettings(QuantConnect.API.BaseLiveAlgorithmSettings):
|
||||
"""
|
||||
Live algorithm settings for trading with Interactive Brokers
|
||||
|
||||
InteractiveBrokersLiveAlgorithmSettings(user: str, password: str, account: str)
|
||||
"""
|
||||
def __init__(self, user: str, password: str, account: str) -> QuantConnect.API.InteractiveBrokersLiveAlgorithmSettings:
|
||||
pass
|
||||
|
||||
|
||||
class LiveAlgorithm(QuantConnect.Api.RestResponse):
|
||||
"""
|
||||
Live algorithm instance result from the QuantConnect Rest API.
|
||||
|
||||
LiveAlgorithm()
|
||||
"""
|
||||
Brokerage: str
|
||||
DeployId: str
|
||||
Error: str
|
||||
Launched: datetime.datetime
|
||||
ProjectId: int
|
||||
Status: QuantConnect.AlgorithmStatus
|
||||
Stopped: typing.Optional[datetime.datetime]
|
||||
Subscription: str
|
||||
|
||||
class LiveAlgorithmApiSettingsWrapper(System.object):
|
||||
"""
|
||||
Helper class to put BaseLiveAlgorithmSettings in proper format.
|
||||
|
||||
LiveAlgorithmApiSettingsWrapper(projectId: int, compileId: str, serverType: str, settings: BaseLiveAlgorithmSettings, version: str)
|
||||
"""
|
||||
def __init__(self, projectId: int, compileId: str, serverType: str, settings: QuantConnect.API.BaseLiveAlgorithmSettings, version: str) -> QuantConnect.API.LiveAlgorithmApiSettingsWrapper:
|
||||
pass
|
||||
|
||||
Brokerage: QuantConnect.API.BaseLiveAlgorithmSettings
|
||||
|
||||
CompileId: str
|
||||
|
||||
ProjectId: int
|
||||
|
||||
ServerType: str
|
||||
|
||||
VersionId: str
|
||||
|
||||
|
||||
|
||||
class LiveAlgorithmResults(QuantConnect.Api.RestResponse):
|
||||
"""
|
||||
Details a live algorithm from the "live/read" Api endpoint
|
||||
|
||||
LiveAlgorithmResults()
|
||||
"""
|
||||
LiveResults: QuantConnect.API.LiveResultsData
|
||||
|
||||
|
||||
|
||||
class LiveAlgorithmResultsJsonConverter(Newtonsoft.Json.JsonConverter):
|
||||
"""
|
||||
Custom JsonConverter for LiveResults data for live algorithms
|
||||
|
||||
LiveAlgorithmResultsJsonConverter()
|
||||
"""
|
||||
def CanConvert(self, objectType: type) -> bool:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def CreateLiveResultsFromJObject(jObject: Newtonsoft.Json.Linq.JObject) -> QuantConnect.API.LiveAlgorithmResults:
|
||||
pass
|
||||
|
||||
def ReadJson(self, reader: Newtonsoft.Json.JsonReader, objectType: type, existingValue: object, serializer: Newtonsoft.Json.JsonSerializer) -> object:
|
||||
pass
|
||||
|
||||
def WriteJson(self, writer: Newtonsoft.Json.JsonWriter, value: object, serializer: Newtonsoft.Json.JsonSerializer) -> None:
|
||||
pass
|
||||
|
||||
CanWrite: bool
|
||||
|
||||
|
||||
|
||||
class LiveList(QuantConnect.Api.RestResponse):
|
||||
"""
|
||||
List of the live algorithms running which match the requested status
|
||||
|
||||
LiveList()
|
||||
"""
|
||||
Algorithms: typing.List[QuantConnect.API.LiveAlgorithm]
|
||||
|
||||
class LiveLog(QuantConnect.Api.RestResponse):
|
||||
"""
|
||||
Logs from a live algorithm
|
||||
|
||||
LiveLog()
|
||||
"""
|
||||
Logs: typing.List[str]
|
||||
|
||||
|
||||
|
||||
class LiveResultsData(System.object):
|
||||
"""
|
||||
Holds information about the state and operation of the live running algorithm
|
||||
|
||||
LiveResultsData()
|
||||
"""
|
||||
Resolution: QuantConnect.Resolution
|
||||
|
||||
Results: QuantConnect.Packets.LiveResult
|
||||
|
||||
Version: int
|
||||
|
||||
|
||||
|
||||
class Node(System.object):
|
||||
"""
|
||||
Node class built for API endpoints nodes/read and nodes/create.
|
||||
Converts JSON properties from API response into data members for the class.
|
||||
Contains all relevant information on a Node to interact through API endpoints.
|
||||
|
||||
Node()
|
||||
"""
|
||||
Busy: bool
|
||||
|
||||
CpuCount: int
|
||||
|
||||
Description: str
|
||||
|
||||
Id: str
|
||||
|
||||
Name: str
|
||||
|
||||
Prices: QuantConnect.API.NodePrices
|
||||
|
||||
ProjectName: str
|
||||
|
||||
Ram: float
|
||||
|
||||
SKU: str
|
||||
|
||||
Speed: float
|
||||
|
||||
UsedBy: str
|
||||
|
||||
|
||||
|
||||
class NodeList(QuantConnect.Api.RestResponse):
|
||||
"""
|
||||
Rest api response wrapper for node/read, contains sets of node lists for each
|
||||
target environment. List are composed of QuantConnect.API.Node objects.
|
||||
|
||||
NodeList()
|
||||
"""
|
||||
BacktestNodes: typing.List[QuantConnect.API.Node]
|
||||
LiveNodes: typing.List[QuantConnect.API.Node]
|
||||
ResearchNodes: typing.List[QuantConnect.API.Node]
|
||||
|
||||
class NodePrices(System.object):
|
||||
"""
|
||||
Class for deserializing node prices from node object
|
||||
|
||||
NodePrices()
|
||||
"""
|
||||
Monthly: int
|
||||
|
||||
Yearly: int
|
||||
|
||||
|
||||
|
||||
class NodeType(System.Enum, System.IConvertible, System.IFormattable, System.IComparable):
|
||||
"""
|
||||
NodeTypes enum for all possible options of target environments
|
||||
Used in conjuction with SKU class as a NodeType is a required parameter for SKU
|
||||
|
||||
enum NodeType, values: Backtest (0), Live (2), Research (1)
|
||||
"""
|
||||
value__: int
|
||||
Backtest: 'NodeType'
|
||||
Live: 'NodeType'
|
||||
Research: 'NodeType'
|
||||
|
||||
|
||||
class OandaLiveAlgorithmSettings(QuantConnect.API.BaseLiveAlgorithmSettings):
|
||||
"""
|
||||
Live algorithm settings for trading with Oanda
|
||||
|
||||
OandaLiveAlgorithmSettings(accessToken: str, environment: BrokerageEnvironment, account: str)
|
||||
"""
|
||||
def __init__(self, accessToken: str, environment: QuantConnect.BrokerageEnvironment, account: str) -> QuantConnect.API.OandaLiveAlgorithmSettings:
|
||||
pass
|
||||
|
||||
AccessToken: str
|
||||
|
||||
DateIssued: str
|
||||
|
||||
|
||||
|
||||
class Prices(System.object):
|
||||
"""
|
||||
Prices rest response wrapper
|
||||
|
||||
Prices()
|
||||
"""
|
||||
Price: float
|
||||
|
||||
Symbol: QuantConnect.Symbol
|
||||
|
||||
SymbolID: str
|
||||
|
||||
Updated: datetime.datetime
|
||||
|
||||
|
||||
class PricesList(QuantConnect.Api.RestResponse):
|
||||
"""
|
||||
Collection container for a list of prices objects
|
||||
|
||||
PricesList()
|
||||
"""
|
||||
Prices: typing.List[QuantConnect.API.Prices]
|
||||
|
||||
class SKU(System.object):
|
||||
"""
|
||||
Class for generating a SKU for a node with a given configuration
|
||||
Every SKU is made up of 3 variables:
|
||||
- Target environment (L for live, B for Backtest, R for Research)
|
||||
- CPU core count
|
||||
- Dedicated RAM (GB)
|
||||
|
||||
SKU(cores: int, memory: int, target: NodeType)
|
||||
"""
|
||||
def ToString(self) -> str:
|
||||
pass
|
||||
|
||||
def __init__(self, cores: int, memory: int, target: QuantConnect.API.NodeType) -> QuantConnect.API.SKU:
|
||||
pass
|
||||
|
||||
Cores: int
|
||||
Memory: int
|
||||
Target: QuantConnect.API.NodeType
|
||||
|
||||
class Split(System.object):
|
||||
"""
|
||||
Split returned from the api
|
||||
|
||||
Split()
|
||||
"""
|
||||
Date: datetime.datetime
|
||||
|
||||
ReferencePrice: float
|
||||
|
||||
SplitFactor: float
|
||||
|
||||
Symbol: QuantConnect.Symbol
|
||||
|
||||
SymbolID: str
|
||||
|
||||
|
||||
|
||||
class SplitList(QuantConnect.Api.RestResponse):
|
||||
"""
|
||||
Collection container for a list of split objects
|
||||
|
||||
SplitList()
|
||||
"""
|
||||
Splits: typing.List[QuantConnect.API.Split]
|
||||
|
||||
|
||||
|
||||
class TradierLiveAlgorithmSettings(QuantConnect.API.BaseLiveAlgorithmSettings):
|
||||
"""
|
||||
Live algorithm settings for trading with Tradier
|
||||
|
||||
TradierLiveAlgorithmSettings(accessToken: str, dateIssued: str, refreshToken: str, account: str)
|
||||
"""
|
||||
def __init__(self, accessToken: str, dateIssued: str, refreshToken: str, account: str) -> QuantConnect.API.TradierLiveAlgorithmSettings:
|
||||
pass
|
||||
|
||||
AccessToken: str
|
||||
|
||||
DateIssued: str
|
||||
|
||||
Lifetime: str
|
||||
|
||||
RefreshToken: str
|
||||
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
# encoding: utf-8
|
||||
# module QuantConnect.Algorithm.Framework.Alphas.Analysis.Functions calls itself Functions
|
||||
# from QuantConnect.Common, Version=2.4.0.0, Culture=neutral, PublicKeyToken=null
|
||||
# by generator 1.145
|
||||
# no doc
|
||||
|
||||
# imports
|
||||
import datetime
|
||||
import QuantConnect.Algorithm.Framework.Alphas
|
||||
import QuantConnect.Algorithm.Framework.Alphas.Analysis
|
||||
import typing
|
||||
|
||||
# no functions
|
||||
# classes
|
||||
|
||||
class BinaryInsightScoreFunction(System.object, QuantConnect.Algorithm.Framework.Alphas.Analysis.IInsightScoreFunction):
|
||||
"""
|
||||
Defines a scoring function that always returns 1 or 0.
|
||||
You're either right or you're wrong with this one :)
|
||||
|
||||
BinaryInsightScoreFunction()
|
||||
"""
|
||||
def Evaluate(self, context: QuantConnect.Algorithm.Framework.Alphas.Analysis.InsightAnalysisContext, scoreType: QuantConnect.Algorithm.Framework.Alphas.InsightScoreType) -> float:
|
||||
pass
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user