Compare commits
197 Commits
8949
...
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 | ||
|
|
56db26d16d | ||
|
|
dd8dc473b3 | ||
|
|
b8033c496c | ||
|
|
166fee311a | ||
|
|
be8e381fba | ||
|
|
75eac27795 | ||
|
|
12b481f1ce | ||
|
|
9cb2452025 | ||
|
|
d8dc03fadc | ||
|
|
124e76cfe8 | ||
|
|
4a0fb30df5 | ||
|
|
60b8cf76ba | ||
|
|
9916a9069c | ||
|
|
f135fb8060 | ||
|
|
7bb143b215 | ||
|
|
d7e543736f | ||
|
|
8785af7ad6 | ||
|
|
808fa327e2 | ||
|
|
778e3015c8 | ||
|
|
616b2b8d52 | ||
|
|
98d3a98656 | ||
|
|
20791d6a9e | ||
|
|
3609340281 | ||
|
|
8edc89a4ff | ||
|
|
718dc02173 | ||
|
|
ec5ff53566 | ||
|
|
400a0d42d9 | ||
|
|
ae11e9ce43 | ||
|
|
d3ccc14fcd | ||
|
|
90e911f22c | ||
|
|
34676f0c46 | ||
|
|
d4ca27f93f | ||
|
|
f4ed48fce2 | ||
|
|
ce29a68f85 | ||
|
|
375f192f07 | ||
|
|
ef9ca98bd9 | ||
|
|
aded66ec5b | ||
|
|
59ce008725 | ||
|
|
46e10c0a27 | ||
|
|
2431cfabe7 | ||
|
|
b8674731a5 | ||
|
|
a14f8f1f47 | ||
|
|
fc6ccdbc11 | ||
|
|
ccd31bdfab | ||
|
|
14dd52ba00 | ||
|
|
2d46674fb7 | ||
|
|
554b7345bb | ||
|
|
4348e830ba | ||
|
|
c85f608444 | ||
|
|
9cdb4a91c5 | ||
|
|
a128f8bb2e | ||
|
|
3ef6a761ad | ||
|
|
b866c8b893 | ||
|
|
64a99d1194 | ||
|
|
57cbfa2f26 | ||
|
|
bc44e23322 | ||
|
|
6c051cd816 | ||
|
|
4c04efc237 | ||
|
|
a83388ab1a | ||
|
|
1c9b817960 | ||
|
|
c5c57a33c1 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -34,8 +34,9 @@
|
||||
|
||||
# QC Cloud Setup Bash Files
|
||||
*.sh
|
||||
# Include docker build scripts for Mac/Linux
|
||||
# Include docker launch scripts for Mac/Linux
|
||||
!run_docker.sh
|
||||
!research/run_docker_notebook.sh
|
||||
|
||||
# QC Config Files:
|
||||
# config.json
|
||||
@@ -267,3 +268,9 @@ Launcher/Plugins/*
|
||||
/ApiPython/quantconnect.egg-info/*
|
||||
|
||||
QuantConnect.Lean.sln.DotSettings*
|
||||
|
||||
#User notebook files
|
||||
Research/Notebooks
|
||||
|
||||
#Docker result files
|
||||
Results/
|
||||
144
.idea/readme.md
generated
Normal file
144
.idea/readme.md
generated
Normal file
@@ -0,0 +1,144 @@
|
||||
<h1>Local Development & Docker Integration with Pycharm</h1>
|
||||
|
||||
This document contains information regarding ways to use Lean’s Docker image in conjunction with local development in Pycharm.
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
<h1>Getting Setup</h1>
|
||||
|
||||
|
||||
Before anything we need to ensure a few things have been done:
|
||||
|
||||
|
||||
1. Get [Pycharm Professional](https://www.jetbrains.com/pycharm/)**
|
||||
|
||||
2. Get [Docker](https://docs.docker.com/get-docker/):
|
||||
* Follow the instructions for your Operating System
|
||||
* New to Docker? Try docker getting-started
|
||||
|
||||
|
||||
3. Pull Lean’s latest image from a terminal
|
||||
* _docker pull quantconnect/lean_
|
||||
|
||||
4. Get Lean into Pycharm
|
||||
* Download the repo or clone it using: _git clone[ https://github.com/QuantConnect/Lean](https://github.com/QuantConnect/Lean)_
|
||||
* Open the folder using Pycharm
|
||||
|
||||
|
||||
_**PyCharm’s remote debugger requires PyCharm Professional._
|
||||
|
||||
<br />
|
||||
|
||||
<h1>Develop Algorithms Locally, Run in Container</h1>
|
||||
|
||||
|
||||
We have set up a relatively easy way to develop algorithms in your local IDE and push them into the container to be run and debugged.
|
||||
|
||||
Before we can use this method with Windows or Mac OS we need to share the Lean directory with Docker.
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Activate File Sharing for Docker:</h2>
|
||||
|
||||
* Windows:
|
||||
* [Guide to sharing](https://docs.docker.com/docker-for-windows/#file-sharing)
|
||||
* Share the LEAN root directory with docker
|
||||
|
||||
* Mac:
|
||||
* [Guide to sharing](https://docs.docker.com/docker-for-mac/#file-sharing)
|
||||
* Share the LEAN root directory with docker
|
||||
|
||||
* Linux:
|
||||
* (No setup required)
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Lean Configuration</h2>
|
||||
|
||||
Next we need to be sure that our Lean configuration at **.\Launcher\config.json** is properly set. Just like running lean locally the config must reflect what we want Lean to run.
|
||||
|
||||
You configuration file should look something like this:
|
||||
|
||||
<h3>Python:</h3>
|
||||
|
||||
"algorithm-type-name": "**AlgorithmName**",
|
||||
|
||||
"algorithm-language": "Python",
|
||||
|
||||
"algorithm-location": "../../../Algorithm.Python/**AlgorithmName**.py",
|
||||
|
||||
<h4>Note About Python Algorithm Location</h4>
|
||||
|
||||
|
||||
Our specific configuration binds the Algorithm.Python directory to the container by default so any algorithm you would like to run should be in that directory. Please ensure your algorithm location looks just the same as the example above. If you want to use a different location refer to the section bellow on setting that argument for the container and make sure your config.json also reflects this.
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Running Lean in the Container</h2>
|
||||
|
||||
This section will cover how to actually launch Lean in the container with your desired configuration.
|
||||
|
||||
From a terminal; Pycharm has a built in terminal on the bottom taskbar labeled **Terminal**; launch the run_docker.bat/.sh script; there are a few choices on how to launch this:
|
||||
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_\]:
|
||||
* 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=
|
||||
|
||||
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 />
|
||||
|
||||
<h1>Debugging Python</h1>
|
||||
|
||||
Debugging your Python algorithms requires an extra step within your configuration and inside of PyCharm. Thankfully we were able to configure the PyCharm launch configurations to take care of most of the work for you!
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Modifying the Configuration</h2>
|
||||
|
||||
First in order to debug a Python algorithm in Pycharm we must make the following change to our configuration (Launcher\config.json) under the comment debugging configuration:
|
||||
|
||||
"debugging": true,
|
||||
"debugging-method": "PyCharm",
|
||||
|
||||
|
||||
In setting this we are telling Lean to reach out and create a debugger connection using PyCharm’s PyDevd debugger server. Once this is set Lean will **always** attempt to connect to a debugger server on launch. **If you are no longer debugging set “debugging” to false.**
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Using PyCharm Launch Options</h2>
|
||||
|
||||
|
||||
Now that Lean is configured for the debugger we can make use of the programmed launch options to connect.
|
||||
|
||||
|
||||
|
||||
**<h3>Container (Recommended)</h3>**
|
||||
|
||||
|
||||
To debug inside of the container we must first start the debugger server in Pycharm, to do this use the drop down configuration “Debug in Container” and launch the debugger. Be sure to set some breakpoints in your algorithms!
|
||||
|
||||
Then we will need to launch the container, follow the steps described in the section “[Running Lean in the Container](#Running-Lean-in-the-Container)”. After launching the container the debugging configuration will take effect and it will connect to the debug server where you can begin debugging your algorithm.
|
||||
|
||||
|
||||
**<h3>Local</h3>**
|
||||
|
||||
|
||||
To debug locally we must run the program locally. First, just as the container setup, start the PyCharm debugger server by running the “Debug Local” configuration.
|
||||
|
||||
Then start the program locally by whatever means you typically use, such as Mono, directly running the program at **QuantConnect.Lean.Launcher.exe**, etc. Once the program is running it will make the connection to your PyCharm debugger server where you can begin debugging your algorithm.
|
||||
37
.idea/workspace.xml
generated
Normal file
37
.idea/workspace.xml
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunManager" selected="Python Debug Server.Debug in Container">
|
||||
<configuration name="Debug Local" type="PyRemoteDebugConfigurationType" factoryName="Python Remote Debug">
|
||||
<module name="LEAN" />
|
||||
<option name="PORT" value="5678" />
|
||||
<option name="HOST" value="localhost" />
|
||||
<PathMappingSettings>
|
||||
<option name="pathMappings">
|
||||
<list />
|
||||
</option>
|
||||
</PathMappingSettings>
|
||||
<option name="REDIRECT_OUTPUT" value="true" />
|
||||
<option name="SUSPEND_AFTER_CONNECT" value="true" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="Debug in Container" type="PyRemoteDebugConfigurationType" factoryName="Python Remote Debug">
|
||||
<module name="LEAN" />
|
||||
<option name="PORT" value="5678" />
|
||||
<option name="HOST" value="localhost" />
|
||||
<PathMappingSettings>
|
||||
<option name="pathMappings">
|
||||
<list>
|
||||
<mapping local-root="$PROJECT_DIR$" remote-root="/Lean" />
|
||||
</list>
|
||||
</option>
|
||||
</PathMappingSettings>
|
||||
<option name="REDIRECT_OUTPUT" value="true" />
|
||||
<option name="SUSPEND_AFTER_CONNECT" value="true" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<list>
|
||||
<item itemvalue="Python Debug Server.Debug Local" />
|
||||
<item itemvalue="Python Debug Server.Debug in Container" />
|
||||
</list>
|
||||
</component>
|
||||
</project>
|
||||
@@ -16,9 +16,12 @@ 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
|
||||
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 -d -t -g -p
|
||||
|
||||
136
.vs/readme.md
Normal file
136
.vs/readme.md
Normal file
@@ -0,0 +1,136 @@
|
||||
<h1>Local Development & Docker Integration with Visual Studio</h1>
|
||||
|
||||
|
||||
This document contains information regarding ways to use Visual Studio to work with the Lean's Docker image.
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
<h1>Getting Setup</h1>
|
||||
|
||||
|
||||
Before anything we need to ensure a few things have been done:
|
||||
|
||||
|
||||
1. Get [Visual Studio](https://code.visualstudio.com/download)
|
||||
* Get the Extension [VSMonoDebugger](https://marketplace.visualstudio.com/items?itemName=GordianDotNet.VSMonoDebugger0d62) for C# Debugging
|
||||
|
||||
2. Get [Docker](https://docs.docker.com/get-docker/):
|
||||
* Follow the instructions for your Operating System
|
||||
* New to Docker? Try docker getting-started
|
||||
|
||||
|
||||
3. Pull Lean’s latest image from a terminal
|
||||
* _docker pull quantconnect/lean_
|
||||
|
||||
4. Get Lean into Visual Studio
|
||||
* Download the repo or clone it using: _git clone[ https://github.com/QuantConnect/Lean](https://github.com/QuantConnect/Lean)_
|
||||
* Open the solution **QuantConnect.Lean.sln** using Visual Studio
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
<h1>Develop Algorithms Locally, Run in Container</h1>
|
||||
|
||||
|
||||
We have set up a relatively easy way to develop algorithms in your local IDE and push them into the container to be run and debugged.
|
||||
|
||||
Before we can use this method with Windows or Mac OS we need to share the Lean directory with Docker.
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Activate File Sharing for Docker:</h2>
|
||||
|
||||
* Windows:
|
||||
* [Guide to sharing](https://docs.docker.com/docker-for-windows/#file-sharing)
|
||||
* Share the LEAN root directory with docker
|
||||
|
||||
* Mac:
|
||||
* [Guide to sharing](https://docs.docker.com/docker-for-mac/#file-sharing)
|
||||
* Share the LEAN root directory with docker
|
||||
|
||||
* Linux:
|
||||
* (No setup required)
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Lean Configuration</h2>
|
||||
|
||||
Next we need to be sure that our Lean configuration at **.\Launcher\config.json** is properly set. Just like running lean locally the config must reflect what we want Lean to run.
|
||||
|
||||
You configuration file should look something like this for the following languages:
|
||||
|
||||
<h3>Python:</h3>
|
||||
|
||||
"algorithm-type-name": "**AlgorithmName**",
|
||||
|
||||
"algorithm-language": "Python",
|
||||
|
||||
"algorithm-location": "../../../Algorithm.Python/**AlgorithmName**.py",
|
||||
|
||||
<h3>C#:</h3>
|
||||
|
||||
"algorithm-type-name": "**AlgorithmName**",
|
||||
|
||||
"algorithm-language": "CSharp",
|
||||
|
||||
"algorithm-location": "QuantConnect.Algorithm.CSharp.dll",
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Important Note About C#</h2>
|
||||
|
||||
In order to use a custom C# algorithm, the C# file must be compiled before running in the docker, as it is compiled into the file **"QuantConnect.Algorithm.CSharp.dll"**. Any new C# files will need to be added to the csproj compile list before it will compile, check **Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj** for all algorithms that are compiled. Once there is an entry for your algorithm the project can be compiled by using **Build > Build Solution**.
|
||||
|
||||
If you would like to debug this file in the docker container one small change to the solutions target build is required.
|
||||
1. Right click on the solution **QuantConnect.Lean** in the _Solution Explorer_
|
||||
2. Select **Properties**
|
||||
3. For project entry **QuantConnect.Algorithm.CSharp** change the configuration to **DebugDocker**
|
||||
4. Select **Apply** and close out of the window.
|
||||
5. Build the project at least once before running the docker.
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Running Lean in the Container</h2>
|
||||
|
||||
This section will cover how to actually launch Lean in the container with your desired configuration.
|
||||
|
||||
From a terminal launch the run_docker.bat/.sh script; there are a few choices on how to launch this:
|
||||
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_\]:
|
||||
* 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=
|
||||
|
||||
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 />
|
||||
|
||||
<h1>Connecting to Mono Debugger</h1>
|
||||
|
||||
If you launch the script with debugging set to **yes** (y), then you will need to connect to the debugging server with the mono extension that you installed in the setup stage.
|
||||
|
||||
To setup the extension do the following:
|
||||
* Go to **Extensions > Mono > Settings...**
|
||||
* Enter the following for the settings:
|
||||
* Remote Host IP: 127.0.0.1
|
||||
* Remote Host Port: 55555
|
||||
* Mono Debug Port: 55555
|
||||
* Click **Save** and then close the extension settings
|
||||
|
||||
Now that the extension is setup use it to connect to the Docker container by using:
|
||||
* **Extensions > Mono > Attach to mono debugger**
|
||||
|
||||
The program should then launch and trigger any breakpoints you have set in your C# Algorithm.
|
||||
82
.vscode/launch.json
vendored
Normal file
82
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
/*
|
||||
VS Code Launch configurations for the LEAN engine
|
||||
|
||||
Launch w/ Mono (Local):
|
||||
Builds the project with MSBuild and then launches the program using mono locally;
|
||||
supports debugging. In order to use this you need msbuild and mono on your system path.
|
||||
As well as the Mono Debug extension from the marketplace.
|
||||
|
||||
Debug in Container:
|
||||
Launches our run_docker script to start the container and attaches to the debugger.
|
||||
Requires that you have built the project at least once as it will transfer the compiled
|
||||
csharp files.
|
||||
Requires Mono Debug extension from the marketplace.
|
||||
|
||||
Attach to Python (Container):
|
||||
Will attempt to attach to LEAN in the container using PTVSD. Requires that the container is
|
||||
actively running and config is set: "debugging": true, "debugging-method": "PTVSD",
|
||||
Requires Python extension from the marketplace.
|
||||
|
||||
Attach to Python (Local):
|
||||
Will attempt to attach to LEAN running locally using PTVSD. Requires that the process is
|
||||
actively running and config is set: "debugging": true, "debugging-method": "PTVSD",
|
||||
Requires Python extension from the marketplace.
|
||||
|
||||
*/
|
||||
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Launch w/ Mono (Local)",
|
||||
"type": "mono",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"cwd": "${workspaceFolder}/Launcher/bin/Debug/",
|
||||
"program": "${workspaceFolder}/Launcher/bin/Debug/QuantConnect.Lean.Launcher.exe",
|
||||
"args": [
|
||||
"--data-folder",
|
||||
"${workspaceFolder}/Data",
|
||||
"--config",
|
||||
"${workspaceFolder}/Launcher/config.json"],
|
||||
"console": "externalTerminal"
|
||||
},
|
||||
{
|
||||
"name": "Debug in Container",
|
||||
"type": "mono",
|
||||
"preLaunchTask": "run-docker",
|
||||
"postDebugTask": "close-docker",
|
||||
"request": "attach",
|
||||
"address": "localhost",
|
||||
"port": 55555
|
||||
},
|
||||
{
|
||||
"name": "Attach to Mono",
|
||||
"type": "mono",
|
||||
"request": "attach",
|
||||
"address": "localhost",
|
||||
"postDebugTask": "close-docker",
|
||||
"port": 55555
|
||||
},
|
||||
{
|
||||
"name": "Attach to Python (Container)",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"port": 5678,
|
||||
"pathMappings":[{
|
||||
"localRoot": "${workspaceFolder}",
|
||||
"remoteRoot": "/Lean/"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"name": "Attach to Python (Local)",
|
||||
"type": "python",
|
||||
"request": "attach",
|
||||
"port": 5678,
|
||||
"pathMappings":[{
|
||||
"localRoot": "${workspaceFolder}",
|
||||
"remoteRoot": "${workspaceFolder}"
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
197
.vscode/readme.md
vendored
Normal file
197
.vscode/readme.md
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
<h1>Local Development & Docker Integration with Visual Studio Code</h1>
|
||||
|
||||
|
||||
This document contains information regarding ways to use Visual Studio Code to work with the Lean engine, this includes using Lean’s Docker image in conjunction with local development as well as running Lean locally.
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
<h1>Getting Setup</h1>
|
||||
|
||||
|
||||
Before anything we need to ensure a few things have been done:
|
||||
|
||||
|
||||
1. Get [Visual Studio Code](https://code.visualstudio.com/download)
|
||||
* Get the Extension [Mono Debug](https://marketplace.visualstudio.com/items?itemName=ms-vscode.mono-debug) for C# Debugging
|
||||
* Get the Extension [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) for Python Debugging
|
||||
|
||||
2. Get [Docker](https://docs.docker.com/get-docker/):
|
||||
* Follow the instructions for your Operating System
|
||||
* New to Docker? Try docker getting-started
|
||||
|
||||
3. Install a compiler for the project **(Only needed for C# Debugging or Running Locally)**
|
||||
* On Linux or Mac:
|
||||
* Install [mono-complete](https://www.mono-project.com/docs/getting-started/install/linux/)
|
||||
* Test msbuild with command: _msbuild -version_
|
||||
* On Windows:
|
||||
* Visual Studio comes packed with msbuild or download without VS [here](https://visualstudio.microsoft.com/downloads/?q=build+tools)
|
||||
* Put msbuild on your system path and test with command: _msbuild -version_
|
||||
|
||||
4. Pull Lean’s latest image from a terminal
|
||||
* _docker pull quantconnect/lean_
|
||||
|
||||
5. Get Lean into VS Code
|
||||
* Download the repo or clone it using: _git clone[ https://github.com/QuantConnect/Lean](https://github.com/QuantConnect/Lean)_
|
||||
* Open the folder using VS Code
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
<h1>Develop Algorithms Locally, Run in Container</h1>
|
||||
|
||||
|
||||
We have set up a relatively easy way to develop algorithms in your local IDE and push them into the container to be run and debugged.
|
||||
|
||||
Before we can use this method with Windows or Mac OS we need to share the Lean directory with Docker.
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Activate File Sharing for Docker:</h2>
|
||||
|
||||
* Windows:
|
||||
* [Guide to sharing](https://docs.docker.com/docker-for-windows/#file-sharing)
|
||||
* Share the LEAN root directory with docker
|
||||
|
||||
* Mac:
|
||||
* [Guide to sharing](https://docs.docker.com/docker-for-mac/#file-sharing)
|
||||
* Share the LEAN root directory with docker
|
||||
|
||||
* Linux:
|
||||
* (No setup required)
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Lean Configuration</h2>
|
||||
|
||||
Next we need to be sure that our Lean configuration at **.\Launcher\config.json** is properly set. Just like running lean locally the config must reflect what we want Lean to run.
|
||||
|
||||
You configuration file should look something like this for the following languages:
|
||||
|
||||
<h3>Python:</h3>
|
||||
|
||||
"algorithm-type-name": "**AlgorithmName**",
|
||||
|
||||
"algorithm-language": "Python",
|
||||
|
||||
"algorithm-location": "../../../Algorithm.Python/**AlgorithmName**.py",
|
||||
|
||||
<h3>C#:</h3>
|
||||
|
||||
"algorithm-type-name": "**AlgorithmName**",
|
||||
|
||||
"algorithm-language": "CSharp",
|
||||
|
||||
"algorithm-location": "QuantConnect.Algorithm.CSharp.dll",
|
||||
|
||||
|
||||
<h3>Important Note About C#</h3>
|
||||
|
||||
In order to use a custom C# algorithm, the C# file must be compiled before running in the docker, as it is compiled into the file "QuantConnect.Algorithm.CSharp.dll". Any new C# files will need to be added to the csproj compile list before it will compile, check Algorithm.CSharp/QuantConnect.Algorithm.CSharp.csproj for all algorithms that are compiled. Once there is an entry for your algorithm the project can be compiled by using the “build” task under _“Terminal” > “Run Build Task”._
|
||||
|
||||
Python **does not** have this requirement as the engine will compile it on the fly.
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Running Lean in the Container</h2>
|
||||
|
||||
This section will cover how to actually launch Lean in the container with your desired configuration.
|
||||
|
||||
<br />
|
||||
|
||||
<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 arguments are:
|
||||
|
||||
"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.
|
||||
|
||||
<br />
|
||||
|
||||
<h3>Option 2</h3>
|
||||
|
||||
From a terminal launch the run_docker.bat/.sh script; there are a few choices on how to launch this:
|
||||
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: .\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=
|
||||
|
||||
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 />
|
||||
|
||||
<h1>Debugging Python</h1>
|
||||
|
||||
Python algorithms require a little extra work in order to be able to debug them locally or in the container. Thankfully we were able to configure VS code tasks to take care of the work for you! Follow the steps below to get Python debugging working.
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Modifying the Configuration</h2>
|
||||
|
||||
First in order to debug a Python algorithm in VS Code we must make the following change to our configuration (Launcher\config.json) under the comment debugging configuration:
|
||||
|
||||
"debugging": true,
|
||||
"debugging-method": "PTVSD",
|
||||
|
||||
In setting this we are telling Lean to expect a debugger connection using ‘Python Tools for Visual Studio Debugger’. Once this is set Lean will stop upon initialization and await a connection to the debugger via port 5678.
|
||||
|
||||
<br />
|
||||
|
||||
<h2>Using VS Code Launch Options to Connect</h2>
|
||||
|
||||
Now that Lean is configured for the python debugger we can make use of the programmed launch options to connect.
|
||||
|
||||
<br />
|
||||
|
||||
<h3>Container</h3>
|
||||
|
||||
|
||||
To debug inside of the container we must first start the container, follow the steps described in the section “[Running Lean in the Container](#Running-Lean-in-the-Container)”. Once the container is started you should see the messages in Figure 2.
|
||||
|
||||
If the message is displayed, use the same drop down for “Debug in Container” and select “Attach to Python (Container)”. Then press run, VS Code will now enter and debug any breakpoints you have set in your Python algorithm.
|
||||
|
||||
<br />
|
||||
|
||||
<h3>Local</h3>
|
||||
|
||||
|
||||
To debug locally we must run the program locally using the programmed task found under Terminal > Run Task > “Run Application”. Once Lean is started you should see the messages in Figure 2.
|
||||
|
||||
If the message is displayed, use the launch option “Attach to Python (Local)”. Then press run, VS Code will now enter and debug any breakpoints you have set in your python algorithm.
|
||||
|
||||
<br />
|
||||
|
||||
_Figure 2: Python Debugger Messages_
|
||||
|
||||
```
|
||||
20200715 17:12:06.546 Trace:: PythonInitializer.Initialize(): ended
|
||||
20200715 17:12:06.547 Trace:: DebuggerHelper.Initialize(): python initialization done
|
||||
20200715 17:12:06.547 Trace:: DebuggerHelper.Initialize(): starting...
|
||||
20200715 17:12:06.548 Trace:: DebuggerHelper.Initialize(): waiting for debugger to attach at localhost:5678...
|
||||
```
|
||||
|
||||
<br />
|
||||
|
||||
<h1>Common Issues</h1>
|
||||
Here we will cover some common issues with setting this up. This section will expand as we get user feedback!
|
||||
|
||||
* Error messages about build in VSCode points to comments in JSON. Either select **ignore** or follow steps described [here](https://stackoverflow.com/questions/47834825/in-vs-code-disable-error-comments-are-not-permitted-in-json) to remove the errors entirely.
|
||||
111
.vscode/tasks.json
vendored
Normal file
111
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
{
|
||||
/*
|
||||
VS Code Tasks for the LEAN engine
|
||||
In order to use the build tasks you need msbuild on your system path.
|
||||
*/
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"/p:Configuration=Debug",
|
||||
"/p:DebugType=portable",
|
||||
"/t:build",
|
||||
],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "force build linux",
|
||||
"type": "shell",
|
||||
"command": "msbuild",
|
||||
"args": [
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/p:Configuration=Debug",
|
||||
"/p:DebugType=portable",
|
||||
"/t:build",
|
||||
"/p:ForceLinuxBuild=true"
|
||||
],
|
||||
"group": "build",
|
||||
"presentation": {
|
||||
"reveal": "silent"
|
||||
},
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "run-docker",
|
||||
"type": "shell",
|
||||
"isBackground": true,
|
||||
"windows": {
|
||||
"command": "${workspaceFolder}/run_docker.bat",
|
||||
},
|
||||
"linux": {
|
||||
"command": "${workspaceFolder}/run_docker.sh"
|
||||
},
|
||||
"osx": {
|
||||
"command": "${workspaceFolder}/run_docker.sh"
|
||||
},
|
||||
"args": [
|
||||
"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": [
|
||||
{
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": ".",
|
||||
"file": 1,
|
||||
"location": 2,
|
||||
"message": 3
|
||||
}
|
||||
],
|
||||
"background": {
|
||||
"activeOnStart": true,
|
||||
"beginsPattern": ".",
|
||||
"endsPattern": ".",
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "close-docker",
|
||||
"type": "shell",
|
||||
"command": "docker stop LeanEngine",
|
||||
"presentation": {
|
||||
"echo": false,
|
||||
"reveal": "never",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": false,
|
||||
"clear": true,
|
||||
},
|
||||
"linux":{
|
||||
"command": "sudo docker stop LeanEngine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "Run Application",
|
||||
"type": "process",
|
||||
"command": "QuantConnect.Lean.Launcher.exe",
|
||||
"args" : [
|
||||
"--data-folder",
|
||||
"${workspaceFolder}/Data",
|
||||
"--config",
|
||||
"${workspaceFolder}/Launcher/config.json"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}/Launcher/bin/Debug/"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Custom.Robintrack;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp.AltData
|
||||
{
|
||||
/// <summary>
|
||||
/// Looks at users holding the stock AAPL at a given point in time
|
||||
/// and keeps track of changes in retail investor sentiment.
|
||||
///
|
||||
/// We go long if the sentiment increases by 0.5%, and short if it decreases by -0.5%
|
||||
/// </summary>
|
||||
public class RobintrackHoldingsAlgorithm : QCAlgorithm
|
||||
{
|
||||
private Symbol _aapl;
|
||||
private Symbol _aaplHoldings;
|
||||
private decimal _lastValue;
|
||||
private bool _isLong;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2018, 5, 1);
|
||||
SetEndDate(2020, 5, 5);
|
||||
SetCash(100000);
|
||||
|
||||
_aapl = AddEquity("AAPL", Resolution.Daily).Symbol;
|
||||
_aaplHoldings = AddData<RobintrackHoldings>(_aapl).Symbol;
|
||||
_isLong = false;
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
foreach (var kvp in data.Get<RobintrackHoldings>())
|
||||
{
|
||||
var holdings = kvp.Value;
|
||||
|
||||
if (_lastValue != 0)
|
||||
{
|
||||
var percentChange = (holdings.UsersHolding - _lastValue) / _lastValue;
|
||||
var holdingInfo = $"There are {holdings.UsersHolding} unique users holding {kvp.Key.Underlying} - users holding % of U.S. equities universe: {holdings.UniverseHoldingPercent * 100m}%";
|
||||
|
||||
if (percentChange >= 0.005m && !_isLong)
|
||||
{
|
||||
Log($"{UtcTime} - Buying AAPL - {holdingInfo}");
|
||||
SetHoldings(_aapl, 0.5);
|
||||
_isLong = true;
|
||||
}
|
||||
else if (percentChange <= -0.005m && _isLong)
|
||||
{
|
||||
Log($"{UtcTime} - Shorting AAPL - {holdingInfo}");
|
||||
SetHoldings(_aapl, -0.5);
|
||||
_isLong = false;
|
||||
}
|
||||
}
|
||||
|
||||
_lastValue = holdings.UsersHolding;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
323
Algorithm.CSharp/BacktestingBrokerageRegressionAlgorithm.cs
Normal file
323
Algorithm.CSharp/BacktestingBrokerageRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,323 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Orders.Fees;
|
||||
using QuantConnect.Orders.Fills;
|
||||
using QuantConnect.Securities;
|
||||
using QuantConnect.Securities.Option;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests the order processing of the backtesting brokerage.
|
||||
/// We open an equity position that should fill in two parts, on two different bars.
|
||||
/// We open a long option position and let it expire so we can exercise the position.
|
||||
/// To check the orders we use OnOrderEvent and throw exceptions if verification fails.
|
||||
/// </summary>
|
||||
/// <meta name="tag" content="backtesting brokerage" />
|
||||
/// <meta name="tag" content="regression test" />
|
||||
/// <meta name="tag" content="options" />
|
||||
class BacktestingBrokerageRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Security _security;
|
||||
private Symbol _spy;
|
||||
private OrderTicket _equityBuy;
|
||||
private Option _option;
|
||||
private Symbol _optionSymbol;
|
||||
private OrderTicket _optionBuy;
|
||||
private bool _optionBought = false;
|
||||
private bool _equityBought = false;
|
||||
private decimal _optionStrikePrice;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the algorithm
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
SetCash(100000);
|
||||
SetStartDate(2015, 12, 24);
|
||||
SetEndDate(2015, 12, 28);
|
||||
|
||||
// Get our equity
|
||||
_security = AddEquity("SPY", Resolution.Hour);
|
||||
_security.SetFillModel(new PartialMarketFillModel(2));
|
||||
_spy = _security.Symbol;
|
||||
|
||||
// Get our option
|
||||
_option = AddOption("GOOG");
|
||||
_option.SetFilter(u => u.IncludeWeeklys()
|
||||
.Strikes(-2, +2)
|
||||
.Expiration(TimeSpan.Zero, TimeSpan.FromDays(10)));
|
||||
_optionSymbol = _option.Symbol;
|
||||
}
|
||||
|
||||
/// <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 (!_equityBought && data.ContainsKey(_spy)) {
|
||||
//Buy our Equity
|
||||
var quantity = CalculateOrderQuantity(_spy, .1m);
|
||||
_equityBuy = MarketOrder(_spy, quantity, asynchronous: true);
|
||||
_equityBought = true;
|
||||
}
|
||||
|
||||
if (!_optionBought)
|
||||
{
|
||||
// Buy our option
|
||||
OptionChain chain;
|
||||
if (data.OptionChains.TryGetValue(_optionSymbol, out chain))
|
||||
{
|
||||
// Find the second call strike under market price expiring today
|
||||
var contracts = (
|
||||
from optionContract in chain.OrderByDescending(x => x.Strike)
|
||||
where optionContract.Right == OptionRight.Call
|
||||
where optionContract.Expiry == Time.Date
|
||||
where optionContract.Strike < chain.Underlying.Price
|
||||
select optionContract
|
||||
).Take(2);
|
||||
|
||||
if (contracts.Any())
|
||||
{
|
||||
var optionToBuy = contracts.FirstOrDefault();
|
||||
_optionStrikePrice = optionToBuy.Strike;
|
||||
_optionBuy = MarketOrder(optionToBuy.Symbol, 1);
|
||||
_optionBought = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All order events get pushed through this function
|
||||
/// </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);
|
||||
|
||||
// Based on the type verify the order
|
||||
switch(order.Type)
|
||||
{
|
||||
case OrderType.Market:
|
||||
VerifyMarketOrder(order, orderEvent);
|
||||
break;
|
||||
|
||||
case OrderType.OptionExercise:
|
||||
VerifyOptionExercise(order, orderEvent);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To verify Market orders is process correctly
|
||||
/// </summary>
|
||||
/// <param name="order">Order object to analyze</param>
|
||||
public void VerifyMarketOrder(Order order, OrderEvent orderEvent)
|
||||
{
|
||||
switch(order.Status)
|
||||
{
|
||||
case OrderStatus.Submitted:
|
||||
break;
|
||||
|
||||
// All PartiallyFilled orders should have a LastFillTime
|
||||
case OrderStatus.PartiallyFilled:
|
||||
if (order.LastFillTime == null)
|
||||
{
|
||||
throw new Exception("LastFillTime should not be null");
|
||||
}
|
||||
|
||||
if (order.Quantity/2 != orderEvent.FillQuantity)
|
||||
{
|
||||
throw new Exception("Order size should be half");
|
||||
}
|
||||
break;
|
||||
|
||||
// All filled equity orders should have filled after creation because of our fill model!
|
||||
case OrderStatus.Filled:
|
||||
if (order.SecurityType == SecurityType.Equity && order.CreatedTime == order.LastFillTime)
|
||||
{
|
||||
throw new Exception("Order should not finish during the CreatedTime bar");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// To verify OptionExercise orders is process correctly
|
||||
/// </summary>
|
||||
/// <param name="order">Order object to analyze</param>
|
||||
public void VerifyOptionExercise(Order order, OrderEvent orderEvent)
|
||||
{
|
||||
// If the option price isn't the same as the strike price, its incorrect
|
||||
if (order.Price != _optionStrikePrice)
|
||||
{
|
||||
throw new Exception("OptionExercise order price should be strike price!!");
|
||||
}
|
||||
|
||||
if (orderEvent.Quantity != -1)
|
||||
{
|
||||
throw new Exception("OrderEvent Quantity should be -1");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Runs after algorithm, used to check our portfolio and orders
|
||||
/// </summary>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (!Portfolio.ContainsKey(_optionBuy.Symbol) || !Portfolio.ContainsKey(_optionBuy.Symbol.Underlying) || !Portfolio.ContainsKey(_equityBuy.Symbol))
|
||||
{
|
||||
throw new Exception("Portfolio does not contain the Symbols we purchased");
|
||||
}
|
||||
|
||||
//Check option holding, should not be invested since it expired, profit should be -400
|
||||
var optionHolding = Portfolio[_optionBuy.Symbol];
|
||||
if (optionHolding.Invested || optionHolding.Profit != -400)
|
||||
{
|
||||
throw new Exception("Options holding does not match expected outcome");
|
||||
}
|
||||
|
||||
//Check the option underlying symbol since we should have bought it at exercise
|
||||
//Quantity should be 100, AveragePrice should be option strike price
|
||||
var optionExerciseHolding = Portfolio[_optionBuy.Symbol.Underlying];
|
||||
if (!optionExerciseHolding.Invested || optionExerciseHolding.Quantity != 100 || optionExerciseHolding.AveragePrice != _optionBuy.Symbol.ID.StrikePrice)
|
||||
{
|
||||
throw new Exception("Equity holding for exercised option does not match expected outcome");
|
||||
}
|
||||
|
||||
//Check equity holding, should be invested, profit should be
|
||||
//Quantity should be 50, AveragePrice should be ticket AverageFillPrice
|
||||
var equityHolding = Portfolio[_equityBuy.Symbol];
|
||||
if (!equityHolding.Invested || equityHolding.Quantity != 50 || equityHolding.AveragePrice != _equityBuy.AverageFillPrice)
|
||||
{
|
||||
throw new Exception("Equity holding does not match expected outcome");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// PartialMarketFillModel that allows the user to set the number of fills and restricts
|
||||
/// the fill to only one per bar.
|
||||
/// </summary>
|
||||
private class PartialMarketFillModel : ImmediateFillModel
|
||||
{
|
||||
private readonly decimal _percent;
|
||||
private readonly Dictionary<long, decimal> _absoluteRemainingByOrderId = new Dictionary<long, decimal>();
|
||||
|
||||
/// <param name="numberOfFills"></param>
|
||||
public PartialMarketFillModel(int numberOfFills = 1)
|
||||
{
|
||||
_percent = 1m / numberOfFills;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs partial market fills once per time step
|
||||
/// </summary>
|
||||
/// <param name="asset">The security being ordered</param>
|
||||
/// <param name="order">The order</param>
|
||||
/// <returns>The order fill</returns>
|
||||
public override OrderEvent MarketFill(Security asset, MarketOrder order)
|
||||
{
|
||||
var currentUtcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone);
|
||||
|
||||
// Only fill once a time slice
|
||||
if (order.LastFillTime != null && currentUtcTime <= order.LastFillTime)
|
||||
{
|
||||
return new OrderEvent(order, currentUtcTime, OrderFee.Zero);
|
||||
}
|
||||
|
||||
decimal absoluteRemaining;
|
||||
if (!_absoluteRemainingByOrderId.TryGetValue(order.Id, out absoluteRemaining))
|
||||
{
|
||||
absoluteRemaining = order.AbsoluteQuantity;
|
||||
_absoluteRemainingByOrderId.Add(order.Id, order.AbsoluteQuantity);
|
||||
}
|
||||
|
||||
var fill = base.MarketFill(asset, order);
|
||||
var absoluteFillQuantity = (int)(Math.Min(absoluteRemaining, (int)(_percent * order.Quantity)));
|
||||
fill.FillQuantity = Math.Sign(order.Quantity) * absoluteFillQuantity;
|
||||
|
||||
if (absoluteRemaining == absoluteFillQuantity)
|
||||
{
|
||||
fill.Status = OrderStatus.Filled;
|
||||
_absoluteRemainingByOrderId.Remove(order.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
absoluteRemaining = absoluteRemaining - absoluteFillQuantity;
|
||||
_absoluteRemainingByOrderId[order.Id] = absoluteRemaining;
|
||||
fill.Status = OrderStatus.PartiallyFilled;
|
||||
}
|
||||
|
||||
return fill;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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", "0%"},
|
||||
{"Average Loss", "-0.40%"},
|
||||
{"Compounding Annual Return", "-22.335%"},
|
||||
{"Drawdown", "0.400%"},
|
||||
{"Expectancy", "-1"},
|
||||
{"Net Profit", "-0.323%"},
|
||||
{"Sharpe Ratio", "-0.888"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "100%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.035"},
|
||||
{"Beta", "0.183"},
|
||||
{"Annual Standard Deviation", "0.004"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "12.058"},
|
||||
{"Tracking Error", "0.017"},
|
||||
{"Treynor Ratio", "-0.018"},
|
||||
{"Total Fees", "$2.00"},
|
||||
{"Fitness Score", "0.213"},
|
||||
{"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.917"},
|
||||
{"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"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,11 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
@@ -31,7 +33,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <meta name="tag" content="using data" />
|
||||
/// <meta name="tag" content="options" />
|
||||
/// <meta name="tag" content="filter selection" />
|
||||
public class BasicTemplateOptionsFilterUniverseAlgorithm : QCAlgorithm
|
||||
public class BasicTemplateOptionsFilterUniverseAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private const string UnderlyingTicker = "GOOG";
|
||||
public Symbol OptionSymbol;
|
||||
@@ -40,20 +42,17 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
SetStartDate(2015, 12, 24);
|
||||
SetEndDate(2015, 12, 24);
|
||||
SetCash(10000);
|
||||
SetCash(100000);
|
||||
|
||||
var equity = AddEquity(UnderlyingTicker);
|
||||
var option = AddOption(UnderlyingTicker);
|
||||
OptionSymbol = option.Symbol;
|
||||
|
||||
// set our custom filter for this option chain
|
||||
option.SetFilter(universe => from symbol in universe
|
||||
.WeeklysOnly()
|
||||
// Expiration method accepts TimeSpan objects or integer for days.
|
||||
// The following statements yield the same filtering criteria
|
||||
.Expiration(0, 10)
|
||||
// .Expiration(TimeSpan.Zero, TimeSpan.FromDays(10))
|
||||
// Set our custom universe filter, Expires today, is a call, and is within 10 dollars of the current price
|
||||
option.SetFilter(universe => from symbol in universe.WeeklysOnly().Expiration(0, 1)
|
||||
where symbol.ID.OptionRight != OptionRight.Put &&
|
||||
universe.Underlying.Price - symbol.ID.StrikePrice < 60
|
||||
-10 < universe.Underlying.Price - symbol.ID.StrikePrice &&
|
||||
universe.Underlying.Price - symbol.ID.StrikePrice < 10
|
||||
select symbol);
|
||||
|
||||
// use the underlying equity as the benchmark
|
||||
@@ -67,14 +66,13 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
OptionChain chain;
|
||||
if (slice.OptionChains.TryGetValue(OptionSymbol, out chain))
|
||||
{
|
||||
// find the second call strike under market price expiring today
|
||||
// Get the first ITM call expiring today
|
||||
var contract = (
|
||||
from optionContract in chain.OrderByDescending(x => x.Strike)
|
||||
where optionContract.Right == OptionRight.Call
|
||||
where optionContract.Expiry == Time.Date
|
||||
where optionContract.Strike < chain.Underlying.Price
|
||||
select optionContract
|
||||
).Skip(2).FirstOrDefault();
|
||||
).FirstOrDefault();
|
||||
|
||||
if (contract != null)
|
||||
{
|
||||
@@ -88,5 +86,62 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
Log(orderEvent.ToString());
|
||||
}
|
||||
|
||||
/// <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", "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", "$1.00"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Return Over Maximum Drawdown", "0"},
|
||||
{"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", "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"}
|
||||
};
|
||||
}
|
||||
}
|
||||
118
Algorithm.CSharp/DaylightSavingTimeHistoryRegressionAlgorithm.cs
Normal file
118
Algorithm.CSharp/DaylightSavingTimeHistoryRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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 NodaTime;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// Regression test algorithm simply fetch history on boarder of Daylight Saving Time shift
|
||||
/// </summary>
|
||||
public class DaylightSavingTimeHistoryRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private Symbol[] _symbols = new[]
|
||||
{
|
||||
QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM),
|
||||
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(2011, 11, 10); //Set Start Date
|
||||
SetEndDate(2011, 11, 11); //Set End Date
|
||||
SetCash(100000); //Set Strategy Cash
|
||||
|
||||
for (int i = 0; i < _symbols.Length; i++)
|
||||
{
|
||||
var symbol = _symbols[i];
|
||||
var history = History<QuoteBar>(symbol, 10, Resolution.Daily);
|
||||
|
||||
var duplications = history
|
||||
.GroupBy(k => k.Time)
|
||||
.Where(g => g.Count() > 1);
|
||||
if (duplications.Any())
|
||||
{
|
||||
var time = duplications.First().Key;
|
||||
throw new Exception($"Duplicated bars were issued for time {time}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <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"},
|
||||
{"Tracking Error", "0"},
|
||||
{"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"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,8 +178,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "79228162514264337593543950335"},
|
||||
{"Return Over Maximum Drawdown", "-15.573"},
|
||||
{"Portfolio Turnover", "2.056"},
|
||||
{"Return Over Maximum Drawdown", "-15.574"},
|
||||
{"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.
|
||||
@@ -40,7 +41,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
SetEndDate(2013, 10, 8);
|
||||
SetCash(100000);
|
||||
|
||||
_eurusd = QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM);
|
||||
_eurusd = QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda);
|
||||
var eurgbp = AddForex("EURGBP", Resolution.Daily);
|
||||
_dataPointsPerSymbol.Add(eurgbp.Symbol, 0);
|
||||
}
|
||||
@@ -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", 48 }
|
||||
{ "EURUSD", 28 }
|
||||
};
|
||||
|
||||
foreach (var kvp in _dataPointsPerSymbol)
|
||||
@@ -141,8 +149,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "5.893"},
|
||||
{"Tracking Error", "0.131"},
|
||||
{"Information Ratio", "5.853"},
|
||||
{"Tracking Error", "0.107"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Fitness Score", "0"},
|
||||
|
||||
@@ -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.
|
||||
@@ -40,7 +41,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
SetEndDate(2013, 10, 8);
|
||||
SetCash(100000);
|
||||
|
||||
_eurusd = QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM);
|
||||
_eurusd = QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda);
|
||||
var eurgbp = AddForex("EURGBP", Resolution.Daily);
|
||||
_dataPointsPerSymbol.Add(eurgbp.Symbol, 0);
|
||||
}
|
||||
@@ -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 }
|
||||
};
|
||||
@@ -145,8 +153,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "5.893"},
|
||||
{"Tracking Error", "0.131"},
|
||||
{"Information Ratio", "5.853"},
|
||||
{"Tracking Error", "0.107"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Fitness Score", "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", "-1018168907"}
|
||||
{"OrderListHash", "-1726463684"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Market;
|
||||
using QuantConnect.Orders;
|
||||
using QuantConnect.Interfaces;
|
||||
|
||||
@@ -60,11 +62,24 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
contract.Symbol.ID.OptionRight == OptionRight.Call &&
|
||||
contract.Symbol.ID.Date == new DateTime(2016, 01, 15))
|
||||
{
|
||||
if (slice.Time.Date == new DateTime(2014, 06, 05) && contract.OpenInterest != 50)
|
||||
var history = History<OpenInterest>(contract.Symbol, TimeSpan.FromDays(1)).ToList();
|
||||
if (history.Count == 0)
|
||||
{
|
||||
throw new Exception("Regression test failed: open interest history request is empty");
|
||||
}
|
||||
|
||||
var security = Securities[contract.Symbol];
|
||||
var openInterestCache = security.Cache.GetData<OpenInterest>();
|
||||
if (openInterestCache == null)
|
||||
{
|
||||
throw new Exception("Regression test failed: current open interest isn't in the security cache");
|
||||
}
|
||||
|
||||
if (slice.Time.Date == new DateTime(2014, 06, 05) && (contract.OpenInterest != 50 || security.OpenInterest != 50))
|
||||
{
|
||||
throw new Exception("Regression test failed: current open interest was not correctly loaded and is not equal to 50");
|
||||
}
|
||||
if (slice.Time.Date == new DateTime(2014, 06, 06) && contract.OpenInterest != 70)
|
||||
if (slice.Time.Date == new DateTime(2014, 06, 06) && (contract.OpenInterest != 70 || security.OpenInterest != 70))
|
||||
{
|
||||
throw new Exception("Regression test failed: current open interest was not correctly loaded and is not equal to 70");
|
||||
}
|
||||
|
||||
@@ -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"},
|
||||
|
||||
188
Algorithm.CSharp/OrderImmutabilityRegressionAlgorithm.cs
Normal file
188
Algorithm.CSharp/OrderImmutabilityRegressionAlgorithm.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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.Interfaces;
|
||||
using QuantConnect.Orders;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression algorithm tests that orders are unchangeable from the QCAlgorithm Layer
|
||||
/// Orders should only be modifiable via their ticket and only in permitted ways
|
||||
/// </summary>
|
||||
/// <meta name="tag" content="backtesting brokerage" />
|
||||
/// <meta name="tag" content="regression test" />
|
||||
/// <meta name="tag" content="options" />
|
||||
public class OrderImmutabilityRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private readonly Symbol _spy = QuantConnect.Symbol.Create("SPY", SecurityType.Equity, Market.USA);
|
||||
private OrderTicket _ticket;
|
||||
private Order _originalOrder;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
|
||||
/// </summary>
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2013, 10, 08); //Set Start Date
|
||||
SetEndDate(2013, 10, 09); //Set End Date
|
||||
SetCash(100000); //Set Strategy Cash
|
||||
AddEquity("SPY", Resolution.Daily);
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
_ticket = LimitOrder(_spy, 10, 100);
|
||||
Debug("Purchased Stock");
|
||||
|
||||
// Here we will show how to correctly change an order, we will then verify at End of Algorithm!
|
||||
// First get the order as it is now, should be a copy, so it wont be updated!
|
||||
_originalOrder = Transactions.GetOrderById(_ticket.OrderId);
|
||||
|
||||
// Create an UpdateOrderRequest and send it to the ticket
|
||||
var updateFields = new UpdateOrderFields { Quantity = 20, Tag = "Pepe", LimitPrice = data[_spy].Low};
|
||||
var response = _ticket.Update(updateFields);
|
||||
|
||||
// Test order time
|
||||
if (_originalOrder.Time != UtcTime)
|
||||
{
|
||||
Error("Order Time should be UtcTime!");
|
||||
throw new Exception("Order Time should be UtcTime!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All order events get pushed through this function
|
||||
/// This function will test that what we get from Transactions is indeed a clone
|
||||
/// The only authentic way to change the order is to change through the order ticket!
|
||||
/// </summary>
|
||||
/// <param name="orderEvent">OrderEvent object that contains all the information about the event</param>
|
||||
public override void OnOrderEvent(OrderEvent orderEvent)
|
||||
{
|
||||
|
||||
// Get the order twice, since they are clones they should NOT be the same
|
||||
var orderV1 = Transactions.GetOrderById(orderEvent.OrderId);
|
||||
var orderV2 = Transactions.GetOrderById(orderEvent.OrderId);
|
||||
|
||||
if (orderV1 == orderV2)
|
||||
{
|
||||
Error("Orders should be clones, hence not equal!");
|
||||
throw new Exception("Orders should be clones, hence not equal!");
|
||||
}
|
||||
|
||||
// Try and manipulate orderV2 using the only external accessor BrokerID, since we
|
||||
// are changing a clone the BrokerIDs should not be the same
|
||||
orderV2.BrokerId.Add("FAKE BROKER ID");
|
||||
var orderV3 = Transactions.GetOrderById(orderEvent.OrderId);
|
||||
|
||||
if (orderV2.BrokerId.SequenceEqual(orderV3.BrokerId))
|
||||
{
|
||||
Error("Broker IDs should not be the same!");
|
||||
throw new Exception("Broker IDs should not be the same!");
|
||||
}
|
||||
|
||||
//Try and manipulate the orderV1 using UpdateOrderRequest
|
||||
//NOTICE: Orders should only be updated through their tickets!
|
||||
var updateFields = new UpdateOrderFields { Quantity = 99, Tag = "Pepe2!" };
|
||||
var updateRequest = new UpdateOrderRequest(DateTime.Now, orderEvent.OrderId, updateFields);
|
||||
orderV1.ApplyUpdateOrderRequest(updateRequest);
|
||||
var orderV4 = Transactions.GetOrderById(orderEvent.OrderId);
|
||||
|
||||
if (orderV4.Quantity == orderV1.Quantity)
|
||||
{
|
||||
Error("Order quantity should not be the same!");
|
||||
throw new Exception("Order quantity should not be the same!");
|
||||
}
|
||||
|
||||
if (orderV4.Tag == orderV1.Tag)
|
||||
{
|
||||
Error("Order tag should not be the same!");
|
||||
throw new Exception("Order tag should not be the same!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will run at End of Algorithm
|
||||
/// We will be using this to check our order was updated!
|
||||
/// </summary>
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
//Get an updated copy of the order and compare to our original
|
||||
var updatedOrder = Transactions.GetOrderById(_ticket.OrderId);
|
||||
|
||||
if (updatedOrder.Quantity == _originalOrder.Quantity)
|
||||
{
|
||||
Error("Quantities should have been updated!");
|
||||
throw new Exception("Quantities should have been updated!");
|
||||
}
|
||||
|
||||
if (updatedOrder.Tag == _originalOrder.Tag)
|
||||
{
|
||||
Error("Tag should have been updated!");
|
||||
throw new Exception("Tag should have been updated!");
|
||||
}
|
||||
}
|
||||
|
||||
/// <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", "-5.591%"},
|
||||
{"Drawdown", "0.000%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "-0.032%"},
|
||||
{"Sharpe Ratio", "-9.862"},
|
||||
{"Probabilistic Sharpe Ratio", "0%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.007"},
|
||||
{"Beta", "-0.582"},
|
||||
{"Annual Standard Deviation", "0.004"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-10.999"},
|
||||
{"Tracking Error", "0.011"},
|
||||
{"Treynor Ratio", "0.067"},
|
||||
{"Total Fees", "$1.00"},
|
||||
{"Fitness Score", "0.007"},
|
||||
{"OrderListHash", "1715759777"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -86,12 +86,12 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Trades", "18"},
|
||||
{"Average Win", "0.88%"},
|
||||
{"Average Loss", "-0.95%"},
|
||||
{"Compounding Annual Return", "292.584%"},
|
||||
{"Compounding Annual Return", "292.522%"},
|
||||
{"Drawdown", "3.400%"},
|
||||
{"Expectancy", "0.204"},
|
||||
{"Net Profit", "1.780%"},
|
||||
{"Sharpe Ratio", "11.819"},
|
||||
{"Probabilistic Sharpe Ratio", "66.758%"},
|
||||
{"Sharpe Ratio", "11.817"},
|
||||
{"Probabilistic Sharpe Ratio", "66.756%"},
|
||||
{"Loss Rate", "38%"},
|
||||
{"Win Rate", "62%"},
|
||||
{"Profit-Loss Ratio", "0.93"},
|
||||
@@ -99,15 +99,15 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Beta", "1.548"},
|
||||
{"Annual Standard Deviation", "0.34"},
|
||||
{"Annual Variance", "0.116"},
|
||||
{"Information Ratio", "17.385"},
|
||||
{"Information Ratio", "17.38"},
|
||||
{"Tracking Error", "0.12"},
|
||||
{"Treynor Ratio", "2.597"},
|
||||
{"Treynor Ratio", "2.596"},
|
||||
{"Total Fees", "$45.00"},
|
||||
{"Fitness Score", "0.986"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "9.332"},
|
||||
{"Return Over Maximum Drawdown", "45.085"},
|
||||
{"Sortino Ratio", "9.326"},
|
||||
{"Return Over Maximum Drawdown", "45.056"},
|
||||
{"Portfolio Turnover", "2.728"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
@@ -122,7 +122,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-809947807"}
|
||||
{"OrderListHash", "-46935513"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,7 +527,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "1237222672"}
|
||||
{"OrderListHash", "-1594146186"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +98,16 @@
|
||||
<IsLinux>false</IsLinux>
|
||||
<IsLinux Condition="'$(IsWindows)' != 'true' AND '$(IsOSX)' != 'true' AND '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">true</IsLinux>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'DebugDocker|AnyCPU'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>portable</DebugType>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<LangVersion>6</LangVersion>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>..\QuantConnect.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<Target Name="PrintRID" BeforeTargets="Build">
|
||||
<Message Text="IsWindows $(IsWindows)" Importance="high" />
|
||||
<Message Text="IsOSX $(IsOSX)" Importance="high" />
|
||||
@@ -132,6 +142,32 @@
|
||||
<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" />
|
||||
<Compile Include="AddRiskManagementAlgorithm.cs" />
|
||||
@@ -148,7 +184,6 @@
|
||||
<Compile Include="Alphas\TriangleExchangeRateArbitrageAlpha.cs" />
|
||||
<Compile Include="Alphas\TripleLeveragedETFPairVolatilityDecayAlpha.cs" />
|
||||
<Compile Include="Alphas\VixDualThrustAlpha.cs" />
|
||||
<Compile Include="AltData\RobintrackHoldingsAlgorithm.cs" />
|
||||
<Compile Include="AltData\CachedAlternativeDataAlgorithm.cs" />
|
||||
<Compile Include="AltData\BenzingaNewsAlgorithm.cs" />
|
||||
<Compile Include="AltData\SECReport8KAlgorithm.cs" />
|
||||
@@ -158,6 +193,7 @@
|
||||
<Compile Include="AltData\TiingoNewsAlgorithm.cs" />
|
||||
<Compile Include="AutomaticIndicatorWarmupDataTypeRegressionAlgorithm.cs" />
|
||||
<Compile Include="AutomaticIndicatorWarmupRegressionAlgorithm.cs" />
|
||||
<Compile Include="BacktestingBrokerageRegressionAlgorithm.cs" />
|
||||
<Compile Include="ExtendedMarketTradingRegressionAlgorithm.cs" />
|
||||
<Compile Include="CoarseTiingoNewsUniverseSelectionAlgorithm.cs" />
|
||||
<Compile Include="DelistedFutureLiquidateRegressionAlgorithm.cs" />
|
||||
@@ -190,6 +226,7 @@
|
||||
<Compile Include="MarginRemainingRegressionAlgorithm.cs" />
|
||||
<Compile Include="NoMarginCallExpectedRegressionAlgorithm.cs" />
|
||||
<Compile Include="ObjectStoreExampleAlgorithm.cs" />
|
||||
<Compile Include="OrderImmutabilityRegressionAlgorithm.cs" />
|
||||
<Compile Include="OrderSubmissionDataRegressionAlgorithm.cs" />
|
||||
<Compile Include="RegisterIndicatorRegressionAlgorithm.cs" />
|
||||
<Compile Include="ScheduledEventsOrderRegressionAlgorithm.cs" />
|
||||
@@ -344,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" />
|
||||
@@ -358,6 +396,7 @@
|
||||
<Compile Include="RegressionAlgorithm.cs" />
|
||||
<Compile Include="RenkoConsolidatorAlgorithm.cs" />
|
||||
<Compile Include="ScheduledEventsAlgorithm.cs" />
|
||||
<Compile Include="ScheduledQueuingAlgorithm.cs" />
|
||||
<Compile Include="StressSymbolsAlgorithm.cs" />
|
||||
<Compile Include="StressSymbols.cs" />
|
||||
<Compile Include="TickDataFilteringAlgorithm.cs" />
|
||||
@@ -417,6 +456,9 @@
|
||||
<Analyzer Include="..\packages\Microsoft.NetFramework.Analyzers.2.9.3\analyzers\dotnet\cs\Microsoft.NetFramework.Analyzers.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.NetFramework.Analyzers.2.9.3\analyzers\dotnet\cs\Microsoft.NetFramework.CSharp.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="DaylightSavingTimeHistoryRegressionAlgorithm.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
|
||||
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"}
|
||||
};
|
||||
}
|
||||
}
|
||||
96
Algorithm.CSharp/ScheduledQueuingAlgorithm.cs
Normal file
96
Algorithm.CSharp/ScheduledQueuingAlgorithm.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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.Algorithm.Framework.Alphas;
|
||||
using QuantConnect.Algorithm.Framework.Execution;
|
||||
using QuantConnect.Algorithm.Framework.Portfolio;
|
||||
using QuantConnect.Algorithm.Framework.Selection;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Data.Fundamental;
|
||||
using QuantConnect.Data.UniverseSelection;
|
||||
using QuantConnect.Interfaces;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
public class TachyonDynamicGearbox : QCAlgorithm
|
||||
{
|
||||
private int numberOfSymbols;
|
||||
private int numberOfSymbolsFine;
|
||||
private Queue<Symbol> queue;
|
||||
private int dequeueSize;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2020, 9, 1);
|
||||
SetEndDate(2020, 9, 2);
|
||||
SetCash(100000);
|
||||
|
||||
numberOfSymbols = 2000;
|
||||
numberOfSymbolsFine = 1000;
|
||||
SetUniverseSelection(new FineFundamentalUniverseSelectionModel(CoarseSelectionFunction, FineSelectionFunction));
|
||||
|
||||
SetPortfolioConstruction(new EqualWeightingPortfolioConstructionModel());
|
||||
|
||||
SetExecution(new ImmediateExecutionModel());
|
||||
|
||||
queue = new Queue<Symbol>();
|
||||
dequeueSize = 100;
|
||||
|
||||
AddEquity("SPY", Resolution.Minute);
|
||||
Schedule.On(DateRules.EveryDay("SPY"), TimeRules.At(0, 0), FillQueue);
|
||||
Schedule.On(DateRules.EveryDay("SPY"), TimeRules.Every(TimeSpan.FromMinutes(60)), TakeFromQueue);
|
||||
}
|
||||
|
||||
public IEnumerable<Symbol> CoarseSelectionFunction(IEnumerable<CoarseFundamental> coarse)
|
||||
{
|
||||
var sortedByDollarVolume = coarse
|
||||
.Where(x => x.HasFundamentalData)
|
||||
.OrderByDescending(x => x.DollarVolume);
|
||||
return sortedByDollarVolume.Take(numberOfSymbols).Select(x => x.Symbol);
|
||||
}
|
||||
|
||||
public IEnumerable<Symbol> FineSelectionFunction(IEnumerable<FineFundamental> fine)
|
||||
{
|
||||
|
||||
var sortedByPeRatio = fine.OrderByDescending(x => x.ValuationRatios.PERatio);
|
||||
var topFine = sortedByPeRatio.Take(numberOfSymbolsFine);
|
||||
return topFine.Select(x => x.Symbol);
|
||||
}
|
||||
|
||||
private void FillQueue() {
|
||||
var securities = ActiveSecurities.Values.Where(x => x.Fundamentals != null);
|
||||
|
||||
// Fill queue with symbols sorted by PE ratio (decreasing order)
|
||||
queue.Clear();
|
||||
var sortedByPERatio = securities.OrderByDescending(x => x.Fundamentals.ValuationRatios.PERatio);
|
||||
foreach (Security security in sortedByPERatio)
|
||||
queue.Enqueue(security.Symbol);
|
||||
}
|
||||
|
||||
private void TakeFromQueue() {
|
||||
List<Symbol> symbols = new List<Symbol>();
|
||||
for (int i = 0; i < Math.Min(dequeueSize, queue.Count); i++)
|
||||
symbols.Add(queue.Dequeue());
|
||||
History(symbols, 10, Resolution.Daily);
|
||||
|
||||
Log("Symbols at " + Time + ": " + string.Join(", ", symbols.Select(x => x.ToString())));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,16 +67,16 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
|
||||
if (dateTime.DayOfWeek == DayOfWeek.Tuesday || dateTime.DayOfWeek == DayOfWeek.Thursday)
|
||||
{
|
||||
yield return QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM);
|
||||
yield return QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda);
|
||||
}
|
||||
else if (dateTime.DayOfWeek == DayOfWeek.Friday)
|
||||
{
|
||||
// given the date/time rules specified in Initialize, this symbol will never be selected (every 6 hours never lands on hour==1)
|
||||
yield return QuantConnect.Symbol.Create("EURGBP", SecurityType.Forex, Market.FXCM);
|
||||
yield return QuantConnect.Symbol.Create("EURGBP", SecurityType.Forex, Market.Oanda);
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return QuantConnect.Symbol.Create("NZDUSD", SecurityType.Forex, Market.FXCM);
|
||||
yield return QuantConnect.Symbol.Create("NZDUSD", SecurityType.Forex, Market.Oanda);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,46 +192,46 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// </summary>
|
||||
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
|
||||
{
|
||||
{"Total Trades", "52"},
|
||||
{"Average Win", "0.27%"},
|
||||
{"Average Loss", "-0.22%"},
|
||||
{"Compounding Annual Return", "41.076%"},
|
||||
{"Drawdown", "1.000%"},
|
||||
{"Expectancy", "0.618"},
|
||||
{"Net Profit", "3.112%"},
|
||||
{"Sharpe Ratio", "5.311"},
|
||||
{"Probabilistic Sharpe Ratio", "90.919%"},
|
||||
{"Loss Rate", "29%"},
|
||||
{"Win Rate", "71%"},
|
||||
{"Profit-Loss Ratio", "1.26"},
|
||||
{"Alpha", "0.31"},
|
||||
{"Beta", "0.054"},
|
||||
{"Annual Standard Deviation", "0.06"},
|
||||
{"Annual Variance", "0.004"},
|
||||
{"Information Ratio", "1.79"},
|
||||
{"Tracking Error", "0.079"},
|
||||
{"Treynor Ratio", "5.952"},
|
||||
{"Total Fees", "$36.83"},
|
||||
{"Fitness Score", "0.67"},
|
||||
{"Kelly Criterion Estimate", "25.099"},
|
||||
{"Kelly Criterion Probability Value", "0.068"},
|
||||
{"Sortino Ratio", "13.102"},
|
||||
{"Return Over Maximum Drawdown", "55.759"},
|
||||
{"Portfolio Turnover", "0.675"},
|
||||
{"Total Insights Generated", "54"},
|
||||
{"Total Insights Closed", "52"},
|
||||
{"Total Insights Analysis Completed", "52"},
|
||||
{"Long Insight Count", "54"},
|
||||
{"Total Trades", "86"},
|
||||
{"Average Win", "0.16%"},
|
||||
{"Average Loss", "-0.10%"},
|
||||
{"Compounding Annual Return", "51.162%"},
|
||||
{"Drawdown", "1.100%"},
|
||||
{"Expectancy", "0.793"},
|
||||
{"Net Profit", "3.748%"},
|
||||
{"Sharpe Ratio", "7.195"},
|
||||
{"Probabilistic Sharpe Ratio", "99.177%"},
|
||||
{"Loss Rate", "31%"},
|
||||
{"Win Rate", "69%"},
|
||||
{"Profit-Loss Ratio", "1.60"},
|
||||
{"Alpha", "0.366"},
|
||||
{"Beta", "0.161"},
|
||||
{"Annual Standard Deviation", "0.055"},
|
||||
{"Annual Variance", "0.003"},
|
||||
{"Information Ratio", "3.061"},
|
||||
{"Tracking Error", "0.07"},
|
||||
{"Treynor Ratio", "2.443"},
|
||||
{"Total Fees", "$33.96"},
|
||||
{"Fitness Score", "0.75"},
|
||||
{"Kelly Criterion Estimate", "23.91"},
|
||||
{"Kelly Criterion Probability Value", "0.076"},
|
||||
{"Sortino Ratio", "42.076"},
|
||||
{"Return Over Maximum Drawdown", "129.046"},
|
||||
{"Portfolio Turnover", "0.751"},
|
||||
{"Total Insights Generated", "55"},
|
||||
{"Total Insights Closed", "53"},
|
||||
{"Total Insights Analysis Completed", "53"},
|
||||
{"Long Insight Count", "55"},
|
||||
{"Short Insight Count", "0"},
|
||||
{"Long/Short Ratio", "100%"},
|
||||
{"Estimated Monthly Alpha Value", "$814596.0814"},
|
||||
{"Total Accumulated Estimated Alpha Value", "$888136.0054"},
|
||||
{"Mean Population Estimated Insight Value", "$17079.5386"},
|
||||
{"Mean Population Direction", "59.6154%"},
|
||||
{"Mean Population Estimated Insight Value", "$16757.2831"},
|
||||
{"Mean Population Direction", "58.4906%"},
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "64.1791%"},
|
||||
{"Rolling Averaged Population Direction", "55.0223%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-1450725184"}
|
||||
{"OrderListHash", "941404943"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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", "-142.421"},
|
||||
{"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"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,29 +197,29 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Trades", "6"},
|
||||
{"Average Win", "0.40%"},
|
||||
{"Average Loss", "-0.86%"},
|
||||
{"Compounding Annual Return", "-17.124%"},
|
||||
{"Compounding Annual Return", "-15.825%"},
|
||||
{"Drawdown", "1.100%"},
|
||||
{"Expectancy", "-0.266"},
|
||||
{"Net Profit", "-0.464%"},
|
||||
{"Sharpe Ratio", "-1.547"},
|
||||
{"Probabilistic Sharpe Ratio", "33.672%"},
|
||||
{"Net Profit", "-0.463%"},
|
||||
{"Sharpe Ratio", "-1.475"},
|
||||
{"Probabilistic Sharpe Ratio", "33.116%"},
|
||||
{"Loss Rate", "50%"},
|
||||
{"Win Rate", "50%"},
|
||||
{"Profit-Loss Ratio", "0.47"},
|
||||
{"Alpha", "-0.21"},
|
||||
{"Beta", "0.104"},
|
||||
{"Annual Standard Deviation", "0.086"},
|
||||
{"Alpha", "-0.196"},
|
||||
{"Beta", "0.123"},
|
||||
{"Annual Standard Deviation", "0.081"},
|
||||
{"Annual Variance", "0.007"},
|
||||
{"Information Ratio", "-4.732"},
|
||||
{"Tracking Error", "0.184"},
|
||||
{"Treynor Ratio", "-1.286"},
|
||||
{"Total Fees", "$12.97"},
|
||||
{"Information Ratio", "-4.271"},
|
||||
{"Tracking Error", "0.174"},
|
||||
{"Treynor Ratio", "-0.972"},
|
||||
{"Total Fees", "$12.99"},
|
||||
{"Fitness Score", "0.031"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "-3.761"},
|
||||
{"Return Over Maximum Drawdown", "-15.539"},
|
||||
{"Portfolio Turnover", "0.499"},
|
||||
{"Sortino Ratio", "-3.46"},
|
||||
{"Return Over Maximum Drawdown", "-14.323"},
|
||||
{"Portfolio Turnover", "0.445"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
@@ -233,7 +233,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "-436429281"}
|
||||
{"OrderListHash", "-304070777"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
}
|
||||
|
||||
var eurUsdSubscription = SubscriptionManager.SubscriptionDataConfigService
|
||||
.GetSubscriptionDataConfigs(QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM),
|
||||
.GetSubscriptionDataConfigs(QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda),
|
||||
includeInternalConfigs: true)
|
||||
.Single();
|
||||
if (!eurUsdSubscription.IsInternalFeed)
|
||||
@@ -100,29 +100,29 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Total Trades", "1"},
|
||||
{"Average Win", "0%"},
|
||||
{"Average Loss", "0%"},
|
||||
{"Compounding Annual Return", "17.116%"},
|
||||
{"Compounding Annual Return", "16.445%"},
|
||||
{"Drawdown", "4.800%"},
|
||||
{"Expectancy", "0"},
|
||||
{"Net Profit", "0.913%"},
|
||||
{"Sharpe Ratio", "0.93"},
|
||||
{"Probabilistic Sharpe Ratio", "48.592%"},
|
||||
{"Sharpe Ratio", "0.903"},
|
||||
{"Probabilistic Sharpe Ratio", "48.314%"},
|
||||
{"Loss Rate", "0%"},
|
||||
{"Win Rate", "0%"},
|
||||
{"Profit-Loss Ratio", "0"},
|
||||
{"Alpha", "0.119"},
|
||||
{"Beta", "0.202"},
|
||||
{"Annual Standard Deviation", "0.161"},
|
||||
{"Annual Variance", "0.026"},
|
||||
{"Alpha", "0.113"},
|
||||
{"Beta", "0.203"},
|
||||
{"Annual Standard Deviation", "0.156"},
|
||||
{"Annual Variance", "0.024"},
|
||||
{"Information Ratio", "0.001"},
|
||||
{"Tracking Error", "0.203"},
|
||||
{"Treynor Ratio", "0.739"},
|
||||
{"Tracking Error", "0.198"},
|
||||
{"Treynor Ratio", "0.697"},
|
||||
{"Total Fees", "$2.60"},
|
||||
{"Fitness Score", "0.044"},
|
||||
{"Fitness Score", "0.041"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "1.683"},
|
||||
{"Return Over Maximum Drawdown", "3.545"},
|
||||
{"Portfolio Turnover", "0.055"},
|
||||
{"Sortino Ratio", "1.617"},
|
||||
{"Return Over Maximum Drawdown", "3.406"},
|
||||
{"Portfolio Turnover", "0.052"},
|
||||
{"Total Insights Generated", "0"},
|
||||
{"Total Insights Closed", "0"},
|
||||
{"Total Insights Analysis Completed", "0"},
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using QuantConnect.Securities;
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
@@ -22,7 +24,10 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
/// <summary>
|
||||
/// The forex symbols.
|
||||
/// </summary>
|
||||
public static HashSet<string> ForexSymbols = new HashSet<string>(Currencies.CurrencyPairs);
|
||||
public static HashSet<string> ForexSymbols = new HashSet<string>(SymbolPropertiesDatabase
|
||||
.FromDataFolder()
|
||||
.GetSymbolPropertiesList(Market.Oanda, SecurityType.Forex)
|
||||
.Select(x => x.Key.Symbol));
|
||||
|
||||
/// <summary>
|
||||
/// The stock symbols.
|
||||
|
||||
142
Algorithm.CSharp/SwitchDataModeRegressionAlgorithm.cs
Normal file
142
Algorithm.CSharp/SwitchDataModeRegressionAlgorithm.cs
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.
|
||||
*
|
||||
*/
|
||||
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
|
||||
namespace QuantConnect.Algorithm.CSharp
|
||||
{
|
||||
/// <summary>
|
||||
/// This regression test algorithm reproduces issue https://github.com/QuantConnect/Lean/issues/4031
|
||||
/// fixed in PR https://github.com/QuantConnect/Lean/pull/4650
|
||||
/// Adjusted data have already been all loaded by the workers so DataNormalizationMode change has no effect in the data itself
|
||||
/// </summary>
|
||||
public class SwitchDataModeRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
|
||||
{
|
||||
private const string UnderlyingTicker = "AAPL";
|
||||
|
||||
private readonly Dictionary<DateTime, decimal?> _expectedCloseValues = new Dictionary<DateTime, decimal?>() {
|
||||
{ new DateTime(2014, 6, 6, 9, 57, 0), 86.04398m},
|
||||
{ new DateTime(2014, 6, 6, 9, 58, 0), 86.05196m},
|
||||
{ new DateTime(2014, 6, 6, 9, 59, 0), 648.29m},
|
||||
{ new DateTime(2014, 6, 6, 10, 0, 0), 647.86m},
|
||||
{ new DateTime(2014, 6, 6, 10, 1, 0), 646.84m},
|
||||
{ new DateTime(2014, 6, 6, 10, 2, 0), 647.64m},
|
||||
{ new DateTime(2014, 6, 6, 10, 3, 0), 646.9m}
|
||||
};
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SetStartDate(2014, 6, 6);
|
||||
SetEndDate(2014, 6, 6);
|
||||
|
||||
var aapl = AddEquity(UnderlyingTicker, Resolution.Minute);
|
||||
}
|
||||
|
||||
public override void OnData(Slice data)
|
||||
{
|
||||
if (Time.Hour == 9 && Time.Minute == 58)
|
||||
{
|
||||
AddOption(UnderlyingTicker);
|
||||
}
|
||||
|
||||
AssertValue(data);
|
||||
}
|
||||
|
||||
public override void OnEndOfAlgorithm()
|
||||
{
|
||||
if (_expectedCloseValues.Count > 0)
|
||||
{
|
||||
throw new Exception($"Not all expected data points were received.");
|
||||
}
|
||||
}
|
||||
|
||||
private void AssertValue(Slice data)
|
||||
{
|
||||
decimal? value;
|
||||
if (_expectedCloseValues.TryGetValue(data.Time, out value))
|
||||
{
|
||||
if (data.Bars.FirstOrDefault().Value?.Close.SmartRounding() != value)
|
||||
{
|
||||
throw new Exception($"Expected tradebar price, expected {value} but was {data.Bars.First().Value.Close.SmartRounding()}");
|
||||
}
|
||||
|
||||
_expectedCloseValues.Remove(data.Time);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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"},
|
||||
{"Tracking Error", "0"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Fitness Score", "0"},
|
||||
{"Kelly Criterion Estimate", "0"},
|
||||
{"Kelly Criterion Probability Value", "0"},
|
||||
{"Sortino Ratio", "0"},
|
||||
{"Return Over Maximum Drawdown", "0"},
|
||||
{"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"}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,8 +152,8 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Beta", "0"},
|
||||
{"Annual Standard Deviation", "0"},
|
||||
{"Annual Variance", "0"},
|
||||
{"Information Ratio", "-31.646"},
|
||||
{"Tracking Error", "0.16"},
|
||||
{"Information Ratio", "-58.133"},
|
||||
{"Tracking Error", "0.173"},
|
||||
{"Treynor Ratio", "0"},
|
||||
{"Total Fees", "$0.00"},
|
||||
{"Fitness Score", "0"},
|
||||
|
||||
@@ -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"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ namespace QuantConnect.Algorithm.CSharp
|
||||
{"Mean Population Magnitude", "0%"},
|
||||
{"Rolling Averaged Population Direction", "0%"},
|
||||
{"Rolling Averaged Population Magnitude", "0%"},
|
||||
{"OrderListHash", "124750474"}
|
||||
{"OrderListHash", "1536869386"}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
@@ -1,64 +0,0 @@
|
||||
# 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 datetime import datetime, timedelta
|
||||
|
||||
from System import *
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Custom.Robintrack import *
|
||||
|
||||
### <summary>
|
||||
### Looks at users holding the stock AAPL at a given point in time
|
||||
### and keeps track of changes in retail investor sentiment.
|
||||
###
|
||||
### We go long if the sentiment increases by 0.5%, and short if it decreases by -0.5%
|
||||
### </summary>
|
||||
class RobintrackHoldingsAlgorithm(QCAlgorithm):
|
||||
|
||||
def Initialize(self):
|
||||
self.lastValue = 0
|
||||
|
||||
self.SetStartDate(2018, 5, 1)
|
||||
self.SetEndDate(2020, 5, 5)
|
||||
self.SetCash(100000)
|
||||
|
||||
self.aapl = self.AddEquity("AAPL", Resolution.Daily).Symbol
|
||||
self.aaplHoldings = self.AddData(RobintrackHoldings, self.aapl).Symbol
|
||||
self.isLong = False
|
||||
|
||||
def OnData(self, data):
|
||||
for kvp in data.Get(RobintrackHoldings):
|
||||
holdings = kvp.Value
|
||||
|
||||
if self.lastValue != 0:
|
||||
percentChange = (holdings.UsersHolding - self.lastValue) / self.lastValue
|
||||
holdingInfo = f"There are {holdings.UsersHolding} unique users holding {kvp.Key.Underlying} - users holding % of U.S. equities universe: {holdings.UniverseHoldingPercent * 100.0}%"
|
||||
|
||||
if percentChange >= 0.005 and not self.isLong:
|
||||
self.Log(f"{self.UtcTime} - Buying AAPL - {holdingInfo}")
|
||||
self.SetHoldings(self.aapl, 0.5)
|
||||
self.isLong = True
|
||||
|
||||
elif percentChange <= -0.005 and self.isLong:
|
||||
self.Log(f"{self.UtcTime} - Shorting AAPL - {holdingInfo}")
|
||||
self.SetHoldings(self.aapl, -0.5)
|
||||
self.isLong = False
|
||||
|
||||
self.lastValue = holdings.UsersHolding;
|
||||
@@ -33,40 +33,39 @@ class BasicTemplateOptionsFilterUniverseAlgorithm(QCAlgorithm):
|
||||
UnderlyingTicker = "GOOG"
|
||||
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2015, 12, 16)
|
||||
self.SetStartDate(2015, 12, 24)
|
||||
self.SetEndDate(2015, 12, 24)
|
||||
self.SetCash(100000)
|
||||
|
||||
equity = self.AddEquity(self.UnderlyingTicker);
|
||||
equity = self.AddEquity(self.UnderlyingTicker)
|
||||
option = self.AddOption(self.UnderlyingTicker)
|
||||
self.option_symbol = option.Symbol
|
||||
self.OptionSymbol = option.Symbol
|
||||
|
||||
# set our strike/expiry filter for this option chain
|
||||
# SetFilter method accepts timedelta objects or integer for days.
|
||||
# The following statements yield the same filtering criteria
|
||||
option.SetFilter(-10, +10, 0, 10)
|
||||
# option.SetFilter(-10, 10, timedelta(0), timedelta(10))
|
||||
# Set our custom universe filter
|
||||
option.SetFilter(self.FilterFunction)
|
||||
|
||||
# use the underlying equity as the benchmark
|
||||
self.SetBenchmark(equity.Symbol)
|
||||
|
||||
def FilterFunction(self, universe):
|
||||
#Expires today, is a call, and is within 10 dollars of the current price
|
||||
universe = universe.WeeklysOnly().Expiration(0, 1)
|
||||
return [symbol for symbol in universe
|
||||
if symbol.ID.OptionRight != OptionRight.Put
|
||||
and -10 < universe.Underlying.Price - symbol.ID.StrikePrice < 10]
|
||||
|
||||
def OnData(self,slice):
|
||||
if self.Portfolio.Invested: return
|
||||
|
||||
for kvp in slice.OptionChains:
|
||||
if kvp.Key != self.option_symbol: continue
|
||||
chain = kvp.Value
|
||||
# find the call options expiring today
|
||||
contracts = [i for i in chain if i.Right == OptionRight.Call and i.Expiry.date() == self.Time.date()]
|
||||
# sorted the contracts by their strike, find the second strike under market price
|
||||
sorted_contracts = [i for i in sorted(contracts, key = lambda x:x.Strike, reverse = True) if i.Strike < chain.Underlying.Price]
|
||||
# if found, trade it
|
||||
if len(sorted_contracts) == 0:
|
||||
self.Log("No call contracts expiring today")
|
||||
return
|
||||
self.MarketOrder(sorted_contracts[1].Symbol, 1)
|
||||
|
||||
if kvp.Key != self.OptionSymbol: continue
|
||||
|
||||
def OnOrderEvent(self, orderEvent):
|
||||
# Order fill event handler. On an order fill update the resulting information is passed to this method.
|
||||
# <param name="orderEvent">Order event details containing details of the evemts</param>
|
||||
self.Log(str(orderEvent))
|
||||
# Get the first call strike under market price expiring today
|
||||
chain = kvp.Value
|
||||
contracts = [option for option in sorted(chain, key = lambda x:x.Strike, reverse = True)
|
||||
if option.Expiry.date() == self.Time.date()
|
||||
and option.Strike < chain.Underlying.Price]
|
||||
|
||||
if contracts:
|
||||
self.MarketOrder(contracts[0].Symbol, 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)
|
||||
179
Algorithm.Python/CustomConsolidatorRegressionAlgorithm.py
Normal file
179
Algorithm.Python/CustomConsolidatorRegressionAlgorithm.py
Normal file
@@ -0,0 +1,179 @@
|
||||
# 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.Common")
|
||||
AddReference("QuantConnect.Algorithm")
|
||||
AddReference("QuantConnect.Indicators")
|
||||
|
||||
from QuantConnect import *
|
||||
from QuantConnect.Python import *
|
||||
from QuantConnect.Algorithm import *
|
||||
from QuantConnect.Data import *
|
||||
from QuantConnect.Data.Market import *
|
||||
from QuantConnect.Data.Consolidators import *
|
||||
from QuantConnect.Indicators import *
|
||||
from System import *
|
||||
from datetime import *
|
||||
|
||||
class CustomConsolidatorRegressionAlgorithm(QCAlgorithm):
|
||||
'''Custom Consolidator Regression Algorithm shows some examples of how to build custom
|
||||
consolidators in Python.'''
|
||||
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2013,10,4)
|
||||
self.SetEndDate(2013,10,11)
|
||||
self.SetCash(100000)
|
||||
self.AddEquity("SPY", Resolution.Minute)
|
||||
|
||||
#Create 5 day QuoteBarConsolidator; set consolidated function; add to subscription manager
|
||||
fiveDayConsolidator = QuoteBarConsolidator(timedelta(days=5))
|
||||
fiveDayConsolidator.DataConsolidated += self.OnQuoteBarDataConsolidated
|
||||
self.SubscriptionManager.AddConsolidator("SPY", fiveDayConsolidator)
|
||||
|
||||
#Create a 3:10PM custom quote bar consolidator
|
||||
timedConsolidator = DailyTimeQuoteBarConsolidator(time(hour=15, minute=10))
|
||||
timedConsolidator.DataConsolidated += self.OnQuoteBarDataConsolidated
|
||||
self.SubscriptionManager.AddConsolidator("SPY", timedConsolidator)
|
||||
|
||||
#Create our entirely custom 2 day quote bar consolidator
|
||||
self.customConsolidator = CustomQuoteBarConsolidator(timedelta(days=2))
|
||||
self.customConsolidator.DataConsolidated += (self.OnQuoteBarDataConsolidated)
|
||||
self.SubscriptionManager.AddConsolidator("SPY", self.customConsolidator)
|
||||
|
||||
#Create an indicator and register a consolidator to it
|
||||
self.movingAverage = SimpleMovingAverage(5)
|
||||
self.customConsolidator2 = CustomQuoteBarConsolidator(timedelta(hours=1))
|
||||
self.RegisterIndicator("SPY", self.movingAverage, self.customConsolidator2)
|
||||
|
||||
|
||||
def OnQuoteBarDataConsolidated(self, sender, bar):
|
||||
'''Function assigned to be triggered by consolidators.
|
||||
Designed to post debug messages to show how the examples work, including
|
||||
which consolidator is posting, as well as its values.
|
||||
|
||||
If using an inherited class and not overwriting OnDataConsolidated
|
||||
we expect to see the super C# class as the sender type.
|
||||
|
||||
Using sender.Period only works when all consolidators have a Period value.
|
||||
'''
|
||||
|
||||
consolidatorInfo = str(type(sender)) + str(sender.Period)
|
||||
|
||||
self.Debug("Bar Type: " + consolidatorInfo)
|
||||
self.Debug("Bar Range: " + bar.Time.ctime() + " - " + bar.EndTime.ctime())
|
||||
self.Debug("Bar value: " + str(bar.Close))
|
||||
|
||||
def OnData(self, slice):
|
||||
test = slice.get_Values()
|
||||
|
||||
if self.customConsolidator.Consolidated and slice.ContainsKey("SPY"):
|
||||
data = slice['SPY']
|
||||
|
||||
if self.movingAverage.IsReady:
|
||||
if data.Value > self.movingAverage.Current.Price:
|
||||
self.SetHoldings("SPY", .5)
|
||||
else :
|
||||
self.SetHoldings("SPY", 0)
|
||||
|
||||
|
||||
|
||||
class DailyTimeQuoteBarConsolidator(QuoteBarConsolidator):
|
||||
'''A custom QuoteBar consolidator that inherits from C# class QuoteBarConsolidator.
|
||||
|
||||
This class shows an example of building on top of an existing consolidator class, it is important
|
||||
to note that this class can leverage the functions of QuoteBarConsolidator but its private fields
|
||||
(_period, _workingbar, etc.) are separate from this Python. For that reason if we want Scan() to work
|
||||
we must overwrite the function with our desired Scan function and trigger OnDataConsolidated().
|
||||
|
||||
For this particular example we implemented the scan method to trigger a consolidated bar
|
||||
at closeTime everyday'''
|
||||
|
||||
def __init__(self, closeTime):
|
||||
self.closeTime = closeTime
|
||||
self.workingBar = None
|
||||
|
||||
def Update(self, data):
|
||||
'''Updates this consolidator with the specified data'''
|
||||
|
||||
#If we don't have bar yet, create one
|
||||
if self.workingBar is None:
|
||||
self.workingBar = QuoteBar(data.Time,data.Symbol,data.Bid,data.LastBidSize,
|
||||
data.Ask,data.LastAskSize)
|
||||
|
||||
#Update bar using QuoteBarConsolidator's AggregateBar()
|
||||
self.AggregateBar(self.workingBar, data)
|
||||
|
||||
|
||||
def Scan(self, time):
|
||||
'''Scans this consolidator to see if it should emit a bar due yet'''
|
||||
|
||||
#If its our desired bar end time take the steps to
|
||||
if time.hour == self.closeTime.hour and time.minute == self.closeTime.minute:
|
||||
|
||||
#Set end time
|
||||
self.workingBar.EndTime = time
|
||||
|
||||
#Emit event using QuoteBarConsolidator's OnDataConsolidated()
|
||||
self.OnDataConsolidated(self.workingBar)
|
||||
|
||||
#Reset the working bar to None
|
||||
self.workingBar = None
|
||||
|
||||
class CustomQuoteBarConsolidator(PythonConsolidator):
|
||||
'''A custom quote bar consolidator that inherits from PythonConsolidator and implements
|
||||
the IDataConsolidator interface, it must implement all of IDataConsolidator. Reference
|
||||
PythonConsolidator.cs and DataConsolidatorPythonWrapper.py for more information.
|
||||
|
||||
This class shows how to implement a consolidator from scratch in Python, this gives us more
|
||||
freedom to determine the behavior of the consolidator but can't leverage any of the built in
|
||||
functions of an inherited class.
|
||||
|
||||
For this example we implemented a Quotebar from scratch'''
|
||||
|
||||
def __init__(self, period):
|
||||
|
||||
#IDataConsolidator required vars for all consolidators
|
||||
self.Consolidated = None #Most recently consolidated piece of data.
|
||||
self.WorkingData = None #Data being currently consolidated
|
||||
self.InputType = QuoteBar #The type consumed by this consolidator
|
||||
self.OutputType = QuoteBar #The type produced by this consolidator
|
||||
|
||||
#Consolidator Variables
|
||||
self.Period = period
|
||||
|
||||
def Update(self, data):
|
||||
'''Updates this consolidator with the specified data'''
|
||||
|
||||
#If we don't have bar yet, create one
|
||||
if self.WorkingData is None:
|
||||
self.WorkingData = QuoteBar(data.Time,data.Symbol,data.Bid,data.LastBidSize,
|
||||
data.Ask,data.LastAskSize,self.Period)
|
||||
|
||||
#Update bar using QuoteBar's update()
|
||||
self.WorkingData.Update(data.Value, data.Bid.Close, data.Ask.Close, 0,
|
||||
data.LastBidSize, data.LastAskSize)
|
||||
|
||||
def Scan(self, time):
|
||||
'''Scans this consolidator to see if it should emit a bar due to time passing'''
|
||||
|
||||
if self.Period is not None and self.WorkingData is not None:
|
||||
if time - self.WorkingData.Time >= self.Period:
|
||||
|
||||
#Trigger the event handler with a copy of self and the data
|
||||
self.OnDataConsolidated(self, self.WorkingData)
|
||||
|
||||
#Set the most recent consolidated piece of data and then clear the workingData
|
||||
self.Consolidated = self.WorkingData
|
||||
self.WorkingData = None
|
||||
@@ -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
|
||||
65
Algorithm.Python/FilterUniverseRegressionAlgorithm.py
Normal file
65
Algorithm.Python/FilterUniverseRegressionAlgorithm.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 datetime import timedelta
|
||||
|
||||
### <summary>
|
||||
### This regression algorithm is for testing a custom Python filter for options
|
||||
### that returns a OptionFilterUniverse.
|
||||
### </summary>
|
||||
### <meta name="tag" content="options" />
|
||||
### <meta name="tag" content="filter selection" />
|
||||
### <meta name="tag" content="regression test" />
|
||||
class FilterUniverseRegressionAlgorithm(QCAlgorithm):
|
||||
UnderlyingTicker = "GOOG"
|
||||
|
||||
def Initialize(self):
|
||||
self.SetStartDate(2015, 12, 24)
|
||||
self.SetEndDate(2015, 12, 24)
|
||||
self.SetCash(100000)
|
||||
|
||||
equity = self.AddEquity(self.UnderlyingTicker)
|
||||
option = self.AddOption(self.UnderlyingTicker)
|
||||
self.OptionSymbol = option.Symbol
|
||||
|
||||
# Set our custom universe filter
|
||||
option.SetFilter(self.FilterFunction)
|
||||
|
||||
# use the underlying equity as the benchmark
|
||||
self.SetBenchmark(equity.Symbol)
|
||||
|
||||
def FilterFunction(self, universe):
|
||||
universe = universe.WeeklysOnly().Strikes(-5, +5).CallsOnly().Expiration(0, 1)
|
||||
return universe
|
||||
|
||||
def OnData(self,slice):
|
||||
if self.Portfolio.Invested: return
|
||||
|
||||
for kvp in slice.OptionChains:
|
||||
|
||||
if kvp.Key != self.OptionSymbol: continue
|
||||
|
||||
chain = kvp.Value
|
||||
contracts = [option for option in sorted(chain, key = lambda x:x.Strike, reverse = True)]
|
||||
|
||||
if contracts:
|
||||
self.MarketOrder(contracts[0].Symbol, 1)
|
||||
|
||||
@@ -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])}")
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user