├── .github ├── issue_template.md ├── pull_request_template.md └── workflows │ └── gh-actions.yml ├── .gitignore ├── LICENSE ├── Lean.DataSource.ThetaData.sln ├── QuantConnect.ThetaData.Tests ├── QuantConnect.DataSource.ThetaData.Tests.csproj ├── TestHelpers.cs ├── TestSetup.cs ├── ThetaDataAdditionalTests.cs ├── ThetaDataDownloaderTests.cs ├── ThetaDataHistoryProviderTests..cs ├── ThetaDataOptionChainProviderTests.cs ├── ThetaDataProviderTests.cs ├── ThetaDataQueueUniverseProviderTests.cs ├── ThetaDataSymbolMapperTests.cs └── config.json ├── QuantConnect.ThetaData ├── Converters │ ├── DateTimeIntJsonConverter.cs │ ├── ThetaDataEndOfDayConverter.cs │ ├── ThetaDataNullStringConverter.cs │ ├── ThetaDataOpenHighLowCloseConverter.cs │ ├── ThetaDataOpenInterestConverter.cs │ ├── ThetaDataPriceConverter.cs │ ├── ThetaDataQuoteConverter.cs │ ├── ThetaDataQuoteListContractConverter.cs │ └── ThetaDataTradeConverter.cs ├── Models │ ├── Common │ │ ├── BaseHeaderResponse.cs │ │ └── BaseResponse.cs │ ├── Enums │ │ ├── ContractSecurityType.cs │ │ ├── SubscriptionPlanType.cs │ │ └── WebSocketHeaderType.cs │ ├── Interfaces │ │ ├── IBaseResponse.cs │ │ └── ISubscriptionPlan.cs │ ├── Rest │ │ ├── EndOfDayReportResponse.cs │ │ ├── OpenHighLowCloseResponse.cs │ │ ├── OpenInterestResponse.cs │ │ ├── PriceResponse.cs │ │ ├── QuoteListContract.cs │ │ ├── QuoteResponse.cs │ │ ├── RequestParameters.cs │ │ └── TradeResponse.cs │ ├── SubscriptionPlans │ │ ├── FreeSubscriptionPlan.cs │ │ ├── ProSubscriptionPlan.cs │ │ ├── StandardSubscriptionPlan.cs │ │ └── ValueSubscriptionPlan.cs │ ├── WebSocket │ │ ├── WebSocketContract.cs │ │ ├── WebSocketHeader.cs │ │ ├── WebSocketQuote.cs │ │ ├── WebSocketResponse.cs │ │ └── WebSocketTrade.cs │ └── Wrappers │ │ └── StopwatchWrapper.cs ├── QuantConnect.DataSource.ThetaData.csproj ├── ThetaDataDownloader.cs ├── ThetaDataExtensions.cs ├── ThetaDataHistoryProvider.cs ├── ThetaDataOptionChainProvider.cs ├── ThetaDataProvider.cs ├── ThetaDataQueueUniverseProvider.cs ├── ThetaDataRestApiClient.cs ├── ThetaDataSymbolMapper.cs └── ThetaDataWebSocketClientWrapper.cs ├── README.md └── thetadata.json /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | #### Expected Behavior 4 | 5 | 6 | #### Actual Behavior 7 | 8 | 9 | #### Potential Solution 10 | 11 | 12 | #### Reproducing the Problem 13 | 14 | 15 | #### System Information 16 | 17 | 18 | #### Checklist 19 | 20 | 21 | - [ ] I have completely filled out this template 22 | - [ ] I have confirmed that this issue exists on the current `master` branch 23 | - [ ] I have confirmed that this is not a duplicate issue by searching [issues](https://github.com/QuantConnect/Lean/issues) 24 | 25 | - [ ] I have provided detailed steps to reproduce the issue 26 | 27 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #### Description 5 | 6 | 7 | #### Related Issue 8 | 9 | 10 | 11 | 12 | 13 | #### Motivation and Context 14 | 15 | 16 | #### Requires Documentation Change 17 | 18 | 19 | #### How Has This Been Tested? 20 | 21 | 22 | 23 | 24 | #### Types of changes 25 | 26 | - [ ] Bug fix (non-breaking change which fixes an issue) 27 | - [ ] Refactor (non-breaking change which improves implementation) 28 | - [ ] Performance (non-breaking change which improves performance. Please add associated performance test and results) 29 | - [ ] New feature (non-breaking change which adds functionality) 30 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 31 | - [ ] Non-functional change (xml comments/documentation/etc) 32 | 33 | #### Checklist: 34 | 35 | 36 | - [ ] My code follows the code style of this project. 37 | - [ ] I have read the **CONTRIBUTING** [document](https://github.com/QuantConnect/Lean/blob/master/CONTRIBUTING.md). 38 | - [ ] I have added tests to cover my changes. 39 | - [ ] All new and existing tests passed. 40 | - [ ] My branch follows the naming convention `bug--` or `feature--` 41 | 42 | 43 | -------------------------------------------------------------------------------- /.github/workflows/gh-actions.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test 2 | 3 | on: 4 | push: 5 | branches: ["*"] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-24.04 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | 14 | - name: Liberate disk space 15 | uses: jlumbroso/free-disk-space@main 16 | with: 17 | tool-cache: true 18 | large-packages: false 19 | docker-images: false 20 | swap-storage: false 21 | 22 | - name: Checkout Lean Same Branch 23 | id: lean-same-branch 24 | uses: actions/checkout@v2 25 | continue-on-error: true 26 | with: 27 | ref: ${{ github.ref }} 28 | repository: QuantConnect/Lean 29 | path: Lean 30 | 31 | - name: Checkout Lean Master 32 | if: steps.lean-same-branch.outcome != 'success' 33 | uses: actions/checkout@v2 34 | with: 35 | repository: QuantConnect/Lean 36 | path: Lean 37 | 38 | - name: Move Lean 39 | run: mv Lean ../Lean 40 | 41 | - name: Run Image 42 | uses: addnab/docker-run-action@v3 43 | with: 44 | image: quantconnect/lean:foundation 45 | options: -v /home/runner/work:/__w --workdir /__w/Lean.DataSource.ThetaData/Lean.DataSource.ThetaData -e QC_THETADATA_USERNAME=${{ secrets.THETADATA_USERNAME }} -e QC_THETADATA_PASSWORD=${{ secrets.THETADATA_PASSWORD }} -e QC_JOB_USER_ID=${{ secrets.QC_JOB_USER_ID }} -e QC_API_ACCESS_TOKEN=${{ secrets.QC_API_ACCESS_TOKEN }} -e QC_JOB_ORGANIZATION_ID=${{ secrets.QC_JOB_ORGANIZATION_ID }} 46 | shell: bash 47 | run: | 48 | # Build QuantConnect.ThetaData 49 | dotnet build ./QuantConnect.ThetaData/QuantConnect.DataSource.ThetaData.csproj /p:Configuration=Release /v:quiet /p:WarningLevel=1 && \ 50 | # Build QuantConnect.ThetaData.Tests 51 | dotnet build ./QuantConnect.ThetaData.Tests/QuantConnect.DataSource.ThetaData.Tests.csproj /p:Configuration=Release /v:quiet /p:WarningLevel=1 && \ 52 | # Run QuantConnect.ThetaData.Tests 53 | dotnet test ./QuantConnect.ThetaData.Tests/bin/Release/QuantConnect.Lean.DataSource.ThetaData.Tests.dll 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | *.pyc 7 | 8 | # Visual Studio Project Items: 9 | *.suo 10 | 11 | # Precompiled Headers 12 | *.gch 13 | *.pch 14 | 15 | # Libraries 16 | *.lib 17 | *.a 18 | *.la 19 | *.lo 20 | 21 | # Shared objects (inc. Windows DLLs) 22 | #*.dll 23 | *.so 24 | *.so.* 25 | *.dylib 26 | 27 | # Executables 28 | */bin/*.exe 29 | *.out 30 | *.app 31 | *.i*86 32 | *.x86_64 33 | *.hex 34 | 35 | # QC Cloud Setup Bash Files 36 | *.sh 37 | # Include docker launch scripts for Mac/Linux 38 | !run_docker.sh 39 | !research/run_docker_notebook.sh 40 | 41 | # QC Config Files: 42 | # config.json 43 | 44 | # QC-C-Specific 45 | *Engine/bin/Debug/cache/data/*.zip 46 | */obj/* 47 | */bin/* 48 | *Docker/* 49 | */Docker/* 50 | *Algorithm.Python/Lib/* 51 | */[Ee]xtensions/* 52 | !**/Libraries/* 53 | 54 | # C Debug Binaries 55 | *.pdb 56 | 57 | ## Ignore Visual Studio temporary files, build results, and 58 | ## files generated by popular Visual Studio add-ons. 59 | 60 | # User-specific files 61 | *.suo 62 | *.user 63 | *.userosscache 64 | *.sln.docstates 65 | *.userprefs 66 | 67 | # Build results 68 | [Dd]ebug/ 69 | [Dd]ebugPublic/ 70 | [Rr]elease/ 71 | [Rr]eleases/ 72 | x64/ 73 | x86/ 74 | .vs/ 75 | build/ 76 | bld/ 77 | [Bb]in/ 78 | [Oo]bj/ 79 | 80 | # Roslyn cache directories 81 | *.ide/ 82 | 83 | # MSTest test Results 84 | [Tt]est[Rr]esult*/ 85 | [Bb]uild[Ll]og.* 86 | 87 | #NUNIT 88 | *.VisualState.xml 89 | TestResult.xml 90 | 91 | # Build Results of an ATL Project 92 | [Dd]ebugPS/ 93 | [Rr]eleasePS/ 94 | dlldata.c 95 | 96 | *_i.c 97 | *_p.c 98 | *_i.h 99 | *.ilk 100 | *.meta 101 | *.obj 102 | *.pch 103 | *.pdb 104 | *.pgc 105 | *.pgd 106 | *.rsp 107 | *.sbr 108 | *.tlb 109 | *.tli 110 | *.tlh 111 | *.tmp 112 | *.tmp_proj 113 | *.log 114 | *.vspscc 115 | *.vssscc 116 | .builds 117 | *.pidb 118 | *.svclog 119 | *.scc 120 | 121 | # Chutzpah Test files 122 | _Chutzpah* 123 | 124 | # Visual C++ cache files 125 | ipch/ 126 | *.aps 127 | *.ncb 128 | *.opensdf 129 | *.sdf 130 | *.cachefile 131 | 132 | # Visual Studio profiler 133 | *.psess 134 | *.vsp 135 | *.vspx 136 | 137 | # TFS 2012 Local Workspace 138 | $tf/ 139 | 140 | # Guidance Automation Toolkit 141 | *.gpState 142 | 143 | # ReSharper is a .NET coding add-in 144 | _ReSharper*/ 145 | *.[Rr]e[Ss]harper 146 | *.DotSettings 147 | *.DotSettings.user 148 | 149 | # JustCode is a .NET coding addin-in 150 | .JustCode 151 | 152 | # TeamCity is a build add-in 153 | _TeamCity* 154 | 155 | # DotCover is a Code Coverage Tool 156 | *.dotCover 157 | 158 | # NCrunch 159 | _NCrunch_* 160 | .*crunch*.local.xml 161 | 162 | # MightyMoose 163 | *.mm.* 164 | AutoTest.Net/ 165 | 166 | # Web workbench (sass) 167 | .sass-cache/ 168 | 169 | # Installshield output folder 170 | [Ee]xpress/ 171 | 172 | # JetBrains Rider 173 | .idea/ 174 | 175 | # DocProject is a documentation generator add-in 176 | DocProject/buildhelp/ 177 | DocProject/Help/*.HxT 178 | DocProject/Help/*.HxC 179 | DocProject/Help/*.hhc 180 | DocProject/Help/*.hhk 181 | DocProject/Help/*.hhp 182 | DocProject/Help/Html2 183 | DocProject/Help/html 184 | 185 | # Click-Once directory 186 | publish/ 187 | 188 | # Publish Web Output 189 | *.[Pp]ublish.xml 190 | *.azurePubxml 191 | # TODO: Comment the next line if you want to checkin your web deploy settings 192 | # but database connection strings (with potential passwords) will be unencrypted 193 | *.pubxml 194 | *.publishproj 195 | 196 | # NuGet Packages 197 | *.nupkg 198 | !LocalPackages/* 199 | # The packages folder can be ignored because of Package Restore 200 | **/packages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/packages/build/ 203 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 204 | #!**/packages/repositories.config 205 | # ignore sln level nuget 206 | .nuget/ 207 | !.nuget/NuGet.config 208 | 209 | # Windows Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Windows Store app package directory 214 | AppPackages/ 215 | 216 | # Others 217 | *.Cache 218 | ClientBin/ 219 | [Ss]tyle[Cc]op.* 220 | ~$* 221 | *~ 222 | *.dbmdl 223 | *.dbproj.schemaview 224 | *.pfx 225 | *.publishsettings 226 | node_modules/ 227 | bower_components/ 228 | 229 | # RIA/Silverlight projects 230 | Generated_Code/ 231 | 232 | # Backup & report files from converting an old project file 233 | # to a newer Visual Studio version. Backup files are not needed, 234 | # because we have git ;-) 235 | _UpgradeReport_Files/ 236 | Backup*/ 237 | UpgradeLog*.XML 238 | UpgradeLog*.htm 239 | 240 | # SQL Server files 241 | *.mdf 242 | *.ldf 243 | 244 | # Business Intelligence projects 245 | *.rdl.data 246 | *.bim.layout 247 | *.bim_*.settings 248 | 249 | # Microsoft Fakes 250 | FakesAssemblies/ 251 | 252 | # Test Runner 253 | testrunner/ 254 | 255 | # Meld original diff files 256 | *.orig 257 | 258 | # Output chart data 259 | Charts/ 260 | 261 | # NCrunch files 262 | *.ncrunchsolution 263 | *.ncrunchproject 264 | 265 | # QuantConnect plugin files 266 | QuantConnectProjects.xml 267 | Launcher/Plugins/* 268 | /ApiPython/dist 269 | /ApiPython/quantconnect.egg-info 270 | /ApiPython/quantconnect.egg-info/* 271 | 272 | QuantConnect.Lean.sln.DotSettings* 273 | 274 | #User notebook files 275 | Research/Notebooks 276 | 277 | #Docker result files 278 | Results/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Lean.DataSource.ThetaData.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.002.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuantConnect.DataSource.ThetaData", "QuantConnect.ThetaData\QuantConnect.DataSource.ThetaData.csproj", "{BB6FBD65-4AB0-4DF8-8BFE-731BFEC5F33D}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuantConnect.DataSource.ThetaData.Tests", "QuantConnect.ThetaData.Tests\QuantConnect.DataSource.ThetaData.Tests.csproj", "{F93E08F9-9961-4DBC-9959-EFA00775252A}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "QuantConnect.Tests", "..\Lean\Tests\QuantConnect.Tests.csproj", "{9A666189-E7DD-43DA-95DF-419E3E5363F3}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {BB6FBD65-4AB0-4DF8-8BFE-731BFEC5F33D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {BB6FBD65-4AB0-4DF8-8BFE-731BFEC5F33D}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {BB6FBD65-4AB0-4DF8-8BFE-731BFEC5F33D}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {BB6FBD65-4AB0-4DF8-8BFE-731BFEC5F33D}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {F93E08F9-9961-4DBC-9959-EFA00775252A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {F93E08F9-9961-4DBC-9959-EFA00775252A}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {F93E08F9-9961-4DBC-9959-EFA00775252A}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {F93E08F9-9961-4DBC-9959-EFA00775252A}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {9A666189-E7DD-43DA-95DF-419E3E5363F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {9A666189-E7DD-43DA-95DF-419E3E5363F3}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {9A666189-E7DD-43DA-95DF-419E3E5363F3}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {9A666189-E7DD-43DA-95DF-419E3E5363F3}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {155C3765-EC51-49A5-99BF-856714A46247} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData.Tests/QuantConnect.DataSource.ThetaData.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Release 4 | AnyCPU 5 | net9.0 6 | false 7 | UnitTest 8 | bin\$(Configuration)\ 9 | QuantConnect.Lean.DataSource.ThetaData.Tests 10 | QuantConnect.Lean.DataSource.ThetaData.Tests 11 | QuantConnect.Lean.DataSource.ThetaData.Tests 12 | QuantConnect.Lean.DataSource.ThetaData.Tests 13 | false 14 | 15 | 16 | 17 | 18 | 19 | 20 | all 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | PreserveNewest 37 | 38 | 39 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData.Tests/TestHelpers.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using System; 17 | using NodaTime; 18 | using System.Linq; 19 | using NUnit.Framework; 20 | using QuantConnect.Data; 21 | using QuantConnect.Util; 22 | using QuantConnect.Tests; 23 | using Microsoft.CodeAnalysis; 24 | using QuantConnect.Securities; 25 | using QuantConnect.Data.Market; 26 | using System.Collections.Generic; 27 | 28 | namespace QuantConnect.Lean.DataSource.ThetaData.Tests 29 | { 30 | public static class TestHelpers 31 | { 32 | /// 33 | /// Represents the time zone used by ThetaData, which returns time in the New York (EST) Time Zone with daylight savings time. 34 | /// 35 | /// 36 | /// 37 | /// 38 | private static DateTimeZone TimeZoneThetaData = TimeZones.NewYork; 39 | 40 | public static void ValidateHistoricalBaseData(IEnumerable history, Resolution resolution, TickType tickType, DateTime startDate, DateTime endDate, Symbol requestedSymbol = null) 41 | { 42 | Assert.IsNotNull(history); 43 | Assert.IsNotEmpty(history); 44 | 45 | if (resolution < Resolution.Daily) 46 | { 47 | Assert.That(history.First().Time.Date, Is.EqualTo(startDate.ConvertFromUtc(TimeZoneThetaData).Date)); 48 | Assert.That(history.Last().Time.Date, Is.EqualTo(endDate.ConvertFromUtc(TimeZoneThetaData).Date)); 49 | } 50 | else 51 | { 52 | Assert.That(history.First().Time.Date, Is.GreaterThanOrEqualTo(startDate.ConvertFromUtc(TimeZoneThetaData).Date)); 53 | Assert.That(history.Last().Time.Date, Is.LessThanOrEqualTo(endDate.ConvertFromUtc(TimeZoneThetaData).Date)); 54 | } 55 | 56 | switch (tickType) 57 | { 58 | case TickType.Trade when resolution != Resolution.Tick: 59 | AssertTradeBars(history.Select(x => x as TradeBar), requestedSymbol, resolution.ToTimeSpan()); 60 | break; 61 | case TickType.Trade when requestedSymbol.SecurityType != SecurityType.Index: 62 | AssertTradeTickBars(history.Select(x => x as Tick), requestedSymbol); 63 | break; 64 | case TickType.Trade when requestedSymbol.SecurityType == SecurityType.Index: 65 | AssertIndexTradeTickBars(history.Select(x => x as Tick), requestedSymbol); 66 | break; 67 | case TickType.Quote when resolution == Resolution.Tick: 68 | AssertQuoteTickBars(history.Select(x => x as Tick), requestedSymbol); 69 | break; 70 | case TickType.Quote: 71 | AssertQuoteBars(history.Select(t => t as QuoteBar), requestedSymbol, resolution.ToTimeSpan()); 72 | break; 73 | } 74 | } 75 | 76 | public static void AssertTradeTickBars(IEnumerable ticks, Symbol symbol = null) 77 | { 78 | foreach (var tick in ticks) 79 | { 80 | if (symbol != null) 81 | { 82 | Assert.That(tick.Symbol, Is.EqualTo(symbol)); 83 | } 84 | 85 | Assert.That(tick.Price, Is.GreaterThan(0)); 86 | Assert.That(tick.Value, Is.GreaterThan(0)); 87 | Assert.IsNotEmpty(tick.SaleCondition); 88 | } 89 | } 90 | 91 | public static void AssertIndexTradeTickBars(IEnumerable ticks, Symbol symbol = null) 92 | { 93 | foreach (var tick in ticks) 94 | { 95 | if (symbol != null) 96 | { 97 | Assert.That(tick.Symbol, Is.EqualTo(symbol)); 98 | } 99 | 100 | Assert.That(tick.Price, Is.GreaterThan(0)); 101 | } 102 | } 103 | 104 | public static void AssertQuoteTickBars(IEnumerable ticks, Symbol symbol = null) 105 | { 106 | foreach (var tick in ticks) 107 | { 108 | if (symbol != null) 109 | { 110 | Assert.That(tick.Symbol, Is.EqualTo(symbol)); 111 | } 112 | 113 | Assert.That(tick.AskPrice, Is.GreaterThan(0)); 114 | Assert.That(tick.AskSize, Is.GreaterThan(0)); 115 | Assert.That(tick.BidPrice, Is.GreaterThan(0)); 116 | Assert.That(tick.BidSize, Is.GreaterThan(0)); 117 | Assert.That(tick.DataType, Is.EqualTo(MarketDataType.Tick)); 118 | Assert.That(tick.Time, Is.GreaterThan(default(DateTime))); 119 | Assert.That(tick.EndTime, Is.GreaterThan(default(DateTime))); 120 | Assert.IsNotEmpty(tick.SaleCondition); 121 | } 122 | } 123 | 124 | public static void AssertQuoteBars(IEnumerable ticks, Symbol symbol, TimeSpan resolutionInTimeSpan) 125 | { 126 | foreach (var tick in ticks) 127 | { 128 | if (symbol != null) 129 | { 130 | Assert.That(tick.Symbol, Is.EqualTo(symbol)); 131 | } 132 | 133 | Assert.That(tick.Ask.Open, Is.GreaterThan(0)); 134 | Assert.That(tick.Ask.High, Is.GreaterThan(0)); 135 | Assert.That(tick.Ask.Low, Is.GreaterThan(0)); 136 | Assert.That(tick.Ask.Close, Is.GreaterThan(0)); 137 | 138 | Assert.That(tick.Bid.Open, Is.GreaterThan(0)); 139 | Assert.That(tick.Bid.High, Is.GreaterThan(0)); 140 | Assert.That(tick.Bid.Low, Is.GreaterThan(0)); 141 | Assert.That(tick.Bid.Close, Is.GreaterThan(0)); 142 | 143 | Assert.That(tick.Close, Is.GreaterThan(0)); 144 | Assert.That(tick.High, Is.GreaterThan(0)); 145 | Assert.That(tick.Low, Is.GreaterThan(0)); 146 | Assert.That(tick.Open, Is.GreaterThan(0)); 147 | Assert.That(tick.Price, Is.GreaterThan(0)); 148 | Assert.That(tick.Value, Is.GreaterThan(0)); 149 | Assert.That(tick.DataType, Is.EqualTo(MarketDataType.QuoteBar)); 150 | Assert.That(tick.EndTime, Is.GreaterThan(default(DateTime))); 151 | Assert.That(tick.Time, Is.GreaterThan(default(DateTime))); 152 | 153 | Assert.That(tick.LastAskSize, Is.GreaterThan(0)); 154 | Assert.That(tick.LastBidSize, Is.GreaterThan(0)); 155 | Assert.That(tick.Period, Is.EqualTo(resolutionInTimeSpan)); 156 | } 157 | } 158 | 159 | public static void AssertTradeBars(IEnumerable tradeBars, Symbol symbol, TimeSpan period) 160 | { 161 | foreach (var tradeBar in tradeBars) 162 | { 163 | Assert.That(tradeBar.Symbol, Is.EqualTo(symbol)); 164 | Assert.That(tradeBar.Period, Is.EqualTo(period)); 165 | Assert.That(tradeBar.Open, Is.GreaterThan(0)); 166 | Assert.That(tradeBar.High, Is.GreaterThan(0)); 167 | Assert.That(tradeBar.Low, Is.GreaterThan(0)); 168 | Assert.That(tradeBar.Close, Is.GreaterThan(0)); 169 | Assert.That(tradeBar.Price, Is.GreaterThan(0)); 170 | Assert.That(tradeBar.Volume, Is.GreaterThanOrEqualTo(0)); 171 | Assert.That(tradeBar.Time, Is.GreaterThan(default(DateTime))); 172 | Assert.That(tradeBar.EndTime, Is.GreaterThan(default(DateTime))); 173 | } 174 | } 175 | 176 | public static HistoryRequest CreateHistoryRequest(Symbol symbol, Resolution resolution, TickType tickType, DateTime startDateTime, DateTime endDateTime, 177 | SecurityExchangeHours exchangeHours = null, DateTimeZone dataTimeZone = null, bool includeExtendedMarketHours = true) 178 | { 179 | if (exchangeHours == null) 180 | { 181 | exchangeHours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType); 182 | } 183 | 184 | if (dataTimeZone == null) 185 | { 186 | dataTimeZone = TimeZones.NewYork; 187 | } 188 | 189 | var dataType = LeanData.GetDataType(resolution, tickType); 190 | return new HistoryRequest( 191 | startDateTime.ConvertToUtc(exchangeHours.TimeZone), 192 | endDateTime.ConvertToUtc(exchangeHours.TimeZone), 193 | dataType, 194 | symbol, 195 | resolution, 196 | exchangeHours, 197 | dataTimeZone, 198 | resolution, 199 | includeExtendedMarketHours, 200 | false, 201 | DataNormalizationMode.Raw, 202 | tickType 203 | ); 204 | } 205 | 206 | public static Symbol CreateSymbol(string ticker, SecurityType securityType, OptionRight? optionRight = null, decimal? strikePrice = null, DateTime? expirationDate = null, string market = Market.USA) 207 | { 208 | switch (securityType) 209 | { 210 | case SecurityType.Equity: 211 | case SecurityType.Index: 212 | return Symbol.Create(ticker, securityType, market); 213 | case SecurityType.Option: 214 | var underlyingEquitySymbol = Symbol.Create(ticker, SecurityType.Equity, market); 215 | return Symbol.CreateOption(underlyingEquitySymbol, market, OptionStyle.American, optionRight.Value, strikePrice.Value, expirationDate.Value); 216 | case SecurityType.IndexOption: 217 | var underlyingIndexSymbol = Symbol.Create(ticker, SecurityType.Index, market); 218 | return Symbol.CreateOption(underlyingIndexSymbol, market, OptionStyle.American, optionRight.Value, strikePrice.Value, expirationDate.Value); 219 | case SecurityType.FutureOption: 220 | var underlyingFuture = Symbols.CreateFutureSymbol(ticker, expirationDate.Value); 221 | return Symbols.CreateFutureOptionSymbol(underlyingFuture, optionRight.Value, strikePrice.Value, expirationDate.Value); 222 | default: 223 | throw new NotSupportedException($"The security type '{securityType}' is not supported."); 224 | } 225 | } 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData.Tests/TestSetup.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * 8 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | using System; 18 | using System.IO; 19 | using NUnit.Framework; 20 | using System.Collections; 21 | using QuantConnect.Logging; 22 | using QuantConnect.Configuration; 23 | 24 | namespace QuantConnect.Lean.DataSource.ThetaData.Tests 25 | { 26 | [SetUpFixture] 27 | public class TestSetup 28 | { 29 | [OneTimeSetUp] 30 | public void GlobalSetup() 31 | { 32 | Log.DebuggingEnabled = true; 33 | Log.LogHandler = new CompositeLogHandler(); 34 | Log.Trace("TestSetup(): starting..."); 35 | ReloadConfiguration(); 36 | } 37 | 38 | private static void ReloadConfiguration() 39 | { 40 | // nunit 3 sets the current folder to a temp folder we need it to be the test bin output folder 41 | var dir = TestContext.CurrentContext.TestDirectory; 42 | Environment.CurrentDirectory = dir; 43 | Directory.SetCurrentDirectory(dir); 44 | // reload config from current path 45 | Config.Reset(); 46 | 47 | var environment = Environment.GetEnvironmentVariables(); 48 | foreach (DictionaryEntry entry in environment) 49 | { 50 | var envKey = entry.Key.ToString(); 51 | var value = entry.Value.ToString(); 52 | 53 | if (envKey.StartsWith("QC_")) 54 | { 55 | var key = envKey.Substring(3).Replace("_", "-").ToLower(); 56 | 57 | Log.Trace($"TestSetup(): Updating config setting '{key}' from environment var '{envKey}'"); 58 | Config.Set(key, value); 59 | } 60 | } 61 | 62 | // resets the version among other things 63 | Globals.Reset(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData.Tests/ThetaDataAdditionalTests.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using System; 17 | using NUnit.Framework; 18 | using System.Collections.Generic; 19 | 20 | namespace QuantConnect.Lean.DataSource.ThetaData.Tests; 21 | 22 | [TestFixture] 23 | public class ThetaDataAdditionalTests 24 | { 25 | [Test] 26 | public void GenerateDateRangesWithNinetyDaysInterval() 27 | { 28 | var intervalDays = 90; 29 | var startDate = new DateTime(2020, 07, 18); 30 | var endDate = new DateTime(2021, 01, 14); 31 | 32 | var expectedRanges = new List<(DateTime startDate, DateTime endDate)> 33 | { 34 | (new DateTime(2020, 07, 18), new DateTime(2020, 10, 16)), 35 | (new DateTime(2020, 10, 17), new DateTime(2021, 01, 14)), 36 | }; 37 | 38 | var actualRanges = new List<(DateTime startDate, DateTime endDate)>(ThetaDataExtensions.GenerateDateRangesWithInterval(startDate, endDate, intervalDays)); 39 | 40 | Assert.AreEqual(expectedRanges.Count, actualRanges.Count, "The number of ranges should match."); 41 | 42 | for (int i = 0; i < expectedRanges.Count; i++) 43 | { 44 | Assert.AreEqual(expectedRanges[i].startDate, actualRanges[i].startDate, $"Start date mismatch at index {i}"); 45 | Assert.AreEqual(expectedRanges[i].endDate, actualRanges[i].endDate, $"End date mismatch at index {i}"); 46 | } 47 | } 48 | 49 | [Test] 50 | public void GenerateDateRangesWithOneDayInterval() 51 | { 52 | var intervalDays = 1; 53 | 54 | var startDate = new DateTime(2024, 07, 26); 55 | var endDate = new DateTime(2024, 07, 30); 56 | 57 | var expectedRanges = new List<(DateTime startDate, DateTime endDate)> 58 | { 59 | (new DateTime(2024, 07, 26), new DateTime(2024, 07, 27)), 60 | (new DateTime(2024, 07, 28), new DateTime(2024, 07, 29)), 61 | (new DateTime(2024, 07, 30), new DateTime(2024, 07, 30)) 62 | }; 63 | 64 | var actualRanges = new List<(DateTime startDate, DateTime endDate)>(ThetaDataExtensions.GenerateDateRangesWithInterval(startDate, endDate, intervalDays)); 65 | 66 | Assert.AreEqual(expectedRanges.Count, actualRanges.Count, "The number of ranges should match."); 67 | 68 | for (int i = 0; i < expectedRanges.Count; i++) 69 | { 70 | Assert.AreEqual(expectedRanges[i].startDate, actualRanges[i].startDate, $"Start date mismatch at index {i}"); 71 | Assert.AreEqual(expectedRanges[i].endDate, actualRanges[i].endDate, $"End date mismatch at index {i}"); 72 | } 73 | } 74 | 75 | [Test] 76 | public void GenerateDateRangesWithInterval_ShouldHandleSameStartEndDate() 77 | { 78 | DateTime startDate = new DateTime(2025, 2, 1); 79 | DateTime endDate = new DateTime(2025, 2, 1); 80 | 81 | var ranges = new List<(DateTime startDate, DateTime endDate)>( 82 | ThetaDataExtensions.GenerateDateRangesWithInterval(startDate, endDate, 1) 83 | ); 84 | 85 | Assert.AreEqual(1, ranges.Count, "There should be no date ranges generated."); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData.Tests/ThetaDataDownloaderTests.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 17 | using System; 18 | using System.Linq; 19 | using NUnit.Framework; 20 | using QuantConnect.Util; 21 | 22 | namespace QuantConnect.Lean.DataSource.ThetaData.Tests 23 | { 24 | 25 | [TestFixture] 26 | [Explicit("This test requires the ThetaData terminal to be running in order to execute properly.")] 27 | public class ThetaDataDownloaderTests 28 | { 29 | private ThetaDataDownloader _dataDownloader; 30 | 31 | [SetUp] 32 | public void SetUp() 33 | { 34 | _dataDownloader = new(); 35 | } 36 | 37 | [TearDown] 38 | public void TearDown() 39 | { 40 | if (_dataDownloader != null) 41 | { 42 | _dataDownloader.DisposeSafely(); 43 | } 44 | } 45 | 46 | [TestCase("AAPL", OptionRight.Call, 170, "2024/03/28", Resolution.Tick, TickType.Quote, "2024/03/19", "2024/03/28")] 47 | [TestCase("AAPL", OptionRight.Call, 170, "2024/03/28", Resolution.Tick, TickType.Trade, "2024/03/19", "2024/03/28")] 48 | [TestCase("AAPL", OptionRight.Put, 170, "2024/03/28", Resolution.Second, TickType.Quote, "2024/03/19", "2024/03/28")] 49 | [TestCase("AAPL", OptionRight.Put, 170, "2024/03/28", Resolution.Second, TickType.Trade, "2024/03/19", "2024/03/28")] 50 | [TestCase("AAPL", OptionRight.Call, 170, "2024/03/28", Resolution.Hour, TickType.Quote, "2024/03/19", "2024/03/28")] 51 | [TestCase("AAPL", OptionRight.Call, 170, "2024/03/28", Resolution.Hour, TickType.Trade, "2024/03/19", "2024/03/28")] 52 | [TestCase("AAPL", OptionRight.Put, 170, "2024/03/28", Resolution.Daily, TickType.Quote, "2024/01/18", "2024/03/28")] 53 | [TestCase("AAPL", OptionRight.Call, 170, "2024/03/28", Resolution.Daily, TickType.Trade, "2024/01/18", "2024/03/28")] 54 | [TestCase("AAPL", OptionRight.Put, 170, "2024/03/28", Resolution.Daily, TickType.OpenInterest, "2024/01/18", "2024/03/28")] 55 | public void DownloadsOptionHistoricalData(string ticker, OptionRight optionRight, decimal strikePrice, DateTime expirationDate, Resolution resolution, TickType tickType, DateTime startDate, DateTime endDate) 56 | { 57 | var symbol = TestHelpers.CreateSymbol(ticker, SecurityType.Option, optionRight, strikePrice, expirationDate); 58 | 59 | var parameters = new DataDownloaderGetParameters(symbol, resolution, startDate, endDate, tickType); 60 | 61 | var downloadedHistoricalData = _dataDownloader.Get(parameters); 62 | 63 | TestHelpers.ValidateHistoricalBaseData(downloadedHistoricalData, resolution, tickType, startDate, endDate, symbol); 64 | } 65 | 66 | [TestCase("AAPL", Resolution.Daily, TickType.Quote, "2024/01/18", "2024/03/28")] 67 | public void DownloadsCanonicalOptionHistoricalData(string ticker, Resolution resolution, TickType tickType, DateTime startDate, DateTime endDate) 68 | { 69 | var symbol = Symbol.CreateCanonicalOption(TestHelpers.CreateSymbol(ticker, SecurityType.Equity)); 70 | 71 | var parameters = new DataDownloaderGetParameters(symbol, resolution, startDate, endDate, tickType); 72 | 73 | var downloadedData = _dataDownloader.Get(parameters); 74 | 75 | TestHelpers.ValidateHistoricalBaseData(downloadedData, resolution, tickType, startDate, endDate); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData.Tests/ThetaDataOptionChainProviderTests.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using System; 17 | using System.Linq; 18 | using NUnit.Framework; 19 | using QuantConnect.Util; 20 | using QuantConnect.Tests; 21 | using QuantConnect.Securities; 22 | using System.Collections.Generic; 23 | using QuantConnect.Lean.DataSource.ThetaData.Models.SubscriptionPlans; 24 | 25 | namespace QuantConnect.Lean.DataSource.ThetaData.Tests 26 | { 27 | [TestFixture] 28 | [Explicit("This test requires the ThetaData terminal to be running in order to execute properly.")] 29 | public class ThetaDataOptionChainProviderTests 30 | { 31 | private ThetaDataOptionChainProvider _thetaDataOptionChainProvider; 32 | 33 | [SetUp] 34 | public void SetUp() 35 | { 36 | var userSubscription = new ProSubscriptionPlan(); 37 | _thetaDataOptionChainProvider = new(new ThetaDataSymbolMapper(), new ThetaDataRestApiClient(userSubscription.RateGate)); 38 | } 39 | 40 | private static IEnumerable UnderlyingSymbols 41 | { 42 | get 43 | { 44 | TestGlobals.Initialize(); 45 | yield return Symbol.Create("XEO", SecurityType.Index, Market.USA); 46 | yield return Symbol.Create("DJX", SecurityType.Index, Market.USA); 47 | } 48 | } 49 | 50 | [TestCaseSource(nameof(UnderlyingSymbols))] 51 | public void GetOptionContractList(Symbol symbol) 52 | { 53 | var referenceDate = new DateTime(2024, 03, 28); 54 | var optionChain = _thetaDataOptionChainProvider.GetOptionContractList(symbol, referenceDate).ToList(); 55 | 56 | Assert.That(optionChain, Is.Not.Null.And.Not.Empty); 57 | 58 | // Multiple strikes 59 | var strikes = optionChain.Select(x => x.ID.StrikePrice).Distinct().ToList(); 60 | Assert.That(strikes, Has.Count.GreaterThan(1).And.All.GreaterThan(0)); 61 | 62 | // Multiple expirations 63 | var expirations = optionChain.Select(x => x.ID.Date).Distinct().ToList(); 64 | Assert.That(expirations, Has.Count.GreaterThan(1)); 65 | 66 | // All contracts have the same underlying 67 | var underlying = symbol.Underlying ?? symbol; 68 | Assert.That(optionChain.Select(x => x.Underlying), Is.All.EqualTo(underlying)); 69 | } 70 | 71 | [TestCase(Futures.Indices.SP500EMini, OptionRight.Call, 100, "2024/06/21")] 72 | public void GetFutureOptionContractListShouldReturnNothing(string ticker, OptionRight optionRight, decimal strikePrice, DateTime expiryDate) 73 | { 74 | var symbol = TestHelpers.CreateSymbol(ticker, SecurityType.FutureOption, optionRight, strikePrice, expiryDate); 75 | 76 | var optionChain = _thetaDataOptionChainProvider.GetOptionContractList(symbol, expiryDate).ToList(); 77 | 78 | Assert.IsEmpty(optionChain); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData.Tests/ThetaDataProviderTests.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using System; 17 | using System.Linq; 18 | using NUnit.Framework; 19 | using System.Threading; 20 | using QuantConnect.Data; 21 | using QuantConnect.Tests; 22 | using QuantConnect.Logging; 23 | using System.Threading.Tasks; 24 | using QuantConnect.Data.Market; 25 | using System.Collections.Generic; 26 | using System.Collections.Concurrent; 27 | using QuantConnect.Lean.Engine.DataFeeds.Enumerators; 28 | 29 | namespace QuantConnect.Lean.DataSource.ThetaData.Tests 30 | { 31 | [TestFixture] 32 | [Explicit("This test requires the ThetaData terminal to be running in order to execute properly.")] 33 | public class ThetaDataProviderTests 34 | { 35 | private ThetaDataProvider _thetaDataProvider; 36 | private CancellationTokenSource _cancellationTokenSource; 37 | 38 | [SetUp] 39 | public void SetUp() 40 | { 41 | TestGlobals.Initialize(); 42 | _thetaDataProvider = new(); 43 | _cancellationTokenSource = new(); 44 | } 45 | 46 | [TearDown] 47 | public void TearDown() 48 | { 49 | _cancellationTokenSource.Dispose(); 50 | 51 | if (_thetaDataProvider != null) 52 | { 53 | _thetaDataProvider.Dispose(); 54 | } 55 | } 56 | 57 | [TestCase("AAPL", SecurityType.Equity, Resolution.Second, 0, "2024/08/16")] 58 | [TestCase("AAPL", SecurityType.Option, Resolution.Second, 215, "2024/08/16")] 59 | [TestCase("VIX", SecurityType.Index, Resolution.Second, 0, "2024/08/16")] 60 | public void CanSubscribeAndUnsubscribeOnSecondResolution(string ticker, SecurityType securityType, Resolution resolution, decimal strikePrice, DateTime expiryDate = default) 61 | { 62 | var configs = GetSubscriptionDataConfigs(ticker, securityType, resolution, strikePrice, expiryDate); 63 | 64 | Assert.That(configs, Is.Not.Empty); 65 | 66 | var dataFromEnumerator = new Dictionary() { { typeof(TradeBar), 0 }, { typeof(QuoteBar), 0 } }; 67 | 68 | Action callback = (dataPoint) => 69 | { 70 | if (dataPoint == null) 71 | { 72 | return; 73 | } 74 | 75 | switch (dataPoint) 76 | { 77 | case TradeBar _: 78 | dataFromEnumerator[typeof(TradeBar)] += 1; 79 | break; 80 | case QuoteBar _: 81 | dataFromEnumerator[typeof(QuoteBar)] += 1; 82 | break; 83 | }; 84 | }; 85 | 86 | foreach (var config in configs) 87 | { 88 | ProcessFeed(_thetaDataProvider.Subscribe(config, (sender, args) => 89 | { 90 | var dataPoint = ((NewDataAvailableEventArgs)args).DataPoint; 91 | Log.Trace($"{dataPoint}. Time span: {dataPoint.Time} - {dataPoint.EndTime}"); 92 | }), _cancellationTokenSource.Token, callback: callback); 93 | } 94 | 95 | Thread.Sleep(TimeSpan.FromSeconds(25)); 96 | 97 | Log.Trace("Unsubscribing symbols"); 98 | foreach (var config in configs) 99 | { 100 | _thetaDataProvider.Unsubscribe(config); 101 | } 102 | 103 | Thread.Sleep(TimeSpan.FromSeconds(5)); 104 | 105 | _cancellationTokenSource.Cancel(); 106 | 107 | Log.Trace($"{nameof(ThetaDataProviderTests)}.{nameof(CanSubscribeAndUnsubscribeOnSecondResolution)}: ***** Summary *****"); 108 | Log.Trace($"Input parameters: ticker:{ticker} | securityType:{securityType} | resolution:{resolution}"); 109 | 110 | foreach (var data in dataFromEnumerator) 111 | { 112 | Log.Trace($"[{data.Key}] = {data.Value}"); 113 | } 114 | 115 | if (securityType != SecurityType.Index) 116 | { 117 | Assert.Greater(dataFromEnumerator[typeof(QuoteBar)], 0); 118 | } 119 | // The ThetaData returns TradeBar seldom. Perhaps should find more relevant ticker. 120 | Assert.GreaterOrEqual(dataFromEnumerator[typeof(TradeBar)], 0); 121 | } 122 | 123 | [TestCase("AAPL", SecurityType.Equity)] 124 | [TestCase("SPX", SecurityType.Index)] 125 | public void MultipleSubscriptionOnOptionContractsTickResolution(string ticker, SecurityType securityType) 126 | { 127 | var minReturnResponse = 5; 128 | var obj = new object(); 129 | var cancellationTokenSource = new CancellationTokenSource(); 130 | var resetEvent = new AutoResetEvent(false); 131 | var underlyingSymbol = TestHelpers.CreateSymbol(ticker, securityType); 132 | var configs = _thetaDataProvider.LookupSymbols(underlyingSymbol, false).SelectMany(x => GetSubscriptionTickDataConfigs(x)).Take(250).ToList(); 133 | 134 | var incomingSymbolDataByTickType = new ConcurrentDictionary<(Symbol, TickType), int>(); 135 | 136 | Action callback = (dataPoint) => 137 | { 138 | if (dataPoint == null) 139 | { 140 | return; 141 | } 142 | 143 | var tick = dataPoint as Tick; 144 | 145 | lock (obj) 146 | { 147 | switch (tick.TickType) 148 | { 149 | case TickType.Trade: 150 | incomingSymbolDataByTickType[(tick.Symbol, tick.TickType)] += 1; 151 | break; 152 | case TickType.Quote: 153 | incomingSymbolDataByTickType[(tick.Symbol, tick.TickType)] += 1; 154 | break; 155 | }; 156 | } 157 | }; 158 | 159 | foreach (var config in configs) 160 | { 161 | incomingSymbolDataByTickType.TryAdd((config.Symbol, config.TickType), 0); 162 | ProcessFeed(_thetaDataProvider.Subscribe(config, (sender, args) => 163 | { 164 | var dataPoint = ((NewDataAvailableEventArgs)args).DataPoint; 165 | Log.Trace($"{dataPoint}. Time span: {dataPoint.Time} - {dataPoint.EndTime}"); 166 | }), 167 | cancellationTokenSource.Token, 168 | 300, 169 | callback: callback, 170 | throwExceptionCallback: () => cancellationTokenSource.Cancel()); 171 | } 172 | 173 | resetEvent.WaitOne(TimeSpan.FromMinutes(1), cancellationTokenSource.Token); 174 | 175 | Log.Trace("Unsubscribing symbols"); 176 | foreach (var config in configs) 177 | { 178 | _thetaDataProvider.Unsubscribe(config); 179 | } 180 | 181 | resetEvent.WaitOne(TimeSpan.FromSeconds(20), cancellationTokenSource.Token); 182 | 183 | var symbolVolatilities = incomingSymbolDataByTickType.Where(kv => kv.Value > 0).ToList(); 184 | 185 | Log.Debug($"CancellationToken: {_cancellationTokenSource.Token.IsCancellationRequested}"); 186 | 187 | Assert.IsNotEmpty(symbolVolatilities); 188 | Assert.That(symbolVolatilities.Count, Is.GreaterThan(minReturnResponse)); 189 | 190 | cancellationTokenSource.Cancel(); 191 | } 192 | 193 | private static IEnumerable GetSubscriptionDataConfigs(string ticker, SecurityType securityType, Resolution resolution, decimal strikePrice, DateTime expiry, 194 | OptionRight optionRight = OptionRight.Call, string market = Market.USA) 195 | { 196 | var symbol = TestHelpers.CreateSymbol(ticker, securityType, optionRight, strikePrice, expiry, market); 197 | foreach (var subscription in GetSubscriptionDataConfigs(symbol, resolution)) 198 | { 199 | yield return subscription; 200 | } 201 | } 202 | private static IEnumerable GetSubscriptionDataConfigs(Symbol symbol, Resolution resolution) 203 | { 204 | yield return GetSubscriptionDataConfig(symbol, resolution); 205 | yield return GetSubscriptionDataConfig(symbol, resolution); 206 | } 207 | 208 | public static IEnumerable GetSubscriptionTickDataConfigs(Symbol symbol) 209 | { 210 | yield return new SubscriptionDataConfig(GetSubscriptionDataConfig(symbol, Resolution.Tick), tickType: TickType.Trade); 211 | yield return new SubscriptionDataConfig(GetSubscriptionDataConfig(symbol, Resolution.Tick), tickType: TickType.Quote); 212 | } 213 | 214 | private static SubscriptionDataConfig GetSubscriptionDataConfig(Symbol symbol, Resolution resolution) 215 | { 216 | return new SubscriptionDataConfig( 217 | typeof(T), 218 | symbol, 219 | resolution, 220 | TimeZones.Utc, 221 | TimeZones.Utc, 222 | true, 223 | extendedHours: false, 224 | false); 225 | } 226 | 227 | private Task ProcessFeed( 228 | IEnumerator enumerator, 229 | CancellationToken cancellationToken, 230 | int cancellationTokenDelayMilliseconds = 100, 231 | Action callback = null, 232 | Action throwExceptionCallback = null) 233 | { 234 | return Task.Factory.StartNew(() => 235 | { 236 | try 237 | { 238 | while (enumerator.MoveNext() && !cancellationToken.IsCancellationRequested) 239 | { 240 | BaseData tick = enumerator.Current; 241 | 242 | if (tick != null) 243 | { 244 | callback?.Invoke(tick); 245 | } 246 | 247 | cancellationToken.WaitHandle.WaitOne(TimeSpan.FromMilliseconds(cancellationTokenDelayMilliseconds)); 248 | } 249 | } 250 | catch (Exception ex) 251 | { 252 | Log.Debug($"{nameof(ThetaDataProviderTests)}.{nameof(ProcessFeed)}.Exception: {ex.Message}"); 253 | throw; 254 | } 255 | }, cancellationToken).ContinueWith(task => 256 | { 257 | if (throwExceptionCallback != null) 258 | { 259 | throwExceptionCallback(); 260 | } 261 | Log.Debug("The throwExceptionCallback is null."); 262 | }, TaskContinuationOptions.OnlyOnFaulted); 263 | } 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData.Tests/ThetaDataQueueUniverseProviderTests.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using System; 17 | using System.Linq; 18 | using NUnit.Framework; 19 | using QuantConnect.Lean.Engine.DataFeeds; 20 | 21 | namespace QuantConnect.Lean.DataSource.ThetaData.Tests 22 | { 23 | [TestFixture] 24 | [Explicit("This test requires the ThetaData terminal to be running in order to execute properly.")] 25 | public class ThetaDataQueueUniverseProviderTests 26 | { 27 | private TestableThetaDataProvider _thetaDataProvider; 28 | 29 | [SetUp] 30 | public void SetUp() 31 | { 32 | _thetaDataProvider = new TestableThetaDataProvider(); 33 | } 34 | 35 | private static Symbol[] OptionChainTestCases => 36 | new[] 37 | { 38 | Symbol.Create("SPY", SecurityType.Equity, Market.USA), 39 | Symbol.Create("SPX", SecurityType.Index, Market.USA), 40 | } 41 | .Select(underlying => new[] { underlying, Symbol.CreateCanonicalOption(underlying) }) 42 | .SelectMany(x => x) 43 | .ToArray(); 44 | 45 | [TestCaseSource(nameof(OptionChainTestCases))] 46 | public void GetsOptionChain(Symbol symbol) 47 | { 48 | var date = new DateTime(2014, 10, 7); 49 | _thetaDataProvider.TimeProviderInstance.SetCurrentTimeUtc(date); 50 | var optionChain = _thetaDataProvider.LookupSymbols(symbol, true).ToList(); 51 | 52 | Assert.That(optionChain, Is.Not.Null.And.Not.Empty); 53 | 54 | var expectedOptionType = symbol.SecurityType; 55 | if (!expectedOptionType.IsOption()) 56 | { 57 | expectedOptionType = expectedOptionType == SecurityType.Equity ? SecurityType.Option : SecurityType.IndexOption; 58 | } 59 | Assert.IsTrue(optionChain.All(x => x.SecurityType == expectedOptionType)); 60 | Assert.IsTrue(optionChain.All(x => x.ID.Date.Date >= date)); 61 | } 62 | 63 | 64 | private class TestableThetaDataProvider : ThetaDataProvider 65 | { 66 | public ManualTimeProvider TimeProviderInstance = new ManualTimeProvider(); 67 | 68 | protected override ITimeProvider TimeProvider => TimeProviderInstance; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData.Tests/ThetaDataSymbolMapperTests.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using System; 17 | using NUnit.Framework; 18 | using QuantConnect.Brokerages; 19 | using QuantConnect.Lean.DataSource.ThetaData.Models.Enums; 20 | 21 | namespace QuantConnect.Lean.DataSource.ThetaData.Tests 22 | { 23 | [TestFixture] 24 | public class ThetaDataSymbolMapperTests 25 | { 26 | private ISymbolMapper _symbolMapper = new ThetaDataSymbolMapper(); 27 | 28 | [TestCase("JD", SecurityType.Equity, null, null, null, "JD", null, null, null)] 29 | [TestCase("SPEUFP2I", SecurityType.Index, null, null, null, "SPEUFP2I", null, null, null)] 30 | [TestCase("AAPL", SecurityType.Option, OptionRight.Call, 22, "2024/03/08", "AAPL", "C", "22000", "20240308")] 31 | [TestCase("9QE", SecurityType.Option, OptionRight.Put, 100, "2026/02/02", "9QE", "P", "100000", "20260202")] 32 | [TestCase("9QE", SecurityType.Option, OptionRight.Put, 123, "2026/02/02", "9QE", "P", "123000", "20260202")] 33 | [TestCase("SPXW", SecurityType.IndexOption, OptionRight.Call, 6700, "2022/09/30", "SPXW", "C", "6700000", "20220930")] 34 | [TestCase("NDX", SecurityType.IndexOption, OptionRight.Call, 1650, "2013/01/19", "NDX", "C", "1650000", "20130119")] 35 | public void GetDataProviderOptionTicker(string ticker, SecurityType securityType, OptionRight? optionRight, decimal? strikePrice, DateTime? expirationDate, 36 | string expectedTicker, string expectedOptionRight, string expectedStrike, string expectedExpirationDate) 37 | { 38 | var leanSymbol = TestHelpers.CreateSymbol(ticker, securityType, optionRight, strikePrice, expirationDate); 39 | 40 | var dataProviderContract = _symbolMapper.GetBrokerageSymbol(leanSymbol).Split(','); 41 | 42 | Assert.That(dataProviderContract[0], Is.EqualTo(expectedTicker)); 43 | 44 | if (securityType.IsOption()) 45 | { 46 | Assert.That(dataProviderContract[1], Is.EqualTo(expectedExpirationDate)); 47 | Assert.That(dataProviderContract[2], Is.EqualTo(expectedStrike)); 48 | Assert.That(dataProviderContract[3], Is.EqualTo(expectedOptionRight)); 49 | } 50 | } 51 | 52 | [TestCase("AAPL", ContractSecurityType.Option, "C", 22000, "20240308", Market.USA, OptionRight.Call, 22, "2024/03/08")] 53 | [TestCase("AAPL", ContractSecurityType.Option, "P", 1000000, "20240303", Market.USA, OptionRight.Put, 1000, "2024/03/03")] 54 | [TestCase("AAPL", ContractSecurityType.Equity, "", 0, "", Market.USA, null, null, null)] 55 | [TestCase("INTL", ContractSecurityType.Equity, "", 0, "", Market.USA, null, null, null)] 56 | public void GetLeanSymbol( 57 | string dataProviderTicker, 58 | ContractSecurityType dataProviderContractSecurityType, 59 | string dataProviderOptionRight, 60 | decimal dataProviderStrike, 61 | string dataProviderExpirationDate, 62 | string expectedMarket, 63 | OptionRight? expectedOptionRight = null, 64 | decimal? expectedStrikePrice = null, 65 | DateTime? expectedExpiryDateTime = null) 66 | { 67 | var leanSymbol = (_symbolMapper as ThetaDataSymbolMapper) 68 | .GetLeanSymbol(dataProviderTicker, dataProviderContractSecurityType, dataProviderExpirationDate, dataProviderStrike, dataProviderOptionRight); 69 | 70 | var expectedLeanSymbol = 71 | CreateSymbol(dataProviderContractSecurityType, dataProviderTicker, expectedOptionRight, expectedStrikePrice, expectedExpiryDateTime, expectedMarket); 72 | 73 | Assert.That(leanSymbol, Is.EqualTo(expectedLeanSymbol)); 74 | } 75 | 76 | private Symbol CreateSymbol(ContractSecurityType contractSecurityType, string ticker, OptionRight? optionRight, decimal? strikePrice, DateTime? expirationDate, string market = Market.USA) 77 | { 78 | switch (contractSecurityType) 79 | { 80 | case ContractSecurityType.Option: 81 | return TestHelpers.CreateSymbol(ticker, SecurityType.Option, optionRight, strikePrice, expirationDate, market); 82 | case ContractSecurityType.Equity: 83 | return TestHelpers.CreateSymbol(ticker, SecurityType.Equity, market: market); 84 | default: 85 | throw new NotSupportedException($"The contract security type '{contractSecurityType}' is not supported."); 86 | } 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData.Tests/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "data-folder": "../../../../Lean/Data/", 3 | "thetadata-subscription-plan": "Pro" 4 | } -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Converters/DateTimeIntJsonConverter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using System.Globalization; 18 | using Newtonsoft.Json.Linq; 19 | 20 | namespace QuantConnect.Lean.DataSource.ThetaData.Converters; 21 | 22 | /// 23 | /// Converts a ThetaData Int DateTime presention to 24 | /// 25 | public class DateTimeIntJsonConverter : JsonConverter 26 | { 27 | /// 28 | /// Determines whether this instance can convert the specified object type. 29 | /// 30 | /// Type of the object. 31 | /// 32 | /// true if this instance can convert the specified object type; otherwise, false. 33 | /// 34 | public override bool CanConvert(Type objectType) 35 | { 36 | if (objectType == typeof(DateTime) || objectType == typeof(DateTime?)) 37 | { 38 | return true; 39 | } 40 | 41 | return false; 42 | } 43 | 44 | /// 45 | /// Reads the JSON representation of the object. 46 | /// 47 | /// The to read from. 48 | /// Type of the object. 49 | /// The existing value of object being read. 50 | /// The calling serializer. 51 | /// The object value. 52 | public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) 53 | { 54 | JToken token = JToken.Load(reader); 55 | if (token.Type != JTokenType.Integer) 56 | return null; 57 | 58 | string? dateText = reader.Value?.ToString(); 59 | 60 | return DateTime.ParseExact(dateText!, "yyyyMMdd", CultureInfo.InvariantCulture); 61 | } 62 | 63 | /// 64 | /// Writes the JSON representation of the object. 65 | /// 66 | /// The to write to. 67 | /// The value. 68 | /// The calling serializer. 69 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) 70 | { 71 | throw new NotImplementedException(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Converters/ThetaDataEndOfDayConverter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using Newtonsoft.Json.Linq; 18 | using System.Globalization; 19 | using QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | 21 | namespace QuantConnect.Lean.DataSource.ThetaData.Converters; 22 | 23 | public class ThetaDataEndOfDayConverter : JsonConverter 24 | { 25 | /// 26 | /// Gets a value indicating whether this can write JSON. 27 | /// 28 | /// true if this can write JSON; otherwise, false. 29 | public override bool CanWrite => false; 30 | 31 | /// 32 | /// Gets a value indicating whether this can read JSON. 33 | /// 34 | /// true if this can read JSON; otherwise, false. 35 | public override bool CanRead => true; 36 | 37 | public override EndOfDayReportResponse ReadJson(JsonReader reader, Type objectType, EndOfDayReportResponse existingValue, bool hasExistingValue, JsonSerializer serializer) 38 | { 39 | var token = JToken.Load(reader); 40 | if (token.Type != JTokenType.Array || token.Count() != 17) throw new Exception($"{nameof(ThetaDataEndOfDayConverter)}.{nameof(ReadJson)}: Invalid token type or count. Expected a JSON array with exactly four elements."); 41 | 42 | existingValue = new EndOfDayReportResponse( 43 | reportGeneratedTimeMilliseconds: token[0]!.Value(), 44 | lastTradeTimeMilliseconds: token[1]!.Value(), 45 | open: token[2]!.Value(), 46 | high: token[3]!.Value(), 47 | low: token[4]!.Value(), 48 | close: token[5]!.Value(), 49 | volume: token[6]!.Value(), 50 | amountTrades: token[7]!.Value(), 51 | bidSize: token[8]!.Value(), 52 | bidExchange: token[9]!.Value(), 53 | bidPrice: token[10]!.Value(), 54 | bidCondition: token[11]!.Value() ?? string.Empty, 55 | askSize: token[12]!.Value(), 56 | askExchange: token[13]!.Value(), 57 | askPrice: token[14]!.Value(), 58 | askCondition: token[15]!.Value() ?? string.Empty, 59 | date: DateTime.ParseExact(token[16]!.ToString(), "yyyyMMdd", CultureInfo.InvariantCulture) 60 | ); 61 | 62 | return existingValue; 63 | } 64 | 65 | public override void WriteJson(JsonWriter writer, EndOfDayReportResponse value, JsonSerializer serializer) 66 | { 67 | throw new NotImplementedException(); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Converters/ThetaDataNullStringConverter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using Newtonsoft.Json.Linq; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Converters; 20 | 21 | /// 22 | /// Converts the string value "null" to null during JSON deserialization. 23 | /// 24 | public class ThetaDataNullStringConverter : JsonConverter 25 | { 26 | /// 27 | /// Determines whether this instance can convert the specified object type. 28 | /// 29 | /// Type of the object. 30 | /// 31 | /// true if this instance can convert the specified object type; otherwise, false. 32 | /// 33 | public override bool CanConvert(Type objectType) 34 | { 35 | return objectType == typeof(string); 36 | } 37 | 38 | /// 39 | /// Reads the JSON representation of the object. 40 | /// 41 | /// The Newtonsoft.Json.JsonReader to read from. 42 | /// Type of the object. 43 | /// The existing value of object being read. 44 | /// The calling serializer. 45 | /// The object value. 46 | public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) 47 | { 48 | JToken token = JToken.Load(reader); 49 | if (token.Type == JTokenType.String && (string?)token == "null") 50 | return null; 51 | return token.ToObject(); 52 | } 53 | 54 | /// 55 | /// Writes the JSON representation of the object. 56 | /// 57 | /// The Newtonsoft.Json.JsonWriter to write to. 58 | /// The value. 59 | /// The calling serializer. 60 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) 61 | { 62 | writer.WriteValue(value); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Converters/ThetaDataOpenHighLowCloseConverter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using Newtonsoft.Json.Linq; 18 | using QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 19 | using System.Globalization; 20 | 21 | namespace QuantConnect.Lean.DataSource.ThetaData.Converters; 22 | 23 | /// 24 | /// JSON converter to convert ThetaData OpenHighLowClose 25 | /// 26 | public class ThetaDataOpenHighLowCloseConverter : JsonConverter 27 | { 28 | /// 29 | /// Gets a value indicating whether this can write JSON. 30 | /// 31 | /// true if this can write JSON; otherwise, false. 32 | public override bool CanWrite => false; 33 | 34 | /// 35 | /// Gets a value indicating whether this can read JSON. 36 | /// 37 | /// true if this can read JSON; otherwise, false. 38 | public override bool CanRead => true; 39 | 40 | /// 41 | /// Writes the JSON representation of the object. 42 | /// 43 | /// The to write to. 44 | /// The value. 45 | /// The calling serializer. 46 | public override void WriteJson(JsonWriter writer, OpenHighLowCloseResponse value, JsonSerializer serializer) 47 | { 48 | throw new NotSupportedException(); 49 | } 50 | 51 | /// 52 | /// Reads the JSON representation of the object. 53 | /// 54 | /// The to read from. 55 | /// Type of the object. 56 | /// The existing value of object being read. 57 | /// The existing value has a value. 58 | /// The calling serializer. 59 | /// The object value. 60 | public override OpenHighLowCloseResponse ReadJson(JsonReader reader, Type objectType, OpenHighLowCloseResponse existingValue, bool hasExistingValue, JsonSerializer serializer) 61 | { 62 | var token = JToken.Load(reader); 63 | if (token.Type != JTokenType.Array || token.Count() != 8) throw new Exception($"{nameof(ThetaDataQuoteConverter)}.{nameof(ReadJson)}: Invalid token type or count. Expected a JSON array with exactly four elements."); 64 | 65 | return new OpenHighLowCloseResponse( 66 | timeMilliseconds: token[0]!.Value(), 67 | open: token[1]!.Value(), 68 | high: token[2]!.Value(), 69 | low: token[3]!.Value(), 70 | close: token[4]!.Value(), 71 | volume: token[5]!.Value(), 72 | count: token[6]!.Value(), 73 | date: DateTime.ParseExact(token[7]!.ToString(), "yyyyMMdd", CultureInfo.InvariantCulture)); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Converters/ThetaDataOpenInterestConverter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using Newtonsoft.Json.Linq; 18 | using System.Globalization; 19 | using QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | 21 | namespace QuantConnect.Lean.DataSource.ThetaData.Converters; 22 | 23 | public class ThetaDataOpenInterestConverter : JsonConverter 24 | { 25 | /// 26 | /// Gets a value indicating whether this can write JSON. 27 | /// 28 | /// true if this can write JSON; otherwise, false. 29 | public override bool CanWrite => false; 30 | 31 | /// 32 | /// Gets a value indicating whether this can read JSON. 33 | /// 34 | /// true if this can read JSON; otherwise, false. 35 | public override bool CanRead => true; 36 | 37 | /// 38 | /// Reads the JSON representation of the object. 39 | /// 40 | /// The to read from. 41 | /// Type of the object. 42 | /// The existing value of object being read. 43 | /// The existing value has a value. 44 | /// The calling serializer. 45 | /// The object value. 46 | public override OpenInterestResponse ReadJson(JsonReader reader, Type objectType, OpenInterestResponse existingValue, bool hasExistingValue, JsonSerializer serializer) 47 | { 48 | var token = JToken.Load(reader); 49 | if (token.Type != JTokenType.Array || token.Count() != 3) throw new Exception($"{nameof(ThetaDataOpenInterestConverter)}.{nameof(ReadJson)}: Invalid token type or count. Expected a JSON array with exactly four elements."); 50 | 51 | return new OpenInterestResponse( 52 | timeMilliseconds: token[0]!.Value(), 53 | openInterest: token[1]!.Value(), 54 | date: DateTime.ParseExact(token[2]!.ToString(), "yyyyMMdd", CultureInfo.InvariantCulture) 55 | ); 56 | } 57 | 58 | /// 59 | /// Writes the JSON representation of the object. 60 | /// 61 | /// The to write to. 62 | /// The value. 63 | /// The calling serializer. 64 | public override void WriteJson(JsonWriter writer, OpenInterestResponse value, JsonSerializer serializer) 65 | { 66 | throw new NotImplementedException(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Converters/ThetaDataPriceConverter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | 17 | using Newtonsoft.Json; 18 | using Newtonsoft.Json.Linq; 19 | using System.Globalization; 20 | using QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 21 | 22 | namespace QuantConnect.Lean.DataSource.ThetaData.Converters; 23 | 24 | /// 25 | /// JSON converter to convert ThetaData Price 26 | /// 27 | public class ThetaDataPriceConverter : JsonConverter 28 | { 29 | /// 30 | /// Gets a value indicating whether this can write JSON. 31 | /// 32 | /// true if this can write JSON; otherwise, false. 33 | public override bool CanWrite => false; 34 | 35 | /// 36 | /// Gets a value indicating whether this can read JSON. 37 | /// 38 | /// true if this can read JSON; otherwise, false. 39 | public override bool CanRead => true; 40 | 41 | /// 42 | /// Writes the JSON representation of the object. 43 | /// 44 | /// The to write to. 45 | /// The value. 46 | /// The calling serializer. 47 | public override void WriteJson(JsonWriter writer, PriceResponse value, JsonSerializer serializer) 48 | { 49 | throw new NotSupportedException(); 50 | } 51 | 52 | /// 53 | /// Reads the JSON representation of the object. 54 | /// 55 | /// The to read from. 56 | /// Type of the object. 57 | /// The existing value of object being read. 58 | /// The existing value has a value. 59 | /// The calling serializer. 60 | /// The object value. 61 | public override PriceResponse ReadJson(JsonReader reader, Type objectType, PriceResponse existingValue, bool hasExistingValue, JsonSerializer serializer) 62 | { 63 | var token = JToken.Load(reader); 64 | if (token.Type != JTokenType.Array || token.Count() != 3) throw new Exception($"{nameof(ThetaDataQuoteConverter)}.{nameof(ReadJson)}: Invalid token type or count. Expected a JSON array with exactly four elements."); 65 | 66 | return new PriceResponse( 67 | timeMilliseconds: token[0]!.Value(), 68 | price: token[1]!.Value(), 69 | date: DateTime.ParseExact(token[2]!.ToString(), "yyyyMMdd", CultureInfo.InvariantCulture)); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Converters/ThetaDataQuoteConverter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using Newtonsoft.Json.Linq; 18 | using System.Globalization; 19 | using QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | 21 | namespace QuantConnect.Lean.DataSource.ThetaData.Converters; 22 | 23 | /// 24 | /// JSON converter to convert ThetaData Quote 25 | /// 26 | public class ThetaDataQuoteConverter : JsonConverter 27 | { 28 | /// 29 | /// Gets a value indicating whether this can write JSON. 30 | /// 31 | /// true if this can write JSON; otherwise, false. 32 | public override bool CanWrite => false; 33 | 34 | /// 35 | /// Gets a value indicating whether this can read JSON. 36 | /// 37 | /// true if this can read JSON; otherwise, false. 38 | public override bool CanRead => true; 39 | 40 | /// 41 | /// Writes the JSON representation of the object. 42 | /// 43 | /// The to write to. 44 | /// The value. 45 | /// The calling serializer. 46 | public override void WriteJson(JsonWriter writer, QuoteResponse value, JsonSerializer serializer) 47 | { 48 | throw new NotSupportedException(); 49 | } 50 | 51 | /// 52 | /// Reads the JSON representation of the object. 53 | /// 54 | /// The to read from. 55 | /// Type of the object. 56 | /// The existing value of object being read. 57 | /// The existing value has a value. 58 | /// The calling serializer. 59 | /// The object value. 60 | public override QuoteResponse ReadJson(JsonReader reader, Type objectType, QuoteResponse existingValue, bool hasExistingValue, JsonSerializer serializer) 61 | { 62 | var token = JToken.Load(reader); 63 | if (token.Type != JTokenType.Array || token.Count() != 10) throw new Exception($"{nameof(ThetaDataQuoteConverter)}.{nameof(ReadJson)}: Invalid token type or count. Expected a JSON array with exactly four elements."); 64 | 65 | return new QuoteResponse( 66 | timeMilliseconds: token[0]!.Value(), 67 | bidSize: token[1]!.Value(), 68 | bidExchange: token[2]!.Value(), 69 | bidPrice: token[3]!.Value(), 70 | bidCondition: token[4]!.Value() ?? string.Empty, 71 | askSize: token[5]!.Value(), 72 | askExchange: token[6]!.Value(), 73 | askPrice: token[7]!.Value(), 74 | askCondition: token[8]!.Value() ?? string.Empty, 75 | date: DateTime.ParseExact(token[9]!.ToString(), "yyyyMMdd", CultureInfo.InvariantCulture)); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Converters/ThetaDataQuoteListContractConverter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using Newtonsoft.Json.Linq; 18 | using QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 19 | 20 | namespace QuantConnect.Lean.DataSource.ThetaData.Converters; 21 | 22 | /// 23 | /// JSON converter to convert ThetaData Quote 24 | /// 25 | public class ThetaDataQuoteListContractConverter : JsonConverter 26 | { 27 | /// 28 | /// Gets a value indicating whether this can write JSON. 29 | /// 30 | /// true if this can write JSON; otherwise, false. 31 | public override bool CanWrite => false; 32 | 33 | /// 34 | /// Gets a value indicating whether this can read JSON. 35 | /// 36 | /// true if this can read JSON; otherwise, false. 37 | public override bool CanRead => true; 38 | 39 | /// 40 | /// Writes the JSON representation of the object. 41 | /// 42 | /// The to write to. 43 | /// The value. 44 | /// The calling serializer. 45 | public override void WriteJson(JsonWriter writer, QuoteListContract value, JsonSerializer serializer) 46 | { 47 | throw new NotSupportedException(); 48 | } 49 | 50 | /// 51 | /// Reads the JSON representation of the object. 52 | /// 53 | /// The to read from. 54 | /// Type of the object. 55 | /// The existing value of object being read. 56 | /// The existing value has a value. 57 | /// The calling serializer. 58 | /// The object value. 59 | public override QuoteListContract ReadJson(JsonReader reader, Type objectType, QuoteListContract existingValue, bool hasExistingValue, JsonSerializer serializer) 60 | { 61 | var token = JToken.Load(reader); 62 | if (token.Type != JTokenType.Array || token.Count() != 4) throw new Exception($"{nameof(ThetaDataQuoteConverter)}.{nameof(ReadJson)}: Invalid token type or count. Expected a JSON array with exactly four elements."); 63 | 64 | return new( 65 | token[0]!.Value()!, 66 | token[1]!.Value()!.ConvertFromThetaDataDateFormat(), 67 | token[2]!.Value(), 68 | token[3]!.Value()! 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Converters/ThetaDataTradeConverter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using Newtonsoft.Json.Linq; 18 | using System.Globalization; 19 | using QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | 21 | namespace QuantConnect.Lean.DataSource.ThetaData.Converters; 22 | 23 | public class ThetaDataTradeConverter : JsonConverter 24 | { 25 | /// 26 | /// Gets a value indicating whether this can write JSON. 27 | /// 28 | /// true if this can write JSON; otherwise, false. 29 | public override bool CanWrite => false; 30 | 31 | /// 32 | /// Gets a value indicating whether this can read JSON. 33 | /// 34 | /// true if this can read JSON; otherwise, false. 35 | public override bool CanRead => true; 36 | 37 | /// 38 | /// Writes the JSON representation of the object. 39 | /// 40 | /// The to write to. 41 | /// The value. 42 | /// The calling serializer. 43 | public override void WriteJson(JsonWriter writer, TradeResponse value, JsonSerializer serializer) 44 | { 45 | throw new NotSupportedException(); 46 | } 47 | 48 | /// 49 | /// Reads the JSON representation of the object. 50 | /// 51 | /// The to read from. 52 | /// Type of the object. 53 | /// The existing value of object being read. 54 | /// The existing value has a value. 55 | /// The calling serializer. 56 | /// The object value. 57 | public override TradeResponse ReadJson(JsonReader reader, Type objectType, TradeResponse existingValue, bool hasExistingValue, JsonSerializer serializer) 58 | { 59 | var token = JToken.Load(reader); 60 | if (token.Type != JTokenType.Array || token.Count() != 15) throw new Exception($"{nameof(ThetaDataTradeConverter)}.{nameof(ReadJson)}: Invalid token type or count. Expected a JSON array with exactly four elements."); 61 | 62 | return new TradeResponse( 63 | timeMilliseconds: token[0]!.Value(), 64 | condition: token[6]!.Value() ?? string.Empty, 65 | size: token[7]!.Value(), 66 | exchange: token[8]!.Value(), 67 | price: token[9]!.Value(), 68 | date: DateTime.ParseExact(token[14]!.ToString(), "yyyyMMdd", CultureInfo.InvariantCulture) 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Common/BaseHeaderResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Lean.DataSource.ThetaData.Converters; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Common; 20 | 21 | /// 22 | /// Represents the base header response. 23 | /// 24 | public readonly struct BaseHeaderResponse 25 | { 26 | /// 27 | /// Gets the next page value. 28 | /// 29 | [JsonProperty("next_page")] 30 | [JsonConverter(typeof(ThetaDataNullStringConverter))] 31 | public string NextPage { get; } 32 | 33 | /// 34 | /// Initializes a new instance of the struct. 35 | /// 36 | /// The next page value. 37 | [JsonConstructor] 38 | public BaseHeaderResponse(string nextPage) 39 | { 40 | NextPage = nextPage; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Common/BaseResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Lean.DataSource.ThetaData.Models.Interfaces; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Common; 20 | 21 | /// 22 | /// Represents a base response containing a header and a collection of items of type T. 23 | /// 24 | /// The type of items in the response. 25 | public readonly struct BaseResponse : IBaseResponse 26 | { 27 | /// 28 | /// Gets the header of the response. 29 | /// 30 | [JsonProperty("header")] 31 | public BaseHeaderResponse Header { get; } 32 | 33 | /// 34 | /// Gets the collection of items in the response. 35 | /// 36 | [JsonProperty("response")] 37 | public IEnumerable Response { get; } 38 | 39 | /// 40 | /// Initializes a new instance of the struct. 41 | /// 42 | /// The collection of items in the response. 43 | /// The header of the response. 44 | [JsonConstructor] 45 | public BaseResponse(IEnumerable response, BaseHeaderResponse header) 46 | { 47 | Response = response; 48 | Header = header; 49 | } 50 | } -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Enums/ContractSecurityType.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using Newtonsoft.Json.Converters; 18 | using System.Runtime.Serialization; 19 | 20 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Enums; 21 | 22 | [JsonConverter(typeof(StringEnumConverter))] 23 | public enum ContractSecurityType 24 | { 25 | [EnumMember(Value = "OPTION")] 26 | Option = 0, 27 | 28 | [EnumMember(Value = "STOCK")] 29 | Equity = 1, 30 | 31 | [EnumMember(Value = "INDEX")] 32 | Index = 2 33 | } 34 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Enums/SubscriptionPlanType.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Enums; 17 | 18 | /// 19 | /// Enum representing different subscription plan types. 20 | /// following link: 21 | /// 22 | public enum SubscriptionPlanType 23 | { 24 | /// 25 | /// Free subscription plan. 26 | /// 27 | Free = 0, 28 | 29 | /// 30 | /// Value subscription plan. 31 | /// 32 | Value = 1, 33 | 34 | /// 35 | /// Standard subscription plan. 36 | /// 37 | Standard = 2, 38 | 39 | /// 40 | /// Pro subscription plan. 41 | /// 42 | Pro = 3 43 | } 44 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Enums/WebSocketHeaderType.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using Newtonsoft.Json.Converters; 18 | using System.Runtime.Serialization; 19 | 20 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Enums; 21 | 22 | [JsonConverter(typeof(StringEnumConverter))] 23 | public enum WebSocketHeaderType 24 | { 25 | [EnumMember(Value = "STATUS")] 26 | Status = 0, 27 | 28 | [EnumMember(Value = "QUOTE")] 29 | Quote = 1, 30 | 31 | [EnumMember(Value = "TRADE")] 32 | Trade = 2, 33 | 34 | [EnumMember(Value = "OHLC")] 35 | Ohlc = 3, 36 | 37 | [EnumMember(Value = "REQ_RESPONSE")] 38 | ReqResponse = 4, 39 | 40 | [EnumMember(Value = "STATE")] 41 | State = 5 42 | } 43 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Interfaces/IBaseResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using QuantConnect.Lean.DataSource.ThetaData.Models.Common; 17 | 18 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Interfaces; 19 | 20 | /// 21 | /// Represents the base interface for response objects. 22 | /// 23 | public interface IBaseResponse 24 | { 25 | /// 26 | /// Gets the header of the response. 27 | /// 28 | public BaseHeaderResponse Header { get; } 29 | } 30 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Interfaces/ISubscriptionPlan.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using QuantConnect.Util; 17 | 18 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Interfaces; 19 | 20 | /// 21 | /// The ISubscriptionPlan interface defines the base structure for different price plans offered by ThetaData for users. 22 | /// For detailed documentation on ThetaData subscription plans, refer to the following links: 23 | /// 24 | /// 25 | /// https://www.thetadata.net/subscribe 26 | /// Institutional Data Retail Pricing 27 | /// 28 | /// 29 | /// https://http-docs.thetadata.us/Articles/Getting-Started/Subscriptions.html#options-data 30 | /// Initial Access Date Based on Subscription Plan 31 | /// 32 | /// 33 | /// 34 | public interface ISubscriptionPlan 35 | { 36 | /// 37 | /// Gets the set of resolutions accessible under the subscription plan. 38 | /// 39 | public HashSet AccessibleResolutions { get; } 40 | 41 | /// 42 | /// Gets the date when the user first accessed the subscription plan. 43 | /// 44 | public DateTime FirstAccessDate { get; } 45 | 46 | /// 47 | /// Gets the maximum number of contracts that can be streamed simultaneously under the subscription plan. 48 | /// 49 | public uint MaxStreamingContracts { get; } 50 | 51 | /// 52 | /// Represents a rate limiting mechanism that controls the rate of access to a resource. 53 | /// 54 | public RateGate? RateGate { get; } 55 | } 56 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Rest/EndOfDayReportResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Lean.DataSource.ThetaData.Converters; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | 21 | [JsonConverter(typeof(ThetaDataEndOfDayConverter))] 22 | public readonly struct EndOfDayReportResponse 23 | { 24 | /// 25 | /// Represents the time of day the report was generated 26 | /// 27 | public uint ReportGeneratedTimeMilliseconds { get; } 28 | 29 | /// 30 | /// Represents the time of the last trade 31 | /// 32 | public uint LastTradeTimeMilliseconds { get; } 33 | 34 | /// 35 | /// The opening trade price. 36 | /// 37 | public decimal Open { get; } 38 | 39 | /// 40 | /// The highest traded price. 41 | /// 42 | public decimal High { get; } 43 | 44 | /// 45 | /// The lowest traded price. 46 | /// 47 | public decimal Low { get; } 48 | 49 | /// 50 | /// The closing traded price. 51 | /// 52 | public decimal Close { get; } 53 | 54 | /// 55 | /// The amount of contracts traded. 56 | /// 57 | public decimal Volume { get; } 58 | 59 | /// 60 | /// The amount of trades. 61 | /// 62 | public uint AmountTrades { get; } 63 | 64 | /// 65 | /// The last NBBO bid size. 66 | /// 67 | public decimal BidSize { get; } 68 | 69 | /// 70 | /// The last NBBO bid exchange. 71 | /// 72 | public byte BidExchange { get; } 73 | 74 | /// 75 | /// The last NBBO bid price. 76 | /// 77 | public decimal BidPrice { get; } 78 | 79 | /// 80 | /// The last NBBO bid condition. 81 | /// 82 | public string BidCondition { get; } 83 | 84 | /// 85 | /// The last NBBO ask size. 86 | /// 87 | //[JsonProperty("ask_size")] 88 | public decimal AskSize { get; } 89 | 90 | /// 91 | /// The last NBBO ask exchange. 92 | /// 93 | public byte AskExchange { get; } 94 | 95 | /// 96 | /// The last NBBO ask price. 97 | /// 98 | public decimal AskPrice { get; } 99 | 100 | /// 101 | /// The last NBBO ask condition. 102 | /// 103 | public string AskCondition { get; } 104 | 105 | /// 106 | /// The date formated as YYYYMMDD. 107 | /// 108 | public DateTime Date { get; } 109 | 110 | /// 111 | /// Gets the DateTime representation of the last trade time in milliseconds. DateTime is New York Time (EST) Time Zone! 112 | /// 113 | /// 114 | /// This property calculates the by adding the to the Date property. 115 | /// 116 | public DateTime LastTradeDateTimeMilliseconds { get => Date.AddMilliseconds(LastTradeTimeMilliseconds); } 117 | 118 | //[JsonConstructor] 119 | public EndOfDayReportResponse(uint reportGeneratedTimeMilliseconds, uint lastTradeTimeMilliseconds, decimal open, decimal high, decimal low, decimal close, 120 | decimal volume, uint amountTrades, decimal bidSize, byte bidExchange, decimal bidPrice, string bidCondition, decimal askSize, byte askExchange, 121 | decimal askPrice, string askCondition, DateTime date) 122 | { 123 | ReportGeneratedTimeMilliseconds = reportGeneratedTimeMilliseconds; 124 | LastTradeTimeMilliseconds = lastTradeTimeMilliseconds; 125 | Open = open; 126 | High = high; 127 | Low = low; 128 | Close = close; 129 | Volume = volume; 130 | AmountTrades = amountTrades; 131 | BidSize = bidSize; 132 | BidExchange = bidExchange; 133 | BidPrice = bidPrice; 134 | BidCondition = bidCondition; 135 | AskSize = askSize; 136 | AskExchange = askExchange; 137 | AskPrice = askPrice; 138 | AskCondition = askCondition; 139 | Date = date; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Rest/OpenHighLowCloseResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Lean.DataSource.ThetaData.Converters; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | 21 | [JsonConverter(typeof(ThetaDataOpenHighLowCloseConverter))] 22 | public readonly struct OpenHighLowCloseResponse 23 | { 24 | /// 25 | /// The milliseconds since 00:00:00.000 (midnight) Eastern Time (ET). 26 | /// 27 | public uint TimeMilliseconds { get; } 28 | 29 | /// 30 | /// The opening trade price. 31 | /// 32 | public decimal Open { get; } 33 | 34 | /// 35 | /// The highest traded price. 36 | /// 37 | public decimal High { get; } 38 | 39 | /// 40 | /// The lowest traded price. 41 | /// 42 | public decimal Low { get; } 43 | 44 | /// 45 | /// The closing traded price. 46 | /// 47 | public decimal Close { get; } 48 | 49 | /// 50 | /// The amount of contracts traded. 51 | /// 52 | public decimal Volume { get; } 53 | 54 | /// 55 | /// The amount of trades. 56 | /// 57 | public uint Count { get; } 58 | 59 | /// 60 | /// The date formatted as YYYYMMDD. (e.g. "20240328" -> 2024/03/28) 61 | /// 62 | public DateTime Date { get; } 63 | 64 | /// 65 | /// Gets the DateTime representation of the last quote time. DateTime is New York Time (EST) Time Zone! 66 | /// 67 | /// 68 | /// This property calculates the by adding the to the Date property. 69 | /// 70 | public DateTime DateTimeMilliseconds { get => Date.AddMilliseconds(TimeMilliseconds); } 71 | 72 | /// 73 | /// Initializes a new instance of the struct. 74 | /// 75 | /// The milliseconds since 00:00:00.000 (midnight) Eastern Time (ET). 76 | /// The opening trade price. 77 | /// The highest traded price. 78 | /// The lowest traded price. 79 | /// The closing traded price. 80 | /// The amount of contracts traded. 81 | /// The amount of trades. 82 | /// The date formatted as YYYYMMDD. 83 | public OpenHighLowCloseResponse(uint timeMilliseconds, decimal open, decimal high, decimal low, decimal close, decimal volume, uint count, DateTime date) 84 | { 85 | TimeMilliseconds = timeMilliseconds; 86 | Open = open; 87 | High = high; 88 | Low = low; 89 | Close = close; 90 | Volume = volume; 91 | Count = count; 92 | Date = date; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Rest/OpenInterestResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Lean.DataSource.ThetaData.Converters; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | 21 | /// 22 | /// Represents a response containing open interest data. 23 | /// 24 | [JsonConverter(typeof(ThetaDataOpenInterestConverter))] 25 | public readonly struct OpenInterestResponse 26 | { 27 | /// 28 | /// Gets the time at which open interest was reported, represented in milliseconds since 00:00:00.000 (midnight) Eastern Time (ET). 29 | /// 30 | public uint TimeMilliseconds { get; } 31 | 32 | /// 33 | /// Gets the total amount of outstanding contracts. 34 | /// 35 | public decimal OpenInterest { get; } 36 | 37 | /// 38 | /// Gets the date of the open interest data in the format YYYYMMDD. For example, "20240328" represents March 28, 2024. 39 | /// 40 | public DateTime Date { get; } 41 | 42 | /// 43 | /// Gets the DateTime representation of the last Open Interest time. DateTime is New York Time (EST) Time Zone! 44 | /// 45 | /// 46 | /// This property calculates the by adding the to the Date property. 47 | /// 48 | public DateTime DateTimeMilliseconds { get => Date.AddMilliseconds(TimeMilliseconds); } 49 | 50 | /// 51 | /// Initializes a new instance of the struct with the specified time, open interest, and date. 52 | /// 53 | /// The time in milliseconds since midnight Eastern Time (ET). 54 | /// The total amount of outstanding contracts. 55 | /// The date of the data in the format YYYYMMDD. 56 | public OpenInterestResponse(uint timeMilliseconds, decimal openInterest, DateTime date) 57 | { 58 | TimeMilliseconds = timeMilliseconds; 59 | OpenInterest = openInterest; 60 | Date = date; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Rest/PriceResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Lean.DataSource.ThetaData.Converters; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | 21 | /// 22 | /// Represents a response containing price response data. 23 | /// 24 | [JsonConverter(typeof(ThetaDataPriceConverter))] 25 | public readonly struct PriceResponse 26 | { 27 | /// 28 | /// Gets the time at which open interest was reported, represented in milliseconds since 00:00:00.000 (midnight) Eastern Time (ET). 29 | /// 30 | public uint TimeMilliseconds { get; } 31 | 32 | /// 33 | /// The reported price of the index. 34 | /// 35 | public decimal Price { get; } 36 | 37 | /// 38 | /// The date formatted as YYYYMMDD. (e.g. "20240328" -> 2024/03/28) 39 | /// 40 | public DateTime Date { get; } 41 | 42 | /// 43 | /// Gets the DateTime representation of the last quote time. DateTime is New York Time (EST) Time Zone! 44 | /// 45 | /// 46 | /// This property calculates the by adding the to the Date property. 47 | /// 48 | public DateTime DateTimeMilliseconds { get => Date.AddMilliseconds(TimeMilliseconds); } 49 | 50 | /// 51 | /// Initializes a new instance of the struct. 52 | /// 53 | /// The milliseconds since 00:00:00.000 (midnight) Eastern Time (ET). 54 | /// The reported price of the index. 55 | /// The date formatted as YYYYMMDD. 56 | public PriceResponse(uint timeMilliseconds, decimal price, DateTime date) 57 | { 58 | TimeMilliseconds = timeMilliseconds; 59 | Price = price; 60 | Date = date; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Rest/QuoteListContract.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Lean.DataSource.ThetaData.Converters; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | 21 | [JsonConverter(typeof(ThetaDataQuoteListContractConverter))] 22 | public readonly struct QuoteListContract 23 | { 24 | public string Ticker { get; } 25 | 26 | public DateTime Expiry { get; } 27 | 28 | public decimal Strike { get; } 29 | 30 | public string Right { get; } 31 | 32 | public QuoteListContract(string ticker, DateTime expiry, decimal strike, string right) 33 | { 34 | Ticker = ticker; 35 | Expiry = expiry; 36 | Strike = strike; 37 | Right = right; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Rest/QuoteResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Lean.DataSource.ThetaData.Converters; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | 21 | /// 22 | /// Represents a Quote containing information about the last NBBO (National Best Bid and Offer) for a financial instrument. 23 | /// 24 | [JsonConverter(typeof(ThetaDataQuoteConverter))] 25 | public readonly struct QuoteResponse 26 | { 27 | /// 28 | /// The milliseconds since 00:00:00.000 (midnight) Eastern Time (ET). 29 | /// 30 | public uint TimeMilliseconds { get; } 31 | 32 | /// 33 | /// The last NBBO bid size. 34 | /// 35 | public decimal BidSize { get; } 36 | 37 | /// 38 | /// The last NBBO bid exchange. 39 | /// 40 | public byte BidExchange { get; } 41 | 42 | /// 43 | /// The last NBBO bid price. 44 | /// 45 | public decimal BidPrice { get; } 46 | 47 | /// 48 | /// The last NBBO bid condition. 49 | /// 50 | public string BidCondition { get; } 51 | 52 | /// 53 | /// The last NBBO ask size. 54 | /// 55 | public decimal AskSize { get; } 56 | 57 | /// 58 | /// The last NBBO ask exchange. 59 | /// 60 | public byte AskExchange { get; } 61 | 62 | /// 63 | /// The last NBBO ask price. 64 | /// 65 | public decimal AskPrice { get; } 66 | 67 | /// 68 | /// The last NBBO ask condition. 69 | /// 70 | public string AskCondition { get; } 71 | 72 | /// 73 | /// The date formatted as YYYYMMDD. (e.g. "20240328" -> 2024/03/28) 74 | /// 75 | public DateTime Date { get; } 76 | 77 | /// 78 | /// Gets the DateTime representation of the last quote time. DateTime is New York Time (EST) Time Zone! 79 | /// 80 | /// 81 | /// This property calculates the by adding the to the Date property. 82 | /// 83 | public DateTime DateTimeMilliseconds { get => Date.AddMilliseconds(TimeMilliseconds); } 84 | 85 | /// 86 | /// Initializes a new instance of the struct. 87 | /// 88 | /// Milliseconds since 00:00:00.000 (midnight) Eastern Time (ET). 89 | /// The last NBBO bid size. 90 | /// The last NBBO bid exchange. 91 | /// The last NBBO bid price. 92 | /// The last NBBO bid condition. 93 | /// The last NBBO ask size. 94 | /// The last NBBO ask exchange. 95 | /// The last NBBO ask price. 96 | /// The last NBBO ask condition. 97 | /// The date formatted as YYYYMMDD. 98 | public QuoteResponse(uint timeMilliseconds, decimal bidSize, byte bidExchange, decimal bidPrice, string bidCondition, 99 | decimal askSize, byte askExchange, decimal askPrice, string askCondition, DateTime date) 100 | { 101 | TimeMilliseconds = timeMilliseconds; 102 | BidSize = bidSize; 103 | BidExchange = bidExchange; 104 | BidPrice = bidPrice; 105 | BidCondition = bidCondition; 106 | AskSize = askSize; 107 | AskExchange = askExchange; 108 | AskPrice = askPrice; 109 | AskCondition = askCondition; 110 | Date = date; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Rest/RequestParameters.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 17 | 18 | /// 19 | /// Contains constant values for various request parameters used in API queries. 20 | /// 21 | public static class RequestParameters 22 | { 23 | /// 24 | /// Represents the time interval in milliseconds since midnight Eastern Time (ET). 25 | /// Example values: 26 | /// - 09:30:00 ET = 34_200_000 ms 27 | /// - 16:00:00 ET = 57_600_000 ms 28 | /// 29 | public const string IntervalInMilliseconds = "ivl"; 30 | 31 | /// 32 | /// Represents the start date for a query or request. 33 | /// 34 | public const string StartDate = "start_date"; 35 | 36 | /// 37 | /// Represents the end date for a query or request. 38 | /// 39 | public const string EndDate = "end_date"; 40 | } 41 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Rest/TradeResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Lean.DataSource.ThetaData.Converters; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | 21 | /// 22 | /// Represents a Trade response containing information about the last NBBO Trade for a financial instrument. 23 | /// 24 | [JsonConverter(typeof(ThetaDataTradeConverter))] 25 | public readonly struct TradeResponse 26 | { 27 | /// 28 | /// The milliseconds since 00:00:00.000 (midnight) Eastern Time (ET). 29 | /// 30 | public uint TimeMilliseconds { get; } 31 | 32 | /// 33 | /// The trade condition. 34 | /// 35 | public string Condition { get; } 36 | 37 | /// 38 | /// The amount of contracts traded. 39 | /// 40 | public decimal Size { get; } 41 | 42 | /// 43 | /// The exchange the trade was executed. 44 | /// 45 | public byte Exchange { get; } 46 | 47 | /// 48 | /// The price of the trade. 49 | /// 50 | public decimal Price { get; } 51 | 52 | /// 53 | /// The date formatted as YYYYMMDD. (e.g. "20240328" -> 2024/03/28) 54 | /// 55 | public DateTime Date { get; } 56 | 57 | /// 58 | /// Gets the DateTime representation of the last trade time. DateTime is New York Time (EST) Time Zone! 59 | /// 60 | /// 61 | /// This property calculates the by adding the to the Date property. 62 | /// 63 | public DateTime DateTimeMilliseconds { get => Date.AddMilliseconds(TimeMilliseconds); } 64 | 65 | /// 66 | /// Initializes a new instance of the struct with the specified parameters. 67 | /// 68 | /// The milliseconds since midnight ET. 69 | /// The trade condition. 70 | /// The amount of contracts traded. 71 | /// The exchange where the trade was executed. 72 | /// The price of the trade. 73 | /// The date formatted as YYYYMMDD. 74 | public TradeResponse(uint timeMilliseconds, string condition, decimal size, byte exchange, decimal price, DateTime date) 75 | { 76 | TimeMilliseconds = timeMilliseconds; 77 | Condition = condition; 78 | Size = size; 79 | Exchange = exchange; 80 | Price = price; 81 | Date = date; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/SubscriptionPlans/FreeSubscriptionPlan.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using QuantConnect.Util; 17 | using QuantConnect.Lean.DataSource.ThetaData.Models.Interfaces; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.SubscriptionPlans; 20 | 21 | public class FreeSubscriptionPlan : ISubscriptionPlan 22 | { 23 | public HashSet AccessibleResolutions => new() { Resolution.Daily }; 24 | 25 | public DateTime FirstAccessDate => new DateTime(2023, 06, 01); 26 | 27 | public uint MaxStreamingContracts => 0; 28 | 29 | public RateGate RateGate => new(30, TimeSpan.FromMinutes(1)); 30 | } 31 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/SubscriptionPlans/ProSubscriptionPlan.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using QuantConnect.Util; 17 | using QuantConnect.Lean.DataSource.ThetaData.Models.Interfaces; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.SubscriptionPlans; 20 | 21 | public class ProSubscriptionPlan : ISubscriptionPlan 22 | { 23 | public HashSet AccessibleResolutions => new() { Resolution.Tick, Resolution.Second, Resolution.Minute, Resolution.Hour, Resolution.Daily }; 24 | 25 | public DateTime FirstAccessDate => new DateTime(2012, 06, 01); 26 | 27 | public uint MaxStreamingContracts => 15_000; 28 | 29 | public RateGate? RateGate => null; 30 | } 31 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/SubscriptionPlans/StandardSubscriptionPlan.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using QuantConnect.Util; 17 | using QuantConnect.Lean.DataSource.ThetaData.Models.Interfaces; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.SubscriptionPlans; 20 | 21 | public class StandardSubscriptionPlan : ISubscriptionPlan 22 | { 23 | public HashSet AccessibleResolutions => new() { Resolution.Tick, Resolution.Second, Resolution.Minute, Resolution.Hour, Resolution.Daily }; 24 | 25 | public DateTime FirstAccessDate => new DateTime(2016, 01, 01); 26 | 27 | public uint MaxStreamingContracts => 10_000; 28 | 29 | public RateGate? RateGate => null; 30 | } 31 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/SubscriptionPlans/ValueSubscriptionPlan.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using QuantConnect.Util; 17 | using QuantConnect.Lean.DataSource.ThetaData.Models.Interfaces; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.SubscriptionPlans; 20 | 21 | public class ValueSubscriptionPlan : ISubscriptionPlan 22 | { 23 | /// 24 | public HashSet AccessibleResolutions => new() { Resolution.Minute, Resolution.Hour, Resolution.Daily }; 25 | 26 | public DateTime FirstAccessDate => new DateTime(2020, 01, 01); 27 | 28 | public uint MaxStreamingContracts => 0; 29 | 30 | public RateGate? RateGate => null; 31 | } 32 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/WebSocket/WebSocketContract.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using Newtonsoft.Json.Converters; 18 | using QuantConnect.Lean.DataSource.ThetaData.Models.Enums; 19 | 20 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.WebSocket; 21 | 22 | public readonly struct WebSocketContract 23 | { 24 | [JsonProperty("security_type")] 25 | [JsonConverter(typeof(StringEnumConverter))] 26 | public ContractSecurityType SecurityType { get; } 27 | 28 | [JsonProperty("root")] 29 | public string Root { get; } 30 | 31 | [JsonProperty("expiration")] 32 | public string Expiration { get; } 33 | 34 | [JsonProperty("strike")] 35 | public decimal Strike { get; } 36 | 37 | [JsonProperty("right")] 38 | public string Right { get; } 39 | 40 | [JsonConstructor] 41 | public WebSocketContract(ContractSecurityType securityType, string root, string expiration, decimal strike, string right) 42 | { 43 | SecurityType = securityType; 44 | Root = root; 45 | Expiration = expiration; 46 | Strike = strike; 47 | Right = right; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/WebSocket/WebSocketHeader.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using Newtonsoft.Json.Converters; 18 | using QuantConnect.Lean.DataSource.ThetaData.Models.Enums; 19 | 20 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.WebSocket; 21 | 22 | public readonly struct WebSocketHeader 23 | { 24 | [JsonProperty("type")] 25 | [JsonConverter(typeof(StringEnumConverter))] 26 | public WebSocketHeaderType Type { get; } 27 | 28 | [JsonProperty("status")] 29 | public string Status { get; } 30 | 31 | [JsonProperty("response")] 32 | public string Response { get; } 33 | 34 | [JsonProperty("req_id")] 35 | public int RequestId { get; } 36 | 37 | [JsonProperty("state")] 38 | public string State { get; } 39 | 40 | [JsonConstructor] 41 | public WebSocketHeader(WebSocketHeaderType type, string status, string response, int requestId, string state) 42 | { 43 | Type = type; 44 | Status = status; 45 | Response = response; 46 | RequestId = requestId; 47 | State = state; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/WebSocket/WebSocketQuote.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Lean.DataSource.ThetaData.Converters; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.WebSocket; 20 | 21 | public readonly struct WebSocketQuote 22 | { 23 | [JsonProperty("ms_of_day")] 24 | public int TimeMilliseconds { get; } 25 | 26 | [JsonProperty("bid_size")] 27 | public int BidSize { get; } 28 | 29 | [JsonProperty("bid_exchange")] 30 | public byte BidExchange { get; } 31 | 32 | [JsonProperty("bid")] 33 | public decimal BidPrice { get; } 34 | 35 | [JsonProperty("bid_condition")] 36 | public int BidCondition { get; } 37 | 38 | [JsonProperty("ask_size")] 39 | public int AskSize { get; } 40 | 41 | [JsonProperty("ask_exchange")] 42 | public byte AskExchange { get; } 43 | 44 | [JsonProperty("ask")] 45 | public decimal AskPrice { get; } 46 | 47 | [JsonProperty("ask_condition")] 48 | public int AskCondition { get; } 49 | 50 | [JsonProperty("date")] 51 | [JsonConverter(typeof(DateTimeIntJsonConverter))] 52 | public DateTime Date { get; } 53 | 54 | /// 55 | /// Gets the DateTime representation of the last quote time. DateTime is New York Time (EST) Time Zone! 56 | /// 57 | /// 58 | /// This property calculates the by adding the to the Date property. 59 | /// 60 | public DateTime DateTimeMilliseconds { get => Date.AddMilliseconds(TimeMilliseconds); } 61 | 62 | [JsonConstructor] 63 | public WebSocketQuote( 64 | int timeMilliseconds, 65 | int bidSize, byte bidExchange, decimal bidPrice, int bidCondition, int askSize, byte askExchange, decimal askPrice, int askCondition, DateTime date) 66 | { 67 | TimeMilliseconds = timeMilliseconds; 68 | BidSize = bidSize; 69 | BidExchange = bidExchange; 70 | BidPrice = bidPrice; 71 | BidCondition = bidCondition; 72 | AskSize = askSize; 73 | AskExchange = askExchange; 74 | AskPrice = askPrice; 75 | AskCondition = askCondition; 76 | Date = date; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/WebSocket/WebSocketResponse.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | 18 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.WebSocket 19 | { 20 | 21 | public class WebSocketResponse 22 | { 23 | [JsonProperty("header")] 24 | public WebSocketHeader Header { get; } 25 | 26 | [JsonProperty("contract")] 27 | public WebSocketContract? Contract { get; } 28 | 29 | [JsonProperty("trade")] 30 | public WebSocketTrade? Trade { get; } 31 | 32 | [JsonProperty("quote")] 33 | public WebSocketQuote? Quote { get; } 34 | 35 | public WebSocketResponse(WebSocketHeader header, WebSocketContract? contract, WebSocketTrade? trade, WebSocketQuote? quote) 36 | { 37 | Header = header; 38 | Contract = contract; 39 | Trade = trade; 40 | Quote = quote; 41 | 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/WebSocket/WebSocketTrade.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Lean.DataSource.ThetaData.Converters; 18 | 19 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.WebSocket; 20 | 21 | public readonly struct WebSocketTrade 22 | { 23 | [JsonProperty("ms_of_day")] 24 | public int TimeMilliseconds { get; } 25 | 26 | [JsonProperty("sequence")] 27 | public int Sequence { get; } 28 | 29 | [JsonProperty("size")] 30 | public int Size { get; } 31 | 32 | [JsonProperty("condition")] 33 | public int Condition { get; } 34 | 35 | [JsonProperty("price")] 36 | public decimal Price { get; } 37 | 38 | [JsonProperty("exchange")] 39 | public byte Exchange { get; } 40 | 41 | [JsonProperty("date")] 42 | [JsonConverter(typeof(DateTimeIntJsonConverter))] 43 | public DateTime Date { get; } 44 | 45 | /// 46 | /// Gets the DateTime representation of the last trade time. DateTime is New York Time (EST) Time Zone! 47 | /// 48 | /// 49 | /// This property calculates the by adding the to the Date property. 50 | /// 51 | public DateTime DateTimeMilliseconds { get => Date.AddMilliseconds(TimeMilliseconds); } 52 | 53 | [JsonConstructor] 54 | public WebSocketTrade(int timeMilliseconds, int sequence, int size, int condition, decimal price, byte exchange, DateTime date) 55 | { 56 | TimeMilliseconds = timeMilliseconds; 57 | Sequence = sequence; 58 | Size = size; 59 | Condition = condition; 60 | Price = price; 61 | Exchange = exchange; 62 | Date = date; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/Models/Wrappers/StopwatchWrapper.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using System.Diagnostics; 17 | using QuantConnect.Logging; 18 | using QuantConnect.Lean.DataSource.ThetaData.Models.Common; 19 | 20 | namespace QuantConnect.Lean.DataSource.ThetaData.Models.Wrappers; 21 | 22 | /// 23 | /// A utility class that conditionally starts a stopwatch for measuring execution time 24 | /// when debugging is enabled. Implements to ensure 25 | /// automatic logging upon completion. 26 | /// 27 | public class StopwatchWrapper : IDisposable 28 | { 29 | private readonly Stopwatch? _stopwatch; 30 | private readonly string _message; 31 | 32 | /// 33 | /// Initializes a new instance of the class 34 | /// and starts a stopwatch to measure execution time. 35 | /// 36 | /// A descriptive message to include in the log output. 37 | private StopwatchWrapper(string message) 38 | { 39 | _message = message; 40 | _stopwatch = Stopwatch.StartNew(); 41 | } 42 | 43 | /// 44 | /// Starts a stopwatch if debugging is enabled and returns an appropriate disposable instance. 45 | /// 46 | /// A descriptive message to include in the log output. 47 | /// 48 | /// A instance if debugging is enabled, 49 | /// otherwise a no-op instance. 50 | /// 51 | public static IDisposable? StartIfEnabled(string message) 52 | { 53 | return Log.DebuggingEnabled ? new StopwatchWrapper(message) : null; 54 | } 55 | 56 | /// 57 | /// Stops the stopwatch and logs the elapsed time if debugging is enabled. 58 | /// 59 | public void Dispose() 60 | { 61 | if (_stopwatch != null) 62 | { 63 | _stopwatch.Stop(); 64 | Log.Debug($"{_message} completed in {_stopwatch.ElapsedMilliseconds} ms"); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/QuantConnect.DataSource.ThetaData.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Release 4 | AnyCPU 5 | net9.0 6 | QuantConnect.Lean.DataSource.ThetaData 7 | QuantConnect.Lean.DataSource.ThetaData 8 | QuantConnect.Lean.DataSource.ThetaData 9 | QuantConnect.Lean.DataSource.ThetaData 10 | Library 11 | bin\$(Configuration)\ 12 | false 13 | true 14 | false 15 | QuantConnect LEAN ThetaData Data Source: ThetaData Data Source plugin for Lean 16 | 17 | enable 18 | enable 19 | 20 | 21 | 22 | full 23 | bin\Debug\ 24 | 25 | 26 | 27 | pdbonly 28 | bin\Release\ 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/ThetaDataDownloader.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using NodaTime; 17 | using QuantConnect.Data; 18 | using QuantConnect.Util; 19 | using QuantConnect.Securities; 20 | using System.Collections.Concurrent; 21 | 22 | namespace QuantConnect.Lean.DataSource.ThetaData 23 | { 24 | public class ThetaDataDownloader : IDataDownloader, IDisposable 25 | { 26 | /// 27 | /// 28 | /// 29 | private readonly ThetaDataProvider _historyProvider; 30 | 31 | /// 32 | private readonly MarketHoursDatabase _marketHoursDatabase; 33 | 34 | /// 35 | /// Initializes a new instance of the 36 | /// 37 | public ThetaDataDownloader() 38 | { 39 | _historyProvider = new(); 40 | _marketHoursDatabase = MarketHoursDatabase.FromDataFolder(); 41 | } 42 | 43 | public IEnumerable? Get(DataDownloaderGetParameters downloadParameters) 44 | { 45 | var symbol = downloadParameters.Symbol; 46 | 47 | var dataType = LeanData.GetDataType(downloadParameters.Resolution, downloadParameters.TickType); 48 | var exchangeHours = _marketHoursDatabase.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType); 49 | var dataTimeZone = _marketHoursDatabase.GetDataTimeZone(symbol.ID.Market, symbol, symbol.SecurityType); 50 | 51 | if (symbol.IsCanonical()) 52 | { 53 | return GetCanonicalOptionHistory( 54 | symbol, 55 | downloadParameters.StartUtc, 56 | downloadParameters.EndUtc, 57 | dataType, 58 | downloadParameters.Resolution, 59 | exchangeHours, 60 | dataTimeZone, 61 | downloadParameters.TickType); 62 | } 63 | else 64 | { 65 | var historyRequest = new HistoryRequest( 66 | startTimeUtc: downloadParameters.StartUtc, 67 | endTimeUtc: downloadParameters.EndUtc, dataType, 68 | symbol: symbol, 69 | resolution: downloadParameters.Resolution, 70 | exchangeHours: exchangeHours, 71 | dataTimeZone: dataTimeZone, 72 | fillForwardResolution: downloadParameters.Resolution, 73 | includeExtendedMarketHours: true, 74 | isCustomData: false, 75 | dataNormalizationMode: DataNormalizationMode.Raw, 76 | tickType: downloadParameters.TickType); 77 | 78 | var historyData = _historyProvider.GetHistory(historyRequest); 79 | 80 | if (historyData == null) 81 | { 82 | return null; 83 | } 84 | 85 | return historyData; 86 | } 87 | } 88 | 89 | private IEnumerable? GetCanonicalOptionHistory(Symbol symbol, DateTime startUtc, DateTime endUtc, Type dataType, 90 | Resolution resolution, SecurityExchangeHours exchangeHours, DateTimeZone dataTimeZone, TickType tickType) 91 | { 92 | var blockingOptionCollection = new BlockingCollection(); 93 | var symbols = GetOptions(symbol, startUtc, endUtc); 94 | 95 | // Symbol can have a lot of Option parameters 96 | Task.Run(() => Parallel.ForEach(symbols, targetSymbol => 97 | { 98 | var historyRequest = new HistoryRequest(startUtc, endUtc, dataType, targetSymbol, resolution, exchangeHours, dataTimeZone, 99 | resolution, true, false, DataNormalizationMode.Raw, tickType); 100 | 101 | var history = _historyProvider.GetHistory(historyRequest); 102 | 103 | // If history is null, it indicates an incorrect or missing request for historical data, 104 | // so we skip processing for this symbol and move to the next one. 105 | if (history == null) 106 | { 107 | return; 108 | } 109 | 110 | foreach (var data in history) 111 | { 112 | blockingOptionCollection.Add(data); 113 | } 114 | })).ContinueWith(_ => 115 | { 116 | blockingOptionCollection.CompleteAdding(); 117 | }); 118 | 119 | var options = blockingOptionCollection.GetConsumingEnumerable(); 120 | 121 | // Validate if the collection contains at least one successful response from history. 122 | if (!options.Any()) 123 | { 124 | return null; 125 | } 126 | 127 | return options; 128 | } 129 | 130 | protected virtual IEnumerable GetOptions(Symbol symbol, DateTime startUtc, DateTime endUtc) 131 | { 132 | var exchangeHours = _marketHoursDatabase.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType); 133 | 134 | return Time.EachTradeableDay(exchangeHours, startUtc.Date, endUtc.Date) 135 | .Select(date => _historyProvider.GetOptionChain(symbol, date)) 136 | .SelectMany(x => x) 137 | .Distinct(); 138 | } 139 | 140 | /// 141 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 142 | /// 143 | public void Dispose() 144 | { 145 | _historyProvider.DisposeSafely(); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/ThetaDataExtensions.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using System.Globalization; 17 | using QuantConnect.Logging; 18 | using QuantConnect.Securities; 19 | 20 | namespace QuantConnect.Lean.DataSource.ThetaData 21 | { 22 | public static class ThetaDataExtensions 23 | { 24 | /// 25 | /// Indicates whether the exchange message error has been fired. 26 | /// 27 | private static bool _isExchangeMessageFired; 28 | 29 | /// 30 | /// Converts a date string from Theta data format (yyyyMMdd) to a DateTime object. 31 | /// 32 | /// The date string in Theta data format (e.g., "20240303" for March 3, 2024). 33 | /// The equivalent DateTime object. 34 | public static DateTime ConvertFromThetaDataDateFormat(this string date) => DateTime.ParseExact(date, DateFormat.EightCharacter, CultureInfo.InvariantCulture); 35 | 36 | /// 37 | /// Converts a DateTime object to Theta data format (yyyyMMdd) to a string. 38 | /// 39 | /// The DateTime object (e.g., new DateTime(2024, 03, 03)) 40 | /// The equivalent Theta Date string date. 41 | public static string ConvertToThetaDataDateFormat(this DateTime date) => date.ToStringInvariant(DateFormat.EightCharacter); 42 | 43 | /// 44 | /// Generates date ranges with each range covering a specified number of days from the specified to . 45 | /// 46 | /// The starting date (inclusive) of the date range. 47 | /// The ending date (exclusive) of the date range. 48 | /// The number of days each date range should cover. 49 | /// An enumerable sequence of tuples where each tuple contains a start date (inclusive) and end date (exclusive) covering the specified number of days. 50 | /// 51 | /// 52 | /// 53 | /// Best Practices for request interval size and duration: 54 | /// Making large requests is often times inefficient and can lead to out of memory errors. 55 | /// Recommended date range limit for each type of resolution / asset class is under 1 million ticks. 56 | /// Going over the recommended date range may cause client or server side out of memory errors. 57 | /// 58 | /// 59 | public static IEnumerable<(DateTime startDate, DateTime endDate)> GenerateDateRangesWithInterval(DateTime startDate, DateTime endDate, int intervalDays = 1) 60 | { 61 | DateTime currentDate = startDate; 62 | 63 | while (currentDate <= endDate) 64 | { 65 | DateTime nextDate = currentDate.AddDays(intervalDays); 66 | 67 | if (nextDate > endDate) 68 | nextDate = endDate; 69 | 70 | yield return (currentDate, nextDate); 71 | 72 | // Move to the next interval 73 | currentDate = nextDate.AddDays(1); 74 | } 75 | } 76 | 77 | /// 78 | /// Retrieves the time zone of the exchange for the given symbol. 79 | /// 80 | /// The symbol for which to get the exchange time zone. 81 | /// 82 | /// The representing the time zone of the exchange 83 | /// where the given symbol is traded. 84 | /// 85 | /// 86 | /// This method uses the to fetch the exchange hours 87 | /// and extract the time zone information for the provided symbol. 88 | /// 89 | public static NodaTime.DateTimeZone GetSymbolExchangeTimeZone(this Symbol symbol) 90 | => MarketHoursDatabase.FromDataFolder().GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType).TimeZone; 91 | 92 | /// 93 | /// Attempts to get the exchange name corresponding to the given exchange number. 94 | /// 95 | /// The numerical code of the exchange. 96 | /// 97 | /// The name of the exchange if found; otherwise, an empty string. 98 | /// 99 | /// 100 | /// Logs an error if the exchange number is not found in the dictionary. 101 | /// 102 | public static string TryGetExchangeOrDefault(this byte exchangeNumber) 103 | { 104 | if (Exchanges.TryGetValue(exchangeNumber, out var exchange)) 105 | { 106 | return exchange; 107 | } 108 | if (!_isExchangeMessageFired) 109 | { 110 | _isExchangeMessageFired = true; 111 | Log.Error($"{nameof(ThetaDataExtensions)}.{nameof(TryGetExchangeOrDefault)}: Exchange number {exchangeNumber} not found."); 112 | } 113 | return string.Empty; 114 | } 115 | 116 | /// 117 | /// Represents a collection of Exchanges with their corresponding numerical codes. 118 | /// 119 | private static Dictionary Exchanges = new() 120 | { 121 | { 1, "NQEX" }, 122 | { 2, "NQAD" }, 123 | { 3, "NYSE" }, 124 | { 4, "AMEX" }, 125 | { 5, "CBOE" }, 126 | { 6, "ISEX" }, 127 | { 7, "PACF" }, 128 | { 8, "CINC" }, 129 | { 9, "PHIL" }, 130 | { 10, "OPRA" }, 131 | { 11, "BOST" }, 132 | { 12, "NQNM" }, 133 | { 13, "NQSC" }, 134 | { 14, "NQBB" }, 135 | { 15, "NQPK" }, 136 | { 16, "NQIX" }, 137 | { 17, "CHIC" }, 138 | { 18, "TSE" }, 139 | { 19, "CDNX" }, 140 | { 20, "CME" }, 141 | { 21, "NYBT" }, 142 | { 22, "MRCY" }, 143 | { 23, "COMX" }, 144 | { 24, "CBOT" }, 145 | { 25, "NYMX" }, 146 | { 26, "KCBT" }, 147 | { 27, "MGEX" }, 148 | { 28, "NYBO" }, 149 | { 29, "NQBS" }, 150 | { 30, "DOWJ" }, 151 | { 31, "GEMI" }, 152 | { 32, "SIMX" }, 153 | { 33, "FTSE" }, 154 | { 34, "EURX" }, 155 | { 35, "IMPL" }, 156 | { 36, "DTN" }, 157 | { 37, "LMT" }, 158 | { 38, "LME" }, 159 | { 39, "IPEX" }, 160 | { 40, "NQMF" }, 161 | { 41, "FCEC" }, 162 | { 42, "C2" }, 163 | { 43, "MIAX" }, 164 | { 44, "CLRP" }, 165 | { 45, "BARK" }, 166 | { 46, "EMLD" }, 167 | { 47, "NQBX" }, 168 | { 48, "HOTS" }, 169 | { 49, "EUUS" }, 170 | { 50, "EUEU" }, 171 | { 51, "ENCM" }, 172 | { 52, "ENID" }, 173 | { 53, "ENIR" }, 174 | { 54, "CFE" }, 175 | { 55, "PBOT" }, 176 | { 56, "CMEFloor" }, 177 | { 57, "NQNX" }, 178 | { 58, "BTRF" }, 179 | { 59, "NTRF" }, 180 | { 60, "BATS" }, 181 | { 61, "FCBT" }, 182 | { 62, "PINK" }, 183 | { 63, "BATY" }, 184 | { 64, "EDGE" }, 185 | { 65, "EDGX" }, 186 | { 66, "RUSL" }, 187 | { 67, "CMEX" }, 188 | { 68, "IEX" }, 189 | { 69, "PERL" }, 190 | { 70, "LSE" }, 191 | { 71, "GIF" }, 192 | { 72, "TSIX" }, 193 | { 73, "MEMX" }, 194 | { 74, "EMPT" }, 195 | { 75, "LTSE" }, 196 | { 76, "EMPT" }, 197 | { 77, "EMPT" }, 198 | }; 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/ThetaDataOptionChainProvider.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using RestSharp; 17 | using QuantConnect.Logging; 18 | using QuantConnect.Interfaces; 19 | using QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 20 | using QuantConnect.Lean.DataSource.ThetaData.Models.Common; 21 | 22 | namespace QuantConnect.Lean.DataSource.ThetaData 23 | { 24 | /// 25 | /// ThetaData.net implementation of 26 | /// 27 | public class ThetaDataOptionChainProvider : IOptionChainProvider 28 | { 29 | /// 30 | /// Provides the TheData mapping between Lean symbols and brokerage specific symbols. 31 | /// 32 | private readonly ThetaDataSymbolMapper _symbolMapper; 33 | 34 | /// 35 | /// Provider The TheData Rest Api client instance. 36 | /// 37 | private readonly ThetaDataRestApiClient _restApiClient; 38 | 39 | /// 40 | /// Collection of pre-defined option rights. 41 | /// Initialized for performance optimization as the API only returns strike price without indicating the right. 42 | /// 43 | private readonly IEnumerable optionRights = new[] { OptionRight.Call, OptionRight.Put }; 44 | 45 | /// 46 | /// Indicates whether the warning for invalid has been fired. 47 | /// 48 | private volatile bool _unsupportedSecurityTypeWarningFired; 49 | 50 | /// 51 | /// Initializes a new instance of the 52 | /// 53 | /// The TheData mapping between Lean symbols and brokerage specific symbols. 54 | /// The client for interacting with the Theta Data REST API by sending HTTP requests 55 | public ThetaDataOptionChainProvider(ThetaDataSymbolMapper symbolMapper, ThetaDataRestApiClient restApiClient) 56 | { 57 | _symbolMapper = symbolMapper; 58 | _restApiClient = restApiClient; 59 | } 60 | 61 | /// 62 | public IEnumerable GetOptionContractList(Symbol symbol, DateTime date) 63 | { 64 | if ((symbol.SecurityType.IsOption() && symbol.SecurityType == SecurityType.FutureOption) || 65 | (symbol.HasUnderlying && symbol.Underlying.SecurityType != SecurityType.Equity && symbol.Underlying.SecurityType != SecurityType.Index)) 66 | { 67 | if (!_unsupportedSecurityTypeWarningFired) 68 | { 69 | _unsupportedSecurityTypeWarningFired = true; 70 | Log.Trace($"{nameof(ThetaDataOptionChainProvider)}.{nameof(GetOptionContractList)}: Unsupported security type {symbol.SecurityType}"); 71 | } 72 | yield break; 73 | } 74 | 75 | var underlying = symbol.SecurityType.IsOption() ? symbol.Underlying : symbol; 76 | var optionsSecurityType = underlying.SecurityType == SecurityType.Index ? SecurityType.IndexOption : SecurityType.Option; 77 | var optionStyle = optionsSecurityType.DefaultOptionStyle(); 78 | 79 | // just using quote, which is the most inclusive 80 | var request = new RestRequest($"/list/contracts/option/quote", Method.GET); 81 | 82 | request.AddQueryParameter(RequestParameters.StartDate, date.ConvertToThetaDataDateFormat()); 83 | request.AddQueryParameter("root", underlying.Value); 84 | 85 | foreach (var option in _restApiClient.ExecuteRequest>(request).SelectMany(x => x.Response)) 86 | { 87 | yield return _symbolMapper.GetLeanSymbol(underlying.Value, optionsSecurityType, underlying.ID.Market, optionStyle, 88 | option.Expiry, option.Strike, option.Right == "C" ? OptionRight.Call : OptionRight.Put, underlying); 89 | } 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/ThetaDataQueueUniverseProvider.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using QuantConnect.Interfaces; 17 | 18 | namespace QuantConnect.Lean.DataSource.ThetaData 19 | { 20 | /// 21 | /// ThetaData.net implementation of 22 | /// 23 | public partial class ThetaDataProvider : IDataQueueUniverseProvider 24 | { 25 | /// 26 | /// Provides the full option chain for a given underlying. 27 | /// 28 | private IOptionChainProvider _optionChainProvider; 29 | 30 | /// 31 | public bool CanPerformSelection() 32 | { 33 | return IsConnected; 34 | } 35 | 36 | /// 37 | public IEnumerable LookupSymbols(Symbol symbol, bool includeExpired, string? securityCurrency = null) 38 | { 39 | var utcNow = TimeProvider.GetUtcNow(); 40 | var symbols = GetOptionChain(symbol, utcNow.Date); 41 | 42 | foreach (var optionSymbol in symbols) 43 | { 44 | yield return optionSymbol; 45 | } 46 | } 47 | 48 | /// 49 | /// Retrieves a collection of option contracts for a given security symbol and requested date. 50 | /// We have returned option contracts from to a future date, excluding expired contracts. 51 | /// 52 | /// The unique security identifier for which option contracts are to be retrieved. 53 | /// The date from which to find option contracts. 54 | /// A collection of option contracts. 55 | public IEnumerable GetOptionChain(Symbol symbol, DateTime requestedDate) => _optionChainProvider.GetOptionContractList(symbol, requestedDate); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/ThetaDataRestApiClient.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using RestSharp; 17 | using Newtonsoft.Json; 18 | using QuantConnect.Util; 19 | using QuantConnect.Logging; 20 | using QuantConnect.Configuration; 21 | using System.Collections.Concurrent; 22 | using QuantConnect.Lean.DataSource.ThetaData.Models.Rest; 23 | using QuantConnect.Lean.DataSource.ThetaData.Models.Wrappers; 24 | using QuantConnect.Lean.DataSource.ThetaData.Models.Interfaces; 25 | 26 | namespace QuantConnect.Lean.DataSource.ThetaData 27 | { 28 | /// 29 | /// Represents a client for interacting with the Theta Data REST API by sending HTTP requests. 30 | /// 31 | public class ThetaDataRestApiClient 32 | { 33 | /// 34 | /// The maximum number of times a failed request will be retried. 35 | /// 36 | private const int MaxRequestRetries = 2; 37 | 38 | /// 39 | /// Represents the API version used in the REST API endpoints. 40 | /// 41 | /// 42 | /// This constant defines the version of the API to be used in requests. 43 | /// It is appended to the base URL to form the complete endpoint path. 44 | /// 45 | private const string ApiVersion = "/v2"; 46 | 47 | /// 48 | /// Represents the base URL for the REST API. 49 | /// 50 | private readonly string RestApiBaseUrl = Config.Get("thetadata-rest-url", "http://127.0.0.1:25510"); 51 | 52 | /// 53 | /// Represents a client for making RESTFul API requests. 54 | /// 55 | private readonly RestClient _restClient; 56 | 57 | /// 58 | /// Represents a RateGate instance used to control the rate of certain operations. 59 | /// 60 | private readonly RateGate? _rateGate; 61 | 62 | /// 63 | /// Initializes a new instance of the 64 | /// 65 | /// User's ThetaData subscription price plan. 66 | public ThetaDataRestApiClient(RateGate rateGate) 67 | { 68 | _restClient = new RestClient(RestApiBaseUrl + ApiVersion); 69 | _rateGate = rateGate; 70 | } 71 | 72 | /// 73 | /// Executes a REST request in parallel and returns the results synchronously. 74 | /// 75 | /// The type of object that implements the interface. 76 | /// The REST request to execute. 77 | /// A collection of objects that implement the interface. 78 | public IEnumerable ExecuteRequest(RestRequest? request) where T : IBaseResponse 79 | { 80 | var parameters = GetSpecificQueryParameters(request.Parameters, RequestParameters.IntervalInMilliseconds, RequestParameters.StartDate, RequestParameters.EndDate); 81 | 82 | if (parameters.Count != 3) 83 | { 84 | return ExecuteRequestAsync(request).SynchronouslyAwaitTaskResult(); 85 | } 86 | 87 | var intervalInDay = parameters[RequestParameters.IntervalInMilliseconds] switch 88 | { 89 | "0" => 1, 90 | "1000" or "60000" => 30, 91 | "3600000" => 90, 92 | _ => throw new NotImplementedException($"{nameof(ThetaDataRestApiClient)}.{nameof(ExecuteRequestParallelAsync)}: The interval '{parameters[RequestParameters.IntervalInMilliseconds]}' is not supported.") 93 | }; 94 | 95 | var startDate = parameters[RequestParameters.StartDate].ConvertFromThetaDataDateFormat(); 96 | var endDate = parameters[RequestParameters.EndDate].ConvertFromThetaDataDateFormat(); 97 | 98 | if ((endDate - startDate).TotalDays <= intervalInDay) 99 | { 100 | return ExecuteRequestAsync(request).SynchronouslyAwaitTaskResult(); 101 | } 102 | 103 | return ExecuteRequestParallelAsync(request, startDate, endDate, intervalInDay).SynchronouslyAwaitTaskResult(); 104 | } 105 | 106 | /// 107 | /// Executes a REST request and deserializes the response content into an object. 108 | /// 109 | /// The type of objects that implement the base response interface. 110 | /// The REST request to execute. 111 | /// An enumerable collection of objects that implement the specified base response interface. 112 | /// Thrown when an error occurs during the execution of the request or when the response is invalid. 113 | private async IAsyncEnumerable ExecuteRequestWithPaginationAsync(RestRequest? request) where T : IBaseResponse 114 | { 115 | var retryCount = 0; 116 | while (request != null) 117 | { 118 | Log.Debug($"{nameof(ThetaDataRestApiClient)}.{nameof(ExecuteRequest)}: URI: {_restClient.BuildUri(request)}"); 119 | 120 | _rateGate?.WaitToProceed(); 121 | 122 | using (StopwatchWrapper.StartIfEnabled($"{nameof(ThetaDataRestApiClient)}.{nameof(ExecuteRequest)}: Executed request to {request.Resource}")) 123 | { 124 | var response = await _restClient.ExecuteAsync(request).ConfigureAwait(false); 125 | 126 | // docs: https://http-docs.thetadata.us/docs/theta-data-rest-api-v2/3ucp87xxgy8d3-error-codes 127 | if ((int)response.StatusCode == 472) 128 | { 129 | Log.Debug($"{nameof(ThetaDataRestApiClient)}.{nameof(ExecuteRequest)}:No data found for the specified request (Status Code: 472) by {response.ResponseUri}"); 130 | yield break; 131 | } 132 | 133 | if (response == null || response.StatusCode != System.Net.HttpStatusCode.OK) 134 | { 135 | if (retryCount < MaxRequestRetries) 136 | { 137 | retryCount++; 138 | await Task.Delay(1000 * retryCount).ConfigureAwait(false); 139 | continue; 140 | } 141 | throw new Exception($"{nameof(ThetaDataRestApiClient)}.{nameof(ExecuteRequest)}: No response received for request to {request.Resource}. Error message: {response?.ErrorMessage ?? "No error message available."}"); 142 | } 143 | 144 | var res = JsonConvert.DeserializeObject(response.Content); 145 | 146 | yield return res; 147 | 148 | if (res?.Header.NextPage != null) 149 | { 150 | request = new RestRequest(Method.GET) { Resource = new Uri(res.Header.NextPage).AbsolutePath.Replace(ApiVersion, string.Empty) }; 151 | } 152 | else 153 | { 154 | request = null; 155 | 156 | } 157 | } 158 | } 159 | } 160 | 161 | /// 162 | /// Executes a REST request asynchronously and retrieves a paginated response. 163 | /// 164 | /// The type of response that implements . 165 | /// The REST request to execute. 166 | /// 167 | /// A task that represents the asynchronous operation, returning an 168 | /// containing the responses received from paginated requests. 169 | /// 170 | private async Task> ExecuteRequestAsync(RestRequest? request) where T : IBaseResponse 171 | { 172 | var responses = new List(); 173 | await foreach (var response in ExecuteRequestWithPaginationAsync(request)) 174 | { 175 | responses.Add(response); 176 | } 177 | return responses; 178 | } 179 | 180 | /// 181 | /// Executes a REST request in parallel over multiple date ranges, ensuring efficient batch processing. 182 | /// A maximum of 4 parallel requests are made at a time to avoid excessive API load. 183 | /// 184 | /// The type of response that implements . 185 | /// The REST request to execute. 186 | /// The start date of the data range. 187 | /// The end date of the data range. 188 | /// 189 | /// The interval in days for splitting the date range into smaller requests. 190 | /// 191 | /// 192 | /// A task representing the asynchronous operation, returning an 193 | /// containing the aggregated responses from all parallel requests. 194 | /// 195 | private async Task> ExecuteRequestParallelAsync(RestRequest? request, DateTime startDate, DateTime endDate, int intervalInDay) where T : IBaseResponse 196 | { 197 | var resultDict = new ConcurrentDictionary>(); 198 | 199 | var dateRanges = ThetaDataExtensions.GenerateDateRangesWithInterval(startDate, endDate, intervalInDay).Select((range, index) => (range, index)).ToList(); 200 | 201 | await Parallel.ForEachAsync(dateRanges, async (item, _) => 202 | { 203 | var (dateRange, index) = item; 204 | var requestClone = new RestRequest(request.Resource, request.Method); 205 | 206 | foreach (var param in request.Parameters) 207 | { 208 | switch (param.Name) 209 | { 210 | case RequestParameters.StartDate: 211 | requestClone.AddOrUpdateParameter(RequestParameters.StartDate, dateRange.startDate.ConvertToThetaDataDateFormat(), ParameterType.QueryString); 212 | break; 213 | case RequestParameters.EndDate: 214 | requestClone.AddOrUpdateParameter(RequestParameters.EndDate, dateRange.endDate.ConvertToThetaDataDateFormat(), ParameterType.QueryString); 215 | break; 216 | default: 217 | requestClone.AddParameter(param); 218 | break; 219 | } 220 | } 221 | 222 | var results = new List(); 223 | await foreach (var response in ExecuteRequestWithPaginationAsync(requestClone)) 224 | { 225 | results.Add(response); 226 | } 227 | resultDict[index] = results; 228 | }).ConfigureAwait(false); 229 | return resultDict.OrderBy(kvp => kvp.Key).SelectMany(kvp => kvp.Value); 230 | } 231 | 232 | /// 233 | /// Extracts specific query parameters from a collection of request parameters. 234 | /// 235 | /// The collection of request parameters. 236 | /// The parameter names to find. 237 | /// A dictionary of the matching query parameters and their values. 238 | /// Thrown when a required parameter is missing or has an invalid value. 239 | private Dictionary GetSpecificQueryParameters(IReadOnlyCollection requestParameters, params string[] findingParamNames) 240 | { 241 | var parameters = new Dictionary(findingParamNames.Length); 242 | foreach (var parameter in requestParameters) 243 | { 244 | if (parameter?.Name != null && findingParamNames.Contains(parameter.Name, StringComparer.InvariantCultureIgnoreCase)) 245 | { 246 | var value = parameter.Value?.ToString(); 247 | if (string.IsNullOrEmpty(value)) 248 | { 249 | throw new ArgumentException($"The value for the parameter '{parameter.Name}' is null or empty. Ensure that this parameter has a valid value.", nameof(requestParameters)); 250 | } 251 | 252 | parameters[parameter.Name] = value; 253 | } 254 | } 255 | return parameters; 256 | } 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /QuantConnect.ThetaData/ThetaDataWebSocketClientWrapper.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. 3 | * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software 10 | * distributed under the License is distributed on an "AS IS" BASIS, 11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | * See the License for the specific language governing permissions and 13 | * limitations under the License. 14 | */ 15 | 16 | using Newtonsoft.Json; 17 | using QuantConnect.Logging; 18 | using QuantConnect.Brokerages; 19 | using QuantConnect.Configuration; 20 | using QuantConnect.Lean.DataSource.ThetaData.Models.Interfaces; 21 | 22 | namespace QuantConnect.Lean.DataSource.ThetaData 23 | { 24 | /// 25 | /// Provides a WebSocket client wrapper for ThetaData.net. 26 | /// 27 | public class ThetaDataWebSocketClientWrapper : WebSocketClientWrapper 28 | { 29 | /// 30 | /// Represents the base URL endpoint for receiving stream messages from Theta Data. 31 | /// A single connection to this endpoint is required for both sending streaming requests and receiving messages. 32 | /// 33 | private static readonly string BaseUrl = Config.Get("thetadata-ws-url", "ws://127.0.0.1:25520/v1/events"); 34 | 35 | /// 36 | /// Represents the array of required subscription channels for receiving real-time market data. 37 | /// Subscribing to these channels allows access to specific types of data streams from the Options Price Reporting Authority (OPRA) feed. 38 | /// 39 | /// 40 | /// Available Channels: 41 | /// - TRADE: This channel provides every trade executed for a specified contract reported on the OPRA feed. 42 | /// - QUOTE: This channel provides every National Best Bid and Offer (NBBO) quote for US Options reported on the OPRA feed for the specified contract. 43 | /// 44 | private static readonly string[] Channels = { "TRADE", "QUOTE" }; 45 | 46 | /// 47 | /// Represents a method that handles messages received from a WebSocket. 48 | /// 49 | private readonly Action _messageHandler; 50 | 51 | /// 52 | /// Provides the ThetaData mapping between Lean symbols and brokerage specific symbols. 53 | /// 54 | private readonly ISymbolMapper _symbolMapper; 55 | 56 | /// 57 | /// The maximum number of contracts that can be streamed simultaneously under the subscription plan. 58 | /// 59 | /// 60 | private readonly uint _maxStreamingContracts; 61 | 62 | /// 63 | /// Ensures thread-safe synchronization when updating 64 | /// 65 | private readonly object _lock = new(); 66 | 67 | /// 68 | /// Represents the current amount of subscribed symbols. 69 | /// 70 | private volatile uint _subscribedSymbolCount; 71 | 72 | /// 73 | /// Represents a way of tracking streaming requests made. 74 | /// The field should be increased for each new stream request made. 75 | /// 76 | private int _idRequestCount = 0; 77 | 78 | /// 79 | /// Initializes a new instance of the 80 | /// 81 | /// Provides the mapping between Lean symbols and brokerage specific symbols. 82 | /// The maximum number of contracts that can be streamed simultaneously under the subscription plan. 83 | /// The method that handles messages received from the WebSocket client. 84 | public ThetaDataWebSocketClientWrapper(ISymbolMapper symbolMapper, uint maxStreamingContracts, Action messageHandler, EventHandler OnError) 85 | { 86 | Initialize(BaseUrl); 87 | 88 | _symbolMapper = symbolMapper; 89 | _messageHandler = messageHandler; 90 | _maxStreamingContracts = maxStreamingContracts; 91 | 92 | Closed += OnClosed; 93 | Message += OnMessage; 94 | Error += OnError; 95 | } 96 | 97 | /// 98 | /// Wraps the Close method to handle the closing of the WebSocket connection and 99 | /// ensures any ongoing streaming subscriptions are stopped before closing. 100 | /// 101 | public void CloseWebSocketConnection() 102 | { 103 | if (IsOpen) 104 | { 105 | SendStopPreviousStreamingSubscriptions(); 106 | Close(); 107 | } 108 | } 109 | 110 | /// 111 | /// Adds the specified symbols to the subscription 112 | /// 113 | /// The symbols to be added keyed by SecurityType 114 | /// Indicates whether the subscription process is a resubscription, to prevent an increase in the count. 115 | public bool Subscribe(IEnumerable symbols, bool isReSubscribeProcess = false) 116 | { 117 | if (!IsOpen) 118 | { 119 | Connect(); 120 | } 121 | 122 | foreach (var symbol in symbols) 123 | { 124 | lock (_lock) 125 | { 126 | // constantly following of current amount of subscribed symbols (post increment!) 127 | if (!isReSubscribeProcess && ++_subscribedSymbolCount > _maxStreamingContracts) 128 | { 129 | throw new ArgumentException($"{nameof(ThetaDataWebSocketClientWrapper)}.{nameof(Subscribe)}: Subscription Limit Exceeded. The number of symbols you're trying to subscribe to exceeds the maximum allowed limit of {_maxStreamingContracts}. Please adjust your subscription quantity or upgrade your plan accordingly. Current subscription count: {_subscribedSymbolCount}"); 130 | } 131 | } 132 | 133 | foreach (var jsonMessage in GetContractSubscriptionMessage(true, symbol)) 134 | { 135 | SendMessage(jsonMessage); 136 | } 137 | } 138 | 139 | return true; 140 | } 141 | 142 | private IEnumerable GetContractSubscriptionMessage(bool isSubscribe, Symbol symbol) 143 | { 144 | var brokerageSymbol = _symbolMapper.GetBrokerageSymbol(symbol).Split(','); 145 | 146 | switch (symbol.SecurityType) 147 | { 148 | case SecurityType.Equity: 149 | foreach (var channel in Channels) 150 | { 151 | yield return GetMessage(isSubscribe, channel, brokerageSymbol[0], symbol.SecurityType); 152 | } 153 | break; 154 | case SecurityType.Index: 155 | yield return GetMessage(isSubscribe, "TRADE", brokerageSymbol[0], symbol.SecurityType); 156 | break; 157 | case SecurityType.Option: 158 | case SecurityType.IndexOption: 159 | foreach (var channel in Channels) 160 | { 161 | yield return GetMessageOption(isSubscribe, channel, brokerageSymbol[0], brokerageSymbol[1], brokerageSymbol[2], brokerageSymbol[3]); 162 | } 163 | break; 164 | } 165 | } 166 | 167 | /// 168 | /// Removes the specified symbols to the subscription 169 | /// 170 | /// The symbols to be removed keyed by SecurityType 171 | public bool Unsubscribe(IEnumerable symbols) 172 | { 173 | foreach (var symbol in symbols) 174 | { 175 | lock (_lock) 176 | { 177 | _subscribedSymbolCount--; 178 | } 179 | 180 | foreach (var jsonMessage in GetContractSubscriptionMessage(false, symbol)) 181 | { 182 | SendMessage(jsonMessage); 183 | } 184 | } 185 | return true; 186 | } 187 | 188 | /// 189 | /// Constructs a message for subscribing or unsubscribing to an option contract on a specified channel. 190 | /// 191 | /// A boolean value indicating whether to subscribe (true) or unsubscribe (false). 192 | /// The name of the channel to subscribe or unsubscribe from. 193 | /// The ticker symbol of the financial instrument. 194 | /// The expiration date of the option contract. 195 | /// The strike price of the option contract. 196 | /// The option type, either "C" for call or "P" for put. 197 | /// A JSON string representing the constructed message. 198 | private string GetMessageOption(bool isSubscribe, string channelName, string ticker, string expirationDate, string strikePrice, string optionRight) 199 | { 200 | return JsonConvert.SerializeObject(new 201 | { 202 | msg_type = "STREAM", 203 | sec_type = "OPTION", 204 | req_type = channelName, 205 | add = isSubscribe, 206 | id = _idRequestCount, 207 | contract = new 208 | { 209 | root = ticker, 210 | expiration = expirationDate, 211 | strike = strikePrice, 212 | right = optionRight 213 | } 214 | }); 215 | } 216 | 217 | /// 218 | /// Constructs a message for subscribing or unsubscribing to a financial instrument on a specified channel. 219 | /// 220 | /// A boolean value indicating whether to subscribe (true) or unsubscribe (false). 221 | /// The name of the channel to subscribe or unsubscribe from. 222 | /// The ticker symbol of the financial instrument. 223 | /// The type of the security. 224 | /// A JSON string representing the constructed message. 225 | /// Thrown when the security type is not supported. 226 | private string GetMessage(bool isSubscribe, string channelName, string ticker, SecurityType securityType) 227 | { 228 | var sec_type = securityType switch 229 | { 230 | SecurityType.Equity => "STOCK", 231 | SecurityType.Index => "INDEX", 232 | _ => throw new NotSupportedException($"{nameof(ThetaDataWebSocketClientWrapper)}.{nameof(GetMessage)}: Security type {securityType} is not supported.") 233 | }; 234 | 235 | return JsonConvert.SerializeObject(new 236 | { 237 | msg_type = "STREAM", 238 | sec_type = sec_type, 239 | req_type = channelName, 240 | add = isSubscribe, 241 | id = _idRequestCount, 242 | contract = new { root = ticker } 243 | }); 244 | } 245 | 246 | /// 247 | /// Event handler for processing WebSocket messages. 248 | /// 249 | /// The object that raised the event. 250 | /// The WebSocket message received. 251 | private void OnMessage(object? sender, WebSocketMessage webSocketMessage) 252 | { 253 | var e = (TextMessage)webSocketMessage.Data; 254 | 255 | _messageHandler?.Invoke(e.Message); 256 | } 257 | 258 | /// 259 | /// Event handler for processing WebSocket close data. 260 | /// 261 | /// The object that raised the event. 262 | /// The WebSocket Close Data received. 263 | private void OnClosed(object? sender, WebSocketCloseData webSocketCloseData) 264 | { 265 | Log.Trace($"{nameof(ThetaDataWebSocketClientWrapper)}.{nameof(OnClosed)}: {webSocketCloseData.Reason}"); 266 | } 267 | 268 | /// 269 | /// Wraps the send method to send a JSON message over the WebSocket connection 270 | /// and increments the request count. 271 | /// 272 | /// The JSON message to be sent. 273 | private void SendMessage(string jsonMessage) 274 | { 275 | Send(jsonMessage); 276 | Interlocked.Increment(ref _idRequestCount); 277 | } 278 | 279 | /// 280 | /// Sends a request to stop all previous streaming subscriptions. 281 | /// This is crucial to avoid any conflicts or unexpected behavior from previous sessions. 282 | /// For more details, refer to the official documentation: 283 | /// https://http-docs.thetadata.us/docs/theta-data-rest-api-v2/a017d29vrw1q0-stop-all-streams 284 | /// 285 | private void SendStopPreviousStreamingSubscriptions() 286 | { 287 | Log.Debug($"{nameof(ThetaDataWebSocketClientWrapper)}.{nameof(SendStopPreviousStreamingSubscriptions)}: Sending request to stop all previous streaming subscriptions to avoid conflicts and ensure clean state for new sessions."); 288 | Send(JsonConvert.SerializeObject(new { msg_type = "STOP" })); 289 | } 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![LEAN Data Source SDK](http://cdn.quantconnect.com.s3.us-east-1.amazonaws.com/datasources/Github_LeanDataSourceSDK.png) 2 | 3 | # Lean ThetaData DataSource Plugin 4 | 5 | [![Build & Test](https://github.com/QuantConnect/Lean.DataSource.ThetaData/actions/workflows/gh-actions.yml/badge.svg)](https://github.com/QuantConnect/Lean.DataSource.ThetaData/actions/workflows/gh-actions.yml) 6 | 7 | Welcome to the ThetaData Library repository! This library, built on .NET 6, provides seamless integration with the QuantConnect LEAN Algorithmic Trading Engine. It empowers users to interact with ThetaData's financial dataset to create powerful trading algorithms. 8 | 9 | ## Introduction 10 | ThetaData Library is an open-source project written in C#, designed to simplify the process of accessing real-time and historical financial market data. With support for Options Data across all exchanges and low latency, it offers a comprehensive solution for algorithmic trading. 11 | 12 | ## Features 13 | ### Easy Integration with QuantConnect LEAN Algorithmic Trading Engine 14 | Seamlessly incorporate ThetaData into your trading strategies within the QuantConnect LEAN environment. 15 | 16 | ### Rich Financial Data 17 | Access a wealth of financial data including real-time and historical information. Subscribe to different option contracts with various expiry dates, strikes, and rights. 18 | 19 | ### Flexible Configuration 20 | Customize the library to suit your needs with flexible configuration options. 21 | 22 | ### Symbol SecurityType Support 23 | #### Historical Data 24 | - [x] Equity 25 | - [x] Equity Option 26 | - [x] Index 27 | - [x] Index Option 28 | #### Real-time Updates 29 | - [x] Equity 30 | - [x] Equity Option 31 | - [x] Index - [support tickers list](https://http-docs.thetadata.us/docs/theta-data-rest-api-v2/s1ezbyfni6rw0-index-option-tickers) 32 | - [x] IndexOption - [support tickers list](https://http-docs.thetadata.us/docs/theta-data-rest-api-v2/s1ezbyfni6rw0-index-option-tickers) 33 | ### Backtesting and Research 34 | Utilize the power of QuantConnect.LEAN CLI to test and optimize your trading algorithms in both backtest and research modes. 35 | 36 | ## Getting Started 37 | You can use the following command line arguments to launch the [LEAN CLI](https://github.com/quantConnect/Lean-cli) pip project with ThetaData. For more detailed information, refer to the [ThetaData](https://www.quantconnect.com/docs/v2/lean-cli/datasets/theta-data) documentation. 38 | 39 | ### Downloading Data 40 | 41 | ``` 42 | lean data download --data-provider-historical ThetaData --data-type Trade --resolution Daily --security-type Option --ticker NVDA,AMD --start 20240303 --end 20240404 --thetadata-subscription-plan Standard 43 | ``` 44 | ### Backtesting 45 | ``` 46 | lean backtest "My Project" --data-provider-historical ThetaData --thetadata-subscription-plan Standard 47 | ``` 48 | ### Jupyter Research Notebooks 49 | ``` 50 | lean research "My Project" --data-provider-historical ThetaData --thetadata-subscription-plan Standard 51 | ``` 52 | ### Live Trading 53 | ``` 54 | lean live deploy "My Project" --data-provider-live ThetaData --thetadata-subscription-plan Standard --brokerage "Paper Trading" 55 | ``` 56 | 57 | ## Contributing 58 | Contributions to the project are highly encouraged! Feel free to open issues, submit pull requests, or contribute in any way you see fit. 59 | 60 | ## Installation 61 | To contribute to the ThetaData API Connector Library for .NET 6 within QuantConnect LEAN, follow these steps: 62 | 1. Obtain [ThetaData client](https://http-docs.thetadata.us/docs/theta-data-rest-api-v2/4g9ms9h4009k0-getting-started) and follow thier instaction to run client. 63 | 2. Fork the Project: Fork the repository by clicking the "Fork" button at the top right of the GitHub page. 64 | 3. Clone Your Forked Repository: 65 | ``` 66 | https://github.com/QuantConnect/Lean.DataSource.ThetaData.git 67 | ``` 68 | 4. Configuration: 69 | - [optional] Set the thetadata-subscription-plan (by default: Free) 70 | ``` 71 | { 72 | "thetadata-subscription-plan": "" 73 | } 74 | ``` 75 | 76 | ## Price Plan 77 | For detailed information on ThetaData's pricing plans, please refer to the [ThetaData Pricing](https://www.thetadata.net/subscribe) page. 78 | 79 | ## Documentation 80 | For detailed documentation on how to use ThetaData Library, please visit [documentation](https://http-docs.thetadata.us/docs/theta-data-rest-api-v2/4g9ms9h4009k0-getting-started). 81 | 82 | ## License 83 | This project is licensed under the MIT License - see the [LICENSE](https://github.com/QuantConnect/Lean.DataSource.ThetaData/blob/master/LICENSE) file for details. 84 | 85 | Happy coding and algorithmic trading! 📈💻 86 | -------------------------------------------------------------------------------- /thetadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "ThetaData provides a high-performance desktop interface to dozens of market data feeds. The QuantConnect integration enables research, backtesting, optimization, and live trading across multiple asset classes. ThetaData utilizes a specialized terminal built on the Java SDK.", 3 | "platforms-features": [ 4 | { 5 | "Platform Support": ["Cloud Platform", "Local Platform", "LEAN CLI"], 6 | "Download Data": [0, 1, 1], 7 | "Backtesting": [0, 1, 1], 8 | "Optimization": [0, 1, 1], 9 | "Live Trading": [0, 1, 1] 10 | } 11 | ], 12 | "data-supported": ["Equity", "Equity Options", "Indexes", "Index Options"], 13 | "documentation": "/docs/v2/lean-cli/datasets/thetadata", 14 | "more-information": "https://www.thetadata.net/", 15 | "module-specification": { 16 | "download": { 17 | "data-types": [ 18 | "OpenInterest", 19 | "Trade", 20 | "Quote", 21 | "Universe" 22 | ], 23 | "resolutions": [ 24 | "Tick", 25 | "Second", 26 | "Minute", 27 | "Hour", 28 | "Daily" 29 | ], 30 | "security-types": [ 31 | "Equity", 32 | "Option", 33 | "Index", 34 | "IndexOption" 35 | ], 36 | "markets": [ 37 | "USA" 38 | ] 39 | } 40 | } 41 | } 42 | --------------------------------------------------------------------------------