├── .editorconfig ├── .github ├── FUNDING.yml └── workflows │ └── dotnet.yml ├── .gitignore ├── LICENSE ├── README.md ├── Tulip.sln ├── src └── Tulip.NETCore │ ├── ArrayExtensions.cs │ ├── AssemblyInfo.cs │ ├── GlobalUsings.cs │ ├── Indicator.cs │ ├── Indicators.cs │ ├── Indicators │ ├── TI_Abs.cs │ ├── TI_Acos.cs │ ├── TI_Ad.cs │ ├── TI_Add.cs │ ├── TI_Adosc.cs │ ├── TI_Adx.cs │ ├── TI_Adxr.cs │ ├── TI_Ao.cs │ ├── TI_Apo.cs │ ├── TI_Aroon.cs │ ├── TI_AroonOsc.cs │ ├── TI_Asin.cs │ ├── TI_Atan.cs │ ├── TI_Atr.cs │ ├── TI_AvgPrice.cs │ ├── TI_Bbands.cs │ ├── TI_Bop.cs │ ├── TI_Cci.cs │ ├── TI_Ceil.cs │ ├── TI_Cmo.cs │ ├── TI_Cos.cs │ ├── TI_Cosh.cs │ ├── TI_Crossany.cs │ ├── TI_Crossover.cs │ ├── TI_Cvi.cs │ ├── TI_Decay.cs │ ├── TI_Dema.cs │ ├── TI_Di.cs │ ├── TI_Div.cs │ ├── TI_Dm.cs │ ├── TI_Dpo.cs │ ├── TI_Dx.cs │ ├── TI_Edecay.cs │ ├── TI_Ema.cs │ ├── TI_Emv.cs │ ├── TI_Exp.cs │ ├── TI_Fisher.cs │ ├── TI_Floor.cs │ ├── TI_Fosc.cs │ ├── TI_Hma.cs │ ├── TI_Kama.cs │ ├── TI_Kvo.cs │ ├── TI_Lag.cs │ ├── TI_LinReg.cs │ ├── TI_LinRegIntercept.cs │ ├── TI_LinRegSlope.cs │ ├── TI_Ln.cs │ ├── TI_Log10.cs │ ├── TI_Macd.cs │ ├── TI_MarketFi.cs │ ├── TI_Mass.cs │ ├── TI_Max.cs │ ├── TI_Md.cs │ ├── TI_MedPrice.cs │ ├── TI_Mfi.cs │ ├── TI_Min.cs │ ├── TI_Mom.cs │ ├── TI_Msw.cs │ ├── TI_Mul.cs │ ├── TI_Natr.cs │ ├── TI_Nvi.cs │ ├── TI_Obv.cs │ ├── TI_Ppo.cs │ ├── TI_Psar.cs │ ├── TI_Pvi.cs │ ├── TI_Qstick.cs │ ├── TI_Roc.cs │ ├── TI_RocR.cs │ ├── TI_Round.cs │ ├── TI_Rsi.cs │ ├── TI_Sin.cs │ ├── TI_Sinh.cs │ ├── TI_Sma.cs │ ├── TI_Sqrt.cs │ ├── TI_StdDev.cs │ ├── TI_StdErr.cs │ ├── TI_Stoch.cs │ ├── TI_StochRsi.cs │ ├── TI_Sub.cs │ ├── TI_Sum.cs │ ├── TI_Tan.cs │ ├── TI_Tanh.cs │ ├── TI_Tema.cs │ ├── TI_ToDeg.cs │ ├── TI_ToRad.cs │ ├── TI_Tr.cs │ ├── TI_Trima.cs │ ├── TI_Trix.cs │ ├── TI_Trunc.cs │ ├── TI_Tsf.cs │ ├── TI_TypPrice.cs │ ├── TI_UltOsc.cs │ ├── TI_Var.cs │ ├── TI_Vhf.cs │ ├── TI_Vidya.cs │ ├── TI_Volatility.cs │ ├── TI_Vosc.cs │ ├── TI_Vwma.cs │ ├── TI_Wad.cs │ ├── TI_WcPrice.cs │ ├── TI_Wilders.cs │ ├── TI_WillR.cs │ ├── TI_Wma.cs │ └── TI_ZlEma.cs │ ├── Tinet.cs │ └── Tulip.NETCore.csproj ├── tests └── Tulip.NETCore.Tests │ ├── AssemblyInfo.cs │ ├── DataSets │ ├── atoz.json │ ├── extra.json │ ├── testdata.schema.json │ └── untest.json │ ├── Indicator_Tests.cs │ ├── JsonFileDataAttribute.cs │ ├── Models │ ├── JsonExtensions.cs │ └── TestDataModel.cs │ └── Tulip.NETCore.Tests.csproj └── tulip.png /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | 3 | [*] 4 | 5 | #### Core EditorConfig Options #### 6 | 7 | # Indentation and spacing 8 | indent_style=space 9 | indent_size=2 10 | trim_trailing_whitespace=true 11 | charset=utf-8 12 | max_line_length=140 13 | 14 | # New line preferences 15 | insert_final_newline=true 16 | 17 | #### .NET Coding Conventions #### 18 | 19 | [*.cs] 20 | 21 | indent_size=4 22 | 23 | # Microsoft .NET properties 24 | dotnet_separate_import_directive_groups=false 25 | dotnet_sort_system_directives_first=true 26 | dotnet_style_predefined_type_for_locals_parameters_members=true:suggestion 27 | dotnet_style_predefined_type_for_member_access=false:suggestion 28 | csharp_new_line_before_open_brace=all 29 | csharp_new_line_before_else=true 30 | csharp_space_after_cast=true 31 | csharp_space_after_keywords_in_control_flow_statements=true 32 | dotnet_style_collection_initializer=true:suggestion 33 | dotnet_style_object_initializer=true:suggestion 34 | csharp_style_var_when_type_is_apparent=true:suggestion 35 | csharp_style_expression_bodied_methods=true:suggestion 36 | csharp_prefer_braces=true:warning 37 | # ReSharper properties 38 | resharper_place_accessorholder_attribute_on_same_line=false 39 | resharper_csharp_wrap_after_declaration_lpar=true 40 | resharper_csharp_wrap_parameters_style=chop_if_long 41 | resharper_max_formal_parameters_on_line=3 42 | resharper_blank_lines_around_single_line_auto_property=1 43 | resharper_csharp_keep_blank_lines_in_declarations=1 44 | 45 | #### XML Coding Conventions #### 46 | 47 | [*.xml] 48 | ij_xml_space_inside_empty_tag = true 49 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | buy_me_a_coffee: hmG3 2 | -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: .NET 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | operating-system: [ubuntu-latest, windows-latest, macOS-latest] 14 | runs-on: ${{ matrix.operating-system }} 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-dotnet@v4 18 | with: 19 | dotnet-version: 8.0.x 20 | - name: Test & coverage 21 | run: dotnet test --logger "console;verbosity=normal" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=coverage/opencover-coverage.xml 22 | - name: Upload coverage to Codecov 23 | if: ${{ matrix.operating-system == 'ubuntu-latest' }} 24 | uses: codecov/codecov-action@v4 25 | with: 26 | name: codecov-tulip 27 | file: ./tests/Tulip.NETCore.Tests/coverage/opencover-coverage.xml 28 | token: ${{ secrets.CODECOV_TOKEN }} 29 | flags: unittests 30 | fail_ci_if_error: true 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tulip Indicators for .NET Core 2 | 3 | Tulip.NETCore is an **unofficial** .NET port of the Tulip Indicators (TI) - open-source ANSI C library of functions for technical analysis of financial time series data. It is written entirely in C# for the .NET platform. This repository contains the source code of the latest version of the library released under GNU Lesser General Public License v3.0. This makes it free to use, even in commercial projects, with some restrictions. 4 | 5 | [![License](https://img.shields.io/github/license/hmG3/Tulip.NETCore?logo=open-source-initiative)](https://opensource.org/licenses/LGPL-3.0) 6 | [![NuGet](https://img.shields.io/nuget/v/Tulip.NETCore.svg?logo=nuget)](https://nuget.org/packages/Tulip.NETCore) 7 | [![NuGet](https://img.shields.io/nuget/dt/Tulip.NETCore.svg)](https://nuget.org/packages/Tulip.NETCore) 8 | [![Build status](https://github.com/hmG3/Tulip.NETCore/actions/workflows/dotnet.yml/badge.svg)](https://github.com/hmG3/Tulip.NETCore/actions/workflows/dotnet.yml) 9 | [![Coverage status](https://codecov.io/gh/hmG3/Tulip.NETCore/graph/badge.svg?token=NP4J6Q69AQ)](https://codecov.io/gh/hmG3/Tulip.NETCore) 10 | [![Code quality](https://app.codacy.com/project/badge/Grade/17065bed437d4409a4b0cfe5343c1d87)](https://app.codacy.com/gh/hmG3/Tulip.NETCore/dashboard) 11 | 12 | ## Supported frameworks 13 | 14 | - .NET 8 15 | 16 | ## Installation 17 | 18 | A compiled binary of Tulip.NETCore 0.8 is provided by the `Tulip.NETCore` NuGet package. To install it, run the following command in the Package Manager Console: 19 | 20 | ```shell 21 | Install-Package Tulip.NETCore 22 | ``` 23 | 24 | or in .NET Core CLI: 25 | 26 | ```shell 27 | dotnet add package Tulip.NETCore 28 | ``` 29 | 30 | ## Updates and bug fixes 31 | 32 | For more recent versions please refer to: 33 | 34 | - [Homepage of Tulip Indicators](https://tulipindicators.org/) 35 | - [Official GitHub repository](https://github.com/TulipCharts/tulipindicators) 36 | -------------------------------------------------------------------------------- /Tulip.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.9.34607.119 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tulip.NETCore", "src\Tulip.NETCore\Tulip.NETCore.csproj", "{9E84826B-697A-42BD-BE10-3F5178D1CDDE}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tulip.NETCore.Tests", "tests\Tulip.NETCore.Tests\Tulip.NETCore.Tests.csproj", "{0FE7C705-C64D-4958-87EA-536B97389A33}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {9E84826B-697A-42BD-BE10-3F5178D1CDDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {9E84826B-697A-42BD-BE10-3F5178D1CDDE}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {9E84826B-697A-42BD-BE10-3F5178D1CDDE}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {9E84826B-697A-42BD-BE10-3F5178D1CDDE}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {0FE7C705-C64D-4958-87EA-536B97389A33}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {0FE7C705-C64D-4958-87EA-536B97389A33}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {0FE7C705-C64D-4958-87EA-536B97389A33}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {0FE7C705-C64D-4958-87EA-536B97389A33}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {C7053D97-7D6D-4855-9800-3B803B6CBF8F} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/ArrayExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static class ArrayExtensions 4 | { 5 | public static void Deconstruct(this T[] array, out T first, out T second) 6 | { 7 | first = array[0]; 8 | second = array[1]; 9 | } 10 | 11 | public static void Deconstruct(this T[] array, out T first, out T second, out T third) 12 | { 13 | first = array[0]; 14 | second = array[1]; 15 | third = array[2]; 16 | } 17 | 18 | public static void Deconstruct(this T[] array, out T first, out T second, out T third, out T fourth) 19 | { 20 | first = array[0]; 21 | second = array[1]; 22 | third = array[2]; 23 | fourth = array[3]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | [assembly: InternalsVisibleTo("Tulip.NETCore.Tests")] 4 | [assembly: InternalsVisibleTo("TaTooIne.Benchmark")] 5 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/GlobalUsings.cs: -------------------------------------------------------------------------------- 1 | global using System; 2 | global using System.Numerics; 3 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicator.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | public sealed class Indicator 4 | { 5 | internal Indicator(string name, string fullName, string inputs, string options, string outputs) 6 | { 7 | Name = name; 8 | FullName = fullName; 9 | Inputs = inputs.Split('|'); 10 | Options = !String.IsNullOrEmpty(options) ? options.Split('|') : Array.Empty(); 11 | Outputs = outputs.Split('|'); 12 | } 13 | 14 | public string Name { get; } 15 | 16 | public string FullName { get; } 17 | 18 | public string[] Inputs { get; } 19 | 20 | public string[] Options { get; } 21 | 22 | public string[] Outputs { get; } 23 | 24 | public int Run(T[][] inputs, T[] options, T[][] outputs) where T : IFloatingPointIeee754 => 25 | Tinet.IndicatorRun(Name, inputs, options, outputs); 26 | 27 | public int Start(T[] options) where T : IFloatingPointIeee754 => Tinet.IndicatorStart(Name, options); 28 | 29 | public override string ToString() => Name; 30 | } 31 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Abs.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int AbsStart(T[] options) => 0; 6 | 7 | private static int Abs(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Abs); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Acos.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int AcosStart(T[] options) => 0; 6 | 7 | private static int Acos(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Acos); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Ad.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int AdStart(T[] options) => 0; 6 | 7 | private static int Ad(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var (high, low, close, volume) = inputs; 10 | var output = outputs[0]; 11 | 12 | T sum = T.Zero; 13 | for (var i = 0; i < size; ++i) 14 | { 15 | T hl = high[i] - low[i]; 16 | if (!T.IsZero(hl)) 17 | { 18 | sum += (close[i] - low[i] - high[i] + close[i]) / hl * volume[i]; 19 | } 20 | 21 | output[i] = sum; 22 | } 23 | 24 | return TI_OKAY; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Add.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int AddStart(T[] options) => 0; 6 | 7 | private static int Add(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple2(size, inputs[0], inputs[1], outputs[0], (d1, d2) => d1 + d2); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Adosc.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int AdOscStart(T[] options) => Int32.CreateTruncating(options[1]) - 1; 6 | 7 | private static int AdOsc(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var shortPeriod = Int32.CreateTruncating(options[0]); 10 | var longPeriod = Int32.CreateTruncating(options[1]); 11 | 12 | if (shortPeriod < 1 || longPeriod < shortPeriod) 13 | { 14 | return TI_INVALID_OPTION; 15 | } 16 | 17 | if (size <= AdOscStart(options)) 18 | { 19 | return TI_OKAY; 20 | } 21 | 22 | var (high, low, close, volume) = inputs; 23 | var output = outputs[0]; 24 | 25 | int start = longPeriod - 1; 26 | T shortPer = TTwo / T.CreateChecked(shortPeriod + 1); 27 | T longPer = TTwo / T.CreateChecked(longPeriod + 1); 28 | T sum = T.Zero; 29 | T shortEma = T.Zero; 30 | T longEma = T.Zero; 31 | int outputIndex = default; 32 | for (var i = 0; i < size; ++i) 33 | { 34 | T hl = high[i] - low[i]; 35 | if (!T.IsZero(hl)) 36 | { 37 | sum += (close[i] - low[i] - high[i] + close[i]) / hl * volume[i]; 38 | } 39 | 40 | if (i == 0) 41 | { 42 | shortEma = sum; 43 | longEma = sum; 44 | } 45 | else 46 | { 47 | shortEma = (sum - shortEma) * shortPer + shortEma; 48 | longEma = (sum - longEma) * longPer + longEma; 49 | } 50 | 51 | if (i >= start) 52 | { 53 | output[outputIndex++] = shortEma - longEma; 54 | } 55 | } 56 | 57 | return TI_OKAY; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Adx.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int AdxStart(T[] options) => (Int32.CreateTruncating(options[0]) - 1) * 2; 6 | 7 | private static int Adx(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 2) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= AdxStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low, close) = inputs; 22 | var output = outputs[0]; 23 | 24 | T atr = T.Zero; 25 | T dmUp = T.Zero; 26 | T dmDown = T.Zero; 27 | for (var i = 1; i < period; ++i) 28 | { 29 | CalcTrueRange(low, high, close, i, out T trueRange); 30 | atr += trueRange; 31 | 32 | CalcDirection(high, low, i, out T dp, out T dm); 33 | dmUp += dp; 34 | dmDown += dm; 35 | } 36 | 37 | T per = T.CreateChecked(period - 1) / T.CreateChecked(period); 38 | T invPer = T.One / T.CreateChecked(period); 39 | T adx = T.Zero; 40 | T diUp = dmUp / atr; 41 | T diDown = dmDown / atr; 42 | T dx = T.Abs(diUp - diDown) / (diUp + diDown) * THundred; 43 | adx += dx; 44 | 45 | int outputIndex = default; 46 | for (var i = period; i < size; ++i) 47 | { 48 | CalcTrueRange(low, high, close, i, out T trueRange); 49 | atr = atr * per + trueRange; 50 | 51 | CalcDirection(high, low, i, out T dp, out T dm); 52 | dmUp = dmUp * per + dp; 53 | dmDown = dmDown * per + dm; 54 | 55 | diUp = dmUp / atr; 56 | diDown = dmDown / atr; 57 | dx = T.Abs(diUp - diDown) / (diUp + diDown) * THundred; 58 | if (i - period < period - 2) 59 | { 60 | adx += dx; 61 | } 62 | else if (i - period == period - 2) 63 | { 64 | adx += dx; 65 | output[outputIndex++] = adx * invPer; 66 | } 67 | else 68 | { 69 | adx = adx * per + dx; 70 | output[outputIndex++] = adx * invPer; 71 | } 72 | } 73 | 74 | return TI_OKAY; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Adxr.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int AdxrStart(T[] options) => (Int32.CreateTruncating(options[0]) - 1) * 3; 6 | 7 | private static int Adxr(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 2) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= AdxrStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low, close) = inputs; 22 | var output = outputs[0]; 23 | 24 | T atr = T.Zero; 25 | T dmUp = T.Zero; 26 | T dmDown = T.Zero; 27 | for (var i = 1; i < period; ++i) 28 | { 29 | CalcTrueRange(low, high, close, i, out T trueRange); 30 | atr += trueRange; 31 | 32 | CalcDirection(high, low, i, out T dp, out T dm); 33 | dmUp += dp; 34 | dmDown += dm; 35 | } 36 | 37 | T per = T.CreateChecked(period - 1) / T.CreateChecked(period); 38 | T invPer = T.One / T.CreateChecked(period); 39 | T adx = T.Zero; 40 | T diUp = dmUp / atr; 41 | T diDown = dmDown / atr; 42 | T dx = T.Abs(diUp - diDown) / (diUp + diDown) * THundred; 43 | adx += dx; 44 | 45 | var adxr = BufferFactory(period - 1); 46 | int firstAdxr = AdxrStart(options); 47 | 48 | int outputIndex = default; 49 | for (var i = period; i < size; ++i) 50 | { 51 | CalcTrueRange(low, high, close, i, out T trueRange); 52 | atr = atr * per + trueRange; 53 | 54 | CalcDirection(high, low, i, out T dp, out T dm); 55 | dmUp = dmUp * per + dp; 56 | dmDown = dmDown * per + dm; 57 | 58 | diUp = dmUp / atr; 59 | diDown = dmDown / atr; 60 | dx = T.Abs(diUp - diDown) / (diUp + diDown) * THundred; 61 | if (i - period < period - 2) 62 | { 63 | adx += dx; 64 | } 65 | else if (i - period == period - 2) 66 | { 67 | adx += dx; 68 | BufferQPush(ref adxr, adx * invPer); 69 | } 70 | else 71 | { 72 | adx = adx * per + dx; 73 | if (i >= firstAdxr) 74 | { 75 | output[outputIndex++] = T.CreateChecked(0.5) * (adx * invPer + BufferGet(adxr, 1)); 76 | } 77 | 78 | BufferQPush(ref adxr, adx * invPer); 79 | } 80 | } 81 | 82 | return TI_OKAY; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Ao.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int AoStart(T[] options) => 33; 6 | 7 | private static int Ao(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | if (size <= AoStart(options)) 10 | { 11 | return TI_OKAY; 12 | } 13 | 14 | var (high, low) = inputs; 15 | var output = outputs[0]; 16 | 17 | T sum34 = T.Zero; 18 | T sum5 = T.Zero; 19 | for (var i = 0; i < 34; ++i) 20 | { 21 | T hl = T.CreateChecked(0.5) * (high[i] + low[i]); 22 | sum34 += hl; 23 | if (i >= 29) 24 | { 25 | sum5 += hl; 26 | } 27 | } 28 | 29 | T per34 = T.One / T.CreateChecked(34); 30 | T per5 = T.One / T.CreateChecked(5); 31 | int outputIndex = default; 32 | output[outputIndex++] = per5 * sum5 - per34 * sum34; 33 | for (var i = 34; i < size; ++i) 34 | { 35 | T hl = T.CreateChecked(0.5) * (high[i] + low[i]); 36 | sum34 += hl; 37 | sum5 += hl; 38 | 39 | sum34 -= T.CreateChecked(0.5) * (high[i - 34] + low[i - 34]); 40 | sum5 -= T.CreateChecked(0.5) * (high[i - 5] + low[i - 5]); 41 | output[outputIndex++] = per5 * sum5 - per34 * sum34; 42 | } 43 | 44 | return TI_OKAY; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Apo.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int ApoStart(T[] options) => 1; 6 | 7 | private static int Apo(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var shortPeriod = Int32.CreateTruncating(options[0]); 10 | var longPeriod = Int32.CreateTruncating(options[1]); 11 | 12 | if (shortPeriod < 1 || longPeriod < 2 || longPeriod < shortPeriod) 13 | { 14 | return TI_INVALID_OPTION; 15 | } 16 | 17 | if (size <= ApoStart(options)) 18 | { 19 | return TI_OKAY; 20 | } 21 | 22 | var input = inputs[0]; 23 | var output = outputs[0]; 24 | 25 | T shortPer = TTwo / T.CreateChecked(shortPeriod + 1); 26 | T longPer = TTwo / T.CreateChecked(longPeriod + 1); 27 | T shortEma = input[0]; 28 | T longEma = input[0]; 29 | int apoIndex = default; 30 | for (var i = 1; i < size; ++i) 31 | { 32 | shortEma = (input[i] - shortEma) * shortPer + shortEma; 33 | longEma = (input[i] - longEma) * longPer + longEma; 34 | output[apoIndex++] = shortEma - longEma; 35 | } 36 | 37 | return TI_OKAY; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Aroon.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int AroonStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int Aroon(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= AroonStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low) = inputs; 22 | var (aDown, aUp) = outputs; 23 | 24 | T scale = THundred / T.CreateChecked(period); 25 | var maxi = -1; 26 | var mini = -1; 27 | T max = high[0]; 28 | T min = low[0]; 29 | 30 | int aDownIndex = default; 31 | int aUpIndex = default; 32 | for (int i = period, trail = 0; i < size; ++i, ++trail) 33 | { 34 | // Maintain highest. 35 | T bar = high[i]; 36 | int j; 37 | if (maxi < trail) 38 | { 39 | maxi = trail; 40 | max = high[maxi]; 41 | j = trail; 42 | while (++j <= i) 43 | { 44 | bar = high[j]; 45 | if (bar >= max) 46 | { 47 | max = bar; 48 | maxi = j; 49 | } 50 | } 51 | } 52 | else if (bar >= max) 53 | { 54 | maxi = i; 55 | max = bar; 56 | } 57 | 58 | 59 | // Maintain lowest. 60 | bar = low[i]; 61 | if (mini < trail) 62 | { 63 | mini = trail; 64 | min = low[mini]; 65 | j = trail; 66 | while (++j <= i) 67 | { 68 | bar = low[j]; 69 | if (bar <= min) 70 | { 71 | min = bar; 72 | mini = j; 73 | } 74 | } 75 | } 76 | else if (bar <= min) 77 | { 78 | mini = i; 79 | min = bar; 80 | } 81 | 82 | // Calculate the indicator. 83 | aDown[aDownIndex++] = T.CreateChecked(period - (i - mini)) * scale; 84 | aUp[aUpIndex++] = T.CreateChecked(period - (i - maxi)) * scale; 85 | } 86 | 87 | return TI_OKAY; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_AroonOsc.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int AroonOscStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int AroonOsc(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= AroonStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low) = inputs; 22 | var output = outputs[0]; 23 | 24 | T scale = THundred / T.CreateChecked(period); 25 | var maxi = -1; 26 | var mini = -1; 27 | T max = high[0]; 28 | T min = low[0]; 29 | 30 | int outputIndex = default; 31 | for (int i = period, trail = 0; i < size; ++i, ++trail) 32 | { 33 | // Maintain highest. 34 | T bar = high[i]; 35 | int j; 36 | if (maxi < trail) 37 | { 38 | maxi = trail; 39 | max = high[maxi]; 40 | j = trail; 41 | while (++j <= i) 42 | { 43 | bar = high[j]; 44 | if (bar >= max) 45 | { 46 | max = bar; 47 | maxi = j; 48 | } 49 | } 50 | } 51 | else if (bar >= max) 52 | { 53 | maxi = i; 54 | max = bar; 55 | } 56 | 57 | 58 | // Maintain lowest. 59 | bar = low[i]; 60 | if (mini < trail) 61 | { 62 | mini = trail; 63 | min = low[mini]; 64 | j = trail; 65 | while (++j <= i) 66 | { 67 | bar = low[j]; 68 | if (bar <= min) 69 | { 70 | min = bar; 71 | mini = j; 72 | } 73 | } 74 | } 75 | else if (bar <= min) 76 | { 77 | mini = i; 78 | min = bar; 79 | } 80 | 81 | output[outputIndex++] = T.CreateChecked(maxi - mini) * scale; 82 | } 83 | 84 | return TI_OKAY; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Asin.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int AsinStart(T[] options) => 0; 6 | 7 | private static int Asin(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Asin); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Atan.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int AtanStart(T[] options) => 0; 6 | 7 | private static int Atan(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Atan); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Atr.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int AtrStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Atr(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= AtrStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low, close) = inputs; 22 | var output = outputs[0]; 23 | 24 | T sum = high[0] - low[0]; 25 | for (var i = 1; i < period; ++i) 26 | { 27 | CalcTrueRange(low, high, close, i, out T trueRange); 28 | sum += trueRange; 29 | } 30 | 31 | T per = T.One / T.CreateChecked(period); 32 | T val = sum / T.CreateChecked(period); 33 | int outputIndex = default; 34 | output[outputIndex++] = val; 35 | for (var i = period; i < size; ++i) 36 | { 37 | CalcTrueRange(low, high, close, i, out T trueRange); 38 | val = (trueRange - val) * per + val; 39 | output[outputIndex++] = val; 40 | } 41 | 42 | return TI_OKAY; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_AvgPrice.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int AvgPriceStart(T[] options) => 0; 6 | 7 | private static int AvgPrice(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var (open, high, low, close) = inputs; 10 | var output = outputs[0]; 11 | 12 | for (var i = 0; i < size; ++i) 13 | { 14 | output[i] = (open[i] + high[i] + low[i] + close[i]) * T.CreateChecked(0.25); 15 | } 16 | 17 | return TI_OKAY; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Bbands.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int BbandsStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Bbands(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | var stddev = options[1]; 11 | 12 | if (period < 1) 13 | { 14 | return TI_INVALID_OPTION; 15 | } 16 | 17 | if (size <= BbandsStart(options)) 18 | { 19 | return TI_OKAY; 20 | } 21 | 22 | var input = inputs[0]; 23 | var (lower, middle, upper) = outputs; 24 | 25 | T sum = T.Zero; 26 | T sum2 = T.Zero; 27 | for (var i = 0; i < period; ++i) 28 | { 29 | sum += input[i]; 30 | sum2 += input[i] * input[i]; 31 | } 32 | 33 | T scale = T.One / T.CreateChecked(period); 34 | T sd = T.Sqrt(sum2 * scale - sum * scale * (sum * scale)); 35 | 36 | int lowerIndex = default; 37 | int middleIndex = default; 38 | int upperIndex = default; 39 | middle[middleIndex++] = sum * scale; 40 | lower[lowerIndex++] = middle[0] - stddev * sd; 41 | upper[upperIndex++] = middle[0] + stddev * sd; 42 | for (var i = period; i < size; ++i) 43 | { 44 | sum += input[i]; 45 | sum2 += input[i] * input[i]; 46 | 47 | sum -= input[i - period]; 48 | sum2 -= input[i - period] * input[i - period]; 49 | 50 | sd = T.Sqrt(sum2 * scale - sum * scale * (sum * scale)); 51 | middle[middleIndex] = sum * scale; 52 | upper[upperIndex++] = middle[middleIndex] + stddev * sd; 53 | lower[lowerIndex++] = middle[middleIndex] - stddev * sd; 54 | ++middleIndex; 55 | } 56 | 57 | return TI_OKAY; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Bop.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int BopStart(T[] options) => 0; 6 | 7 | private static int Bop(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var (open, high, low, close) = inputs; 10 | var output = outputs[0]; 11 | 12 | for (var i = 0; i < size; ++i) 13 | { 14 | T hl = high[i] - low[i]; 15 | output[i] = hl > T.Zero ? (close[i] - open[i]) / hl : T.Zero; 16 | } 17 | 18 | return TI_OKAY; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Cci.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int CciStart(T[] options) => (Int32.CreateTruncating(options[0]) - 1) * 2; 6 | 7 | private static int Cci(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= CciStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low, close) = inputs; 22 | var output = outputs[0]; 23 | 24 | T scale = T.One / T.CreateChecked(period); 25 | var sum = BufferFactory(period); 26 | int outputIndex = default; 27 | for (var i = 0; i < size; ++i) 28 | { 29 | T today = (high[i] + low[i] + close[i]) * (T.One / TThree); 30 | BufferPush(ref sum, today); 31 | 32 | T avg = sum.sum * scale; 33 | if (i >= period * 2 - 2) 34 | { 35 | T acc = T.Zero; 36 | for (var j = 0; j < period; ++j) 37 | { 38 | acc += T.Abs(avg - sum.vals[j]); 39 | } 40 | 41 | T cci = acc * scale * T.CreateChecked(0.015); 42 | cci = (today - avg) / cci; 43 | output[outputIndex++] = cci; 44 | } 45 | } 46 | 47 | return TI_OKAY; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Ceil.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int CeilStart(T[] options) => 0; 6 | 7 | private static int Ceil(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Ceiling); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Cmo.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int CmoStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int Cmo(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= CmoStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T upSum = T.Zero; 25 | T downSum = T.Zero; 26 | for (var i = 1; i <= period; ++i) 27 | { 28 | upSum += input[i] > input[i - 1] ? input[i] - input[i - 1] : T.Zero; 29 | downSum += input[i] < input[i - 1] ? input[i - 1] - input[i] : T.Zero; 30 | } 31 | 32 | int outputIndex = default; 33 | output[outputIndex++] = THundred * (upSum - downSum) / (upSum + downSum); 34 | for (var i = period + 1; i < size; ++i) 35 | { 36 | upSum -= input[i - period] > input[i - period - 1] ? input[i - period] - input[i - period - 1] : T.Zero; 37 | downSum -= input[i - period] < input[i - period - 1] ? input[i - period - 1] - input[i - period] : T.Zero; 38 | 39 | upSum += input[i] > input[i - 1] ? input[i] - input[i - 1] : T.Zero; 40 | downSum += input[i] < input[i - 1] ? input[i - 1] - input[i] : T.Zero; 41 | 42 | output[outputIndex++] = THundred * (upSum - downSum) / (upSum + downSum); 43 | } 44 | 45 | return TI_OKAY; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Cos.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int CosStart(T[] options) => 0; 6 | 7 | private static int Cos(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Cos); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Cosh.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int CoshStart(T[] options) => 0; 6 | 7 | private static int Cosh(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Cosh); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Crossany.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int CrossanyStart(T[] options) => 1; 6 | 7 | private static int Crossany(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var (a, b) = inputs; 10 | var output = outputs[0]; 11 | 12 | int outputIndex = default; 13 | for (var i = 1; i < size; ++i) 14 | { 15 | output[outputIndex++] = a[i] > b[i] && a[i - 1] <= b[i - 1] || a[i] < b[i] && a[i - 1] >= b[i - 1] ? T.One : T.Zero; 16 | } 17 | 18 | return TI_OKAY; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Crossover.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int CrossoverStart(T[] options) => 1; 6 | 7 | private static int Crossover(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var (a, b) = inputs; 10 | var output = outputs[0]; 11 | 12 | int outputIndex = default; 13 | for (var i = 1; i < size; ++i) 14 | { 15 | output[outputIndex++] = a[i] > b[i] && a[i - 1] <= b[i - 1] ? T.One : T.Zero; 16 | } 17 | 18 | return TI_OKAY; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Cvi.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int CviStart(T[] options) => Int32.CreateTruncating(options[0]) * 2 - 1; 6 | 7 | private static int Cvi(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= CviStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low) = inputs; 22 | var output = outputs[0]; 23 | 24 | T per = TTwo / T.CreateChecked(period + 1); 25 | var lag = BufferFactory(period); 26 | T val = high[0] - low[0]; 27 | for (var i = 1; i < period * 2 - 1; ++i) 28 | { 29 | val = (high[i] - low[i] - val) * per + val; 30 | BufferQPush(ref lag, val); 31 | } 32 | 33 | int outputIndex = default; 34 | for (var i = period * 2 - 1; i < size; ++i) 35 | { 36 | val = (high[i] - low[i] - val) * per + val; 37 | T old = lag.vals[lag.index]; 38 | output[outputIndex++] = THundred * (val - old) / old; 39 | BufferQPush(ref lag, val); 40 | } 41 | 42 | return TI_OKAY; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Decay.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int DecayStart(T[] options) => 0; 6 | 7 | private static int Decay(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var input = inputs[0]; 10 | var output = outputs[0]; 11 | var period = Int32.CreateTruncating(options[0]); 12 | 13 | T scale = T.One / T.CreateChecked(period); 14 | int outputIndex = default; 15 | output[outputIndex++] = input[0]; 16 | for (var i = 1; i < size; ++i) 17 | { 18 | T d = output[i - 1] - scale; 19 | output[outputIndex++] = input[i] > d ? input[i] : d; 20 | } 21 | 22 | return TI_OKAY; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Dema.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int DemaStart(T[] options) => (Int32.CreateTruncating(options[0]) - 1) * 2; 6 | 7 | private static int Dema(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= DemaStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T per = TTwo / T.CreateChecked(period + 1); 25 | T per1 = T.One - per; 26 | 27 | // Calculate EMA(input) 28 | T ema = input[0]; 29 | 30 | // Calculate EMA(EMA(input)) 31 | T ema2 = ema; 32 | int outputIndex = default; 33 | for (var i = 0; i < size; ++i) 34 | { 35 | ema = ema * per1 + input[i] * per; 36 | if (i == period - 1) 37 | { 38 | ema2 = ema; 39 | } 40 | 41 | if (i >= period - 1) 42 | { 43 | ema2 = ema2 * per1 + ema * per; 44 | if (i >= (period - 1) * 2) 45 | { 46 | output[outputIndex++] = ema * TTwo - ema2; 47 | } 48 | } 49 | } 50 | 51 | return TI_OKAY; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Di.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int DiStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Di(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= DiStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low, close) = inputs; 22 | var (plusDi, minusDi) = outputs; 23 | 24 | T atr = T.Zero; 25 | T dmUp = T.Zero; 26 | T dmDown = T.Zero; 27 | for (var i = 1; i < period; ++i) 28 | { 29 | CalcTrueRange(low, high, close, i, out T trueRange); 30 | atr += trueRange; 31 | 32 | CalcDirection(high, low, i, out T dp, out T dm); 33 | dmUp += dp; 34 | dmDown += dm; 35 | } 36 | 37 | T per = T.CreateChecked(period - 1) / T.CreateChecked(period); 38 | int plusDiIndex = default; 39 | int minusDiIndex = default; 40 | plusDi[plusDiIndex++] = THundred * dmUp / atr; 41 | minusDi[minusDiIndex++] = THundred * dmDown / atr; 42 | for (var i = period; i < size; ++i) 43 | { 44 | CalcTrueRange(low, high, close, i, out T trueRange); 45 | atr = atr * per + trueRange; 46 | 47 | CalcDirection(high, low, i, out T dp, out T dm); 48 | dmUp = dmUp * per + dp; 49 | dmDown = dmDown * per + dm; 50 | 51 | plusDi[plusDiIndex++] = THundred * dmUp / atr; 52 | minusDi[minusDiIndex++] = THundred * dmDown / atr; 53 | } 54 | 55 | return TI_OKAY; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Div.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int DivStart(T[] options) => 0; 6 | 7 | private static int Div(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple2(size, inputs[0], inputs[1], outputs[0], (d1, d2) => d1 / d2); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Dm.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int DmStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Dm(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= DmStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low) = inputs; 22 | var (plusDm, minusDm) = outputs; 23 | 24 | T dmUp = T.Zero; 25 | T dmDown = T.Zero; 26 | for (var i = 1; i < period; ++i) 27 | { 28 | CalcDirection(high, low, i, out T dp, out T dm); 29 | 30 | dmUp += dp; 31 | dmDown += dm; 32 | } 33 | 34 | T per = T.CreateChecked(period - 1) / T.CreateChecked(period); 35 | int plusDmIndex = default; 36 | int minusDmIndex = default; 37 | plusDm[plusDmIndex++] = dmUp; 38 | minusDm[minusDmIndex++] = dmDown; 39 | for (var i = period; i < size; ++i) 40 | { 41 | CalcDirection(high, low, i, out T dp, out T dm); 42 | 43 | dmUp = dmUp * per + dp; 44 | dmDown = dmDown * per + dm; 45 | plusDm[plusDmIndex++] = dmUp; 46 | minusDm[minusDmIndex++] = dmDown; 47 | } 48 | 49 | return TI_OKAY; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Dpo.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int DpoStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Dpo(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= DpoStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | for (var i = 0; i < period; ++i) 26 | { 27 | sum += input[i]; 28 | } 29 | 30 | T scale = T.One / T.CreateChecked(period); 31 | var back = period / 2 + 1; 32 | int outputIndex = default; 33 | output[outputIndex++] = input[period - 1 - back] - sum * scale; 34 | for (var i = period; i < size; ++i) 35 | { 36 | sum = sum + input[i] - input[i - period]; 37 | output[outputIndex++] = input[i - back] - sum * scale; 38 | } 39 | 40 | return TI_OKAY; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Dx.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int DxStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Dx(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= DxStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low, close) = inputs; 22 | var output = outputs[0]; 23 | 24 | T atr = T.Zero; 25 | T dmUp = T.Zero; 26 | T dmDown = T.Zero; 27 | for (var i = 1; i < period; ++i) 28 | { 29 | CalcTrueRange(low, high, close, i, out T trueRange); 30 | atr += trueRange; 31 | 32 | CalcDirection(high, low, i, out T dp, out T dm); 33 | dmUp += dp; 34 | dmDown += dm; 35 | } 36 | 37 | T per = T.CreateChecked(period - 1) / T.CreateChecked(period); 38 | T diUp = dmUp / atr; 39 | T diDown = dmDown / atr; 40 | T dx = T.Abs(diUp - diDown) / (diUp + diDown) * THundred; 41 | int outputIndex = default; 42 | output[outputIndex++] = dx; 43 | for (var i = period; i < size; ++i) 44 | { 45 | CalcTrueRange(low, high, close, i, out T trueRange); 46 | atr = atr * per + trueRange; 47 | 48 | CalcDirection(high, low, i, out T dp, out T dm); 49 | dmUp = dmUp * per + dp; 50 | dmDown = dmDown * per + dm; 51 | 52 | diUp = dmUp / atr; 53 | diDown = dmDown / atr; 54 | dx = T.Abs(diUp - diDown) / (diUp + diDown) * THundred; 55 | output[outputIndex++] = dx; 56 | } 57 | 58 | return TI_OKAY; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Edecay.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int EdecayStart(T[] options) => 0; 6 | 7 | private static int Edecay(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var input = inputs[0]; 10 | var output = outputs[0]; 11 | var period = Int32.CreateTruncating(options[0]); 12 | 13 | T scale = T.One - T.One / T.CreateChecked(period); 14 | int outputIndex = default; 15 | output[outputIndex++] = input[0]; 16 | for (var i = 1; i < size; ++i) 17 | { 18 | T d = output[i - 1] * scale; 19 | output[outputIndex++] = input[i] > d ? input[i] : d; 20 | } 21 | 22 | return TI_OKAY; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Ema.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int EmaStart(T[] options) => 0; 6 | 7 | private static int Ema(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= EmaStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T per = TTwo / T.CreateChecked(period + 1); 25 | T val = input[0]; 26 | int outputIndex = default; 27 | output[outputIndex++] = val; 28 | for (var i = 1; i < size; ++i) 29 | { 30 | val = (input[i] - val) * per + val; 31 | output[outputIndex++] = val; 32 | } 33 | 34 | return TI_OKAY; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Emv.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int EmvStart(T[] options) => 1; 6 | 7 | private static int Emv(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | if (size <= EmvStart(options)) 10 | { 11 | return TI_OKAY; 12 | } 13 | 14 | var (high, low, volume) = inputs; 15 | var output = outputs[0]; 16 | 17 | T last = (high[0] + low[0]) * T.CreateChecked(0.5); 18 | int outputIndex = default; 19 | for (var i = 1; i < size; ++i) 20 | { 21 | T hl = (high[i] + low[i]) * T.CreateChecked(0.5); 22 | T br = volume[i] / T.CreateChecked(10000) / (high[i] - low[i]); 23 | output[outputIndex++] = (hl - last) / br; 24 | last = hl; 25 | } 26 | 27 | return TI_OKAY; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Exp.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int ExpStart(T[] options) => 0; 6 | 7 | private static int Exp(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Exp); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Fisher.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int FisherStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Fisher(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= FisherStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low) = inputs; 22 | var (fisher, signal) = outputs; 23 | 24 | var maxi = -1; 25 | var mini = -1; 26 | var THalf = T.CreateChecked(0.5); 27 | T max = THalf * (high[0] + low[0]); 28 | T min = THalf * (high[0] + low[0]); 29 | T val1 = T.Zero; 30 | T fish = T.Zero; 31 | int fisherIndex = default; 32 | int signalIndex = default; 33 | for (int i = period - 1, trail = 0; i < size; ++i, ++trail) 34 | { 35 | // Maintain highest. 36 | T bar = THalf * (high[i] + low[i]); 37 | if (maxi < trail) 38 | { 39 | maxi = trail; 40 | max = THalf * (high[maxi] + low[maxi]); 41 | int j = trail; 42 | while (++j <= i) 43 | { 44 | bar = THalf * (high[j] + low[j]); 45 | if (bar >= max) 46 | { 47 | max = bar; 48 | maxi = j; 49 | } 50 | } 51 | } 52 | else if (bar >= max) 53 | { 54 | maxi = i; 55 | max = bar; 56 | } 57 | 58 | // Maintain lowest. 59 | bar = THalf * (high[i] + low[i]); 60 | if (mini < trail) 61 | { 62 | mini = trail; 63 | min = THalf * (high[mini] + low[mini]); 64 | int j = trail; 65 | while (++j <= i) 66 | { 67 | bar = THalf * (high[j] + low[j]); 68 | if (bar <= min) 69 | { 70 | min = bar; 71 | mini = j; 72 | } 73 | } 74 | } 75 | else if (bar <= min) 76 | { 77 | mini = i; 78 | min = bar; 79 | } 80 | 81 | T mm = max - min; 82 | if (T.IsZero(mm)) 83 | { 84 | mm = T.CreateChecked(0.001); 85 | } 86 | 87 | val1 = T.CreateChecked(0.66) * ((THalf * (high[i] + low[i]) - min) / mm - THalf) + T.CreateChecked(0.67) * val1; 88 | if (val1 > T.CreateChecked(0.99)) 89 | { 90 | val1 = T.CreateChecked(0.999); 91 | } 92 | 93 | if (val1 < T.CreateChecked(-0.99)) 94 | { 95 | val1 = T.CreateChecked(-0.999); 96 | } 97 | 98 | signal[signalIndex++] = fish; 99 | fish = THalf * T.Log((T.One + val1) / (T.One - val1)) + THalf * fish; 100 | fisher[fisherIndex++] = fish; 101 | } 102 | 103 | return TI_OKAY; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Floor.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int FloorStart(T[] options) => 0; 6 | 7 | private static int Floor(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Floor); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Fosc.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int FoscStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int Fosc(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= FoscStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T x = T.Zero; // Sum of Xs. 25 | T x2 = T.Zero; // Sum of square of Xs. 26 | T y = T.Zero; // Flat sum of previous numbers. 27 | T xy = T.Zero; // Weighted sum of previous numbers. 28 | T tsf = T.Zero; 29 | for (var i = 0; i < period - 1; ++i) 30 | { 31 | x += T.CreateChecked(i + 1); 32 | x2 += T.CreateChecked((i + 1) * (i + 1)); 33 | xy += input[i] * T.CreateChecked(i + 1); 34 | y += input[i]; 35 | } 36 | 37 | x += T.CreateChecked(period); 38 | x2 += T.CreateChecked(period * period); 39 | 40 | T p = T.One / T.CreateChecked(period); 41 | T bd = T.One / (T.CreateChecked(period) * x2 - x * x); 42 | int outputIndex = default; 43 | for (var i = period - 1; i < size; ++i) 44 | { 45 | xy += input[i] * T.CreateChecked(period); 46 | y += input[i]; 47 | T b = (T.CreateChecked(period) * xy - x * y) * bd; 48 | T a = (y - b * x) * p; 49 | if (i >= period) 50 | { 51 | output[outputIndex++] = THundred * (input[i] - tsf) / input[i]; 52 | } 53 | 54 | tsf = a + b * T.CreateChecked(period + 1); 55 | xy -= y; 56 | y -= input[i - period + 1]; 57 | } 58 | 59 | return TI_OKAY; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Hma.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int HmaStart(T[] options) 6 | { 7 | var period = Int32.CreateTruncating(options[0]); 8 | return period + Int32.CreateTruncating(T.Sqrt(T.CreateChecked(period))) - 2; 9 | } 10 | 11 | private static int Hma(int size, T[][] inputs, T[] options, T[][] outputs) 12 | { 13 | var period = Int32.CreateTruncating(options[0]); 14 | 15 | if (period < 1) 16 | { 17 | return TI_INVALID_OPTION; 18 | } 19 | 20 | if (size <= HmaStart(options)) 21 | { 22 | return TI_OKAY; 23 | } 24 | 25 | var input = inputs[0]; 26 | var output = outputs[0]; 27 | 28 | // HMA(input, N) = WMA((2 * WMA(input, N/2) - WMA(input, N)), sqrt(N)) 29 | // Need to do three WMAs, with periods N, N/2, and sqrt N. 30 | 31 | var period2 = period / 2; 32 | T sum = T.Zero; // Flat sum of previous numbers. 33 | T weightSum = T.Zero; // Weighted sum of previous numbers. 34 | T sum2 = T.Zero; 35 | T weightSum2 = T.Zero; 36 | // Setup up the WMA(period) and WMA(period/2) on the input. 37 | for (var i = 0; i < period - 1; ++i) 38 | { 39 | weightSum += input[i] * T.CreateChecked(i + 1); 40 | sum += input[i]; 41 | if (i >= period - period2) 42 | { 43 | weightSum2 += input[i] * T.CreateChecked(i + 1 - (period - period2)); 44 | sum2 += input[i]; 45 | } 46 | } 47 | 48 | var periodSqrt = Int32.CreateTruncating(T.Sqrt(T.CreateChecked(period))); 49 | T weights = T.CreateChecked(period * (period + 1)) / TTwo; 50 | T weights2 = T.CreateChecked(period2 * (period2 + 1)) / TTwo; 51 | T weightsSqrt = T.CreateChecked(periodSqrt * (periodSqrt + 1)) / TTwo; 52 | T sumSqrt = T.Zero; 53 | T weightSumSqrt = T.Zero; 54 | var buff = BufferFactory(periodSqrt); 55 | int outputIndex = default; 56 | for (var i = period - 1; i < size; ++i) 57 | { 58 | weightSum += input[i] * T.CreateChecked(period); 59 | sum += input[i]; 60 | 61 | weightSum2 += input[i] * T.CreateChecked(period2); 62 | sum2 += input[i]; 63 | 64 | T wma = weightSum / weights; 65 | T wma2 = weightSum2 / weights2; 66 | T diff = TTwo * wma2 - wma; 67 | 68 | weightSumSqrt += diff * T.CreateChecked(periodSqrt); 69 | sumSqrt += diff; 70 | BufferQPush(ref buff, diff); 71 | 72 | if (i >= period - 1 + (periodSqrt - 1)) 73 | { 74 | output[outputIndex++] = weightSumSqrt / weightsSqrt; 75 | 76 | weightSumSqrt -= sumSqrt; 77 | sumSqrt -= BufferGet(buff, 1); 78 | } 79 | else 80 | { 81 | weightSumSqrt -= sumSqrt; 82 | } 83 | 84 | 85 | weightSum -= sum; 86 | sum -= input[i - period + 1]; 87 | 88 | weightSum2 -= sum2; 89 | sum2 -= input[i - period2 + 1]; 90 | } 91 | 92 | return TI_OKAY; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Kama.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int KamaStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Kama(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= KamaStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | for (var i = 1; i < period; ++i) 26 | { 27 | sum += T.Abs(input[i] - input[i - 1]); 28 | } 29 | 30 | // The caller selects the period used in the efficiency ratio. The fast and slow periods are hard set by the algorithm. 31 | T shortPer = TTwo / T.CreateChecked(2 + 1); 32 | T longPer = TTwo / T.CreateChecked(30 + 1); 33 | 34 | T kama = input[period - 1]; 35 | int outputIndex = default; 36 | output[outputIndex++] = kama; 37 | for (var i = period; i < size; ++i) 38 | { 39 | sum += T.Abs(input[i] - input[i - 1]); 40 | if (i > period) 41 | { 42 | sum -= T.Abs(input[i - period] - input[i - period - 1]); 43 | } 44 | 45 | T er = !T.IsZero(sum) ? T.Abs(input[i] - input[i - period]) / sum : T.One; 46 | T sc = T.Pow(er * (shortPer - longPer) + longPer, TTwo); 47 | 48 | kama += sc * (input[i] - kama); 49 | output[outputIndex++] = kama; 50 | } 51 | 52 | return TI_OKAY; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Kvo.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int KvoStart(T[] options) => 1; 6 | 7 | private static int Kvo(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var shortPeriod = Int32.CreateTruncating(options[0]); 10 | var longPeriod = Int32.CreateTruncating(options[1]); 11 | 12 | if (shortPeriod < 1 || longPeriod < shortPeriod) 13 | { 14 | return TI_INVALID_OPTION; 15 | } 16 | 17 | if (size <= KvoStart(options)) 18 | { 19 | return TI_OKAY; 20 | } 21 | 22 | var (high, low, close, volume) = inputs; 23 | var output = outputs[0]; 24 | 25 | T shortPer = TTwo / T.CreateChecked(shortPeriod + 1); 26 | T longPer = TTwo / T.CreateChecked(longPeriod + 1); 27 | T cm = T.Zero; 28 | T prevHlc = high[0] + low[0] + close[0]; 29 | var trend = -1; 30 | T shortEma = T.Zero; 31 | T longEma = T.Zero; 32 | int outputIndex = default; 33 | for (var i = 1; i < size; ++i) 34 | { 35 | T hlc = high[i] + low[i] + close[i]; 36 | T dm = high[i] - low[i]; 37 | if (hlc > prevHlc && trend != 1) 38 | { 39 | trend = 1; 40 | cm = high[i - 1] - low[i - 1]; 41 | } 42 | else if (hlc < prevHlc && trend != 0) 43 | { 44 | trend = 0; 45 | cm = high[i - 1] - low[i - 1]; 46 | } 47 | 48 | cm += dm; 49 | T vf = volume[i] * T.Abs(dm / cm * TTwo - T.One) * THundred * (trend == 0 ? T.NegativeOne : T.One); 50 | if (i == 1) 51 | { 52 | shortEma = vf; 53 | longEma = vf; 54 | } 55 | else 56 | { 57 | shortEma = (vf - shortEma) * shortPer + shortEma; 58 | longEma = (vf - longEma) * longPer + longEma; 59 | } 60 | 61 | output[outputIndex++] = shortEma - longEma; 62 | prevHlc = hlc; 63 | } 64 | 65 | return TI_OKAY; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Lag.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int LagStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int Lag(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 0) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= LagStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | int outputIndex = default; 25 | for (var i = period; i < size; ++i) 26 | { 27 | output[outputIndex++] = input[i - period]; 28 | } 29 | 30 | return TI_OKAY; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_LinReg.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int LinRegStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int LinReg(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= LinRegStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T x = T.Zero; // Sum of Xs. 25 | T x2 = T.Zero; // Sum of square of Xs. 26 | T y = T.Zero; // Flat sum of previous numbers. 27 | T xy = T.Zero; // Weighted sum of previous numbers. 28 | for (var i = 0; i < period - 1; ++i) 29 | { 30 | x += T.CreateChecked(i + 1); 31 | x2 += T.CreateChecked((i + 1) * (i + 1)); 32 | xy += input[i] * T.CreateChecked(i + 1); 33 | y += input[i]; 34 | } 35 | 36 | x += T.CreateChecked(period); 37 | x2 += T.CreateChecked(period * period); 38 | 39 | T p = T.One / T.CreateChecked(period); 40 | T bd = T.One / (T.CreateChecked(period) * x2 - x * x); 41 | int outputIndex = default; 42 | for (var i = period - 1; i < size; ++i) 43 | { 44 | xy += input[i] * T.CreateChecked(period); 45 | y += input[i]; 46 | T b = (T.CreateChecked(period) * xy - x * y) * bd; 47 | T a = (y - b * x) * p; 48 | output[outputIndex++] = a + b * T.CreateChecked(period); 49 | xy -= y; 50 | y -= input[i - period + 1]; 51 | } 52 | 53 | return TI_OKAY; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_LinRegIntercept.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int LinRegInterceptStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int LinRegIntercept(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= LinRegStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T x = T.Zero; // Sum of Xs. 25 | T x2 = T.Zero; // Sum of square of Xs. 26 | T y = T.Zero; // Flat sum of previous numbers. 27 | T xy = T.Zero; // Weighted sum of previous numbers. 28 | for (var i = 0; i < period - 1; ++i) 29 | { 30 | x += T.CreateChecked(i + 1); 31 | x2 += T.CreateChecked((i + 1) * (i + 1)); 32 | xy += input[i] * T.CreateChecked(i + 1); 33 | y += input[i]; 34 | } 35 | 36 | x += T.CreateChecked(period); 37 | x2 += T.CreateChecked(period * period); 38 | 39 | T p = T.One / T.CreateChecked(period); 40 | T bd = T.One / (T.CreateChecked(period) * x2 - x * x); 41 | int outputIndex = default; 42 | for (var i = period - 1; i < size; ++i) 43 | { 44 | xy += input[i] * T.CreateChecked(period); 45 | y += input[i]; 46 | T b = (T.CreateChecked(period) * xy - x * y) * bd; 47 | T a = (y - b * x) * p; 48 | output[outputIndex++] = a + b; 49 | xy -= y; 50 | y -= input[i - period + 1]; 51 | } 52 | 53 | return TI_OKAY; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_LinRegSlope.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int LinRegSlopeStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int LinRegSlope(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= LinRegStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T x = T.Zero; // Sum of Xs. 25 | T x2 = T.Zero; // Sum of square of Xs. 26 | T y = T.Zero; // Flat sum of previous numbers. 27 | T xy = T.Zero; // Weighted sum of previous numbers. 28 | for (var i = 0; i < period - 1; ++i) 29 | { 30 | x += T.CreateChecked(i + 1); 31 | x2 += T.CreateChecked((i + 1) * (i + 1)); 32 | xy += input[i] * T.CreateChecked(i + 1); 33 | y += input[i]; 34 | } 35 | 36 | x += T.CreateChecked(period); 37 | x2 += T.CreateChecked(period * period); 38 | 39 | T bd = T.One / (T.CreateChecked(period) * x2 - x * x); 40 | int outputIndex = default; 41 | for (var i = period - 1; i < size; ++i) 42 | { 43 | xy += input[i] * T.CreateChecked(period); 44 | y += input[i]; 45 | T b = (T.CreateChecked(period) * xy - x * y) * bd; 46 | output[outputIndex++] = b; 47 | xy -= y; 48 | y -= input[i - period + 1]; 49 | } 50 | 51 | return TI_OKAY; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Ln.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int LnStart(T[] options) => 0; 6 | 7 | private static int Ln(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Log); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Log10.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int Log10Start(T[] options) => 0; 6 | 7 | private static int Log10(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Log10); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Macd.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int MacdStart(T[] options) 6 | { 7 | // NB we return data before signal is strictly valid. 8 | int longPeriod = Int32.CreateTruncating(options[1]); 9 | return longPeriod - 1; 10 | } 11 | 12 | private static int Macd(int size, T[][] inputs, T[] options, T[][] outputs) 13 | { 14 | var shortPeriod = Int32.CreateTruncating(options[0]); 15 | var longPeriod = Int32.CreateTruncating(options[1]); 16 | var signalPeriod = Int32.CreateTruncating(options[2]); 17 | 18 | if (shortPeriod < 1 || longPeriod < 2 || longPeriod < shortPeriod || signalPeriod < 1) 19 | { 20 | return TI_INVALID_OPTION; 21 | } 22 | 23 | if (size <= MacdStart(options)) 24 | { 25 | return TI_OKAY; 26 | } 27 | 28 | var input = inputs[0]; 29 | var (macd, signal, hist) = outputs; 30 | 31 | T shortPer = TTwo / T.CreateChecked(shortPeriod + 1); 32 | T longPer = TTwo / T.CreateChecked(longPeriod + 1); 33 | if (shortPeriod == 12 && longPeriod == 26) 34 | { 35 | // It's what people expect. 36 | shortPer = T.CreateChecked(0.15); 37 | longPer = T.CreateChecked(0.075); 38 | } 39 | 40 | T signalPer = TTwo / T.CreateChecked(signalPeriod + 1); 41 | T shortEma = input[0]; 42 | T longEma = input[0]; 43 | T signalEma = T.Zero; 44 | int macdIndex = default; 45 | int signalIndex = default; 46 | int histIndex = default; 47 | for (var i = 1; i < size; ++i) 48 | { 49 | shortEma = (input[i] - shortEma) * shortPer + shortEma; 50 | longEma = (input[i] - longEma) * longPer + longEma; 51 | T outEma = shortEma - longEma; 52 | if (i == longPeriod - 1) 53 | { 54 | signalEma = outEma; 55 | } 56 | 57 | if (i >= longPeriod - 1) 58 | { 59 | signalEma = (outEma - signalEma) * signalPer + signalEma; 60 | macd[macdIndex++] = outEma; 61 | signal[signalIndex++] = signalEma; 62 | hist[histIndex++] = outEma - signalEma; 63 | } 64 | } 65 | 66 | return TI_OKAY; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_MarketFi.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int MarketFiStart(T[] options) => 0; 6 | 7 | private static int MarketFi(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | if (size <= MarketFiStart(options)) 10 | { 11 | return TI_OKAY; 12 | } 13 | 14 | var (high, low, volume) = inputs; 15 | var output = outputs[0]; 16 | 17 | int outputIndex = default; 18 | for (var i = 0; i < size; ++i) 19 | { 20 | output[outputIndex++] = (high[i] - low[i]) / volume[i]; 21 | } 22 | 23 | return TI_OKAY; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Mass.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int MassStart(T[] options) 6 | { 7 | var sumP = Int32.CreateTruncating(options[0]) - 1; 8 | // The ema uses a hard-coded period of 9. (9-1)*2 = 16 9 | return 16 + sumP; 10 | } 11 | 12 | private static int Mass(int size, T[][] inputs, T[] options, T[][] outputs) 13 | { 14 | var period = Int32.CreateTruncating(options[0]); 15 | 16 | if (period < 1) 17 | { 18 | return TI_INVALID_OPTION; 19 | } 20 | 21 | if (size <= MassStart(options)) 22 | { 23 | return TI_OKAY; 24 | } 25 | 26 | var (high, low) = inputs; 27 | var output = outputs[0]; 28 | 29 | // mass uses a hard-coded 9 period for the ema 30 | T per = TTwo / T.CreateChecked(9 + 1); 31 | T per1 = T.One - per; 32 | 33 | // Calculate EMA(h-l) 34 | T ema = high[0] - low[0]; 35 | 36 | // Calculate EMA(EMA(h-l)) 37 | T ema2 = ema; 38 | var sum = BufferFactory(period); 39 | int outputIndex = default; 40 | for (var i = 0; i < size; ++i) 41 | { 42 | T hl = high[i] - low[i]; 43 | ema = ema * per1 + hl * per; 44 | if (i == 8) 45 | { 46 | ema2 = ema; 47 | } 48 | 49 | if (i < 8) 50 | { 51 | continue; 52 | } 53 | 54 | ema2 = ema2 * per1 + ema * per; 55 | if (i >= 16) 56 | { 57 | BufferPush(ref sum, ema / ema2); 58 | 59 | if (i >= period + 16 - 1) 60 | { 61 | output[outputIndex++] = sum.sum; 62 | } 63 | } 64 | } 65 | 66 | return TI_OKAY; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Max.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int MaxStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Max(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= MaxStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | var maxi = -1; 25 | T max = input[0]; 26 | int outputIndex = default; 27 | for (int i = period - 1, trail = 0; i < size; ++i, ++trail) 28 | { 29 | T bar = input[i]; 30 | 31 | if (maxi < trail) 32 | { 33 | maxi = trail; 34 | max = input[maxi]; 35 | int j = trail; 36 | while (++j <= i) 37 | { 38 | bar = input[j]; 39 | if (bar >= max) 40 | { 41 | max = bar; 42 | maxi = j; 43 | } 44 | } 45 | } 46 | else if (bar >= max) 47 | { 48 | maxi = i; 49 | max = bar; 50 | } 51 | 52 | output[outputIndex++] = max; 53 | } 54 | 55 | return TI_OKAY; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Md.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int MdStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Md(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= MdStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T scale = T.One / T.CreateChecked(period); 25 | T sum = T.Zero; 26 | int outputIndex = default; 27 | for (var i = 0; i < size; ++i) 28 | { 29 | T today = input[i]; 30 | sum += today; 31 | if (i >= period) 32 | { 33 | sum -= input[i - period]; 34 | } 35 | 36 | T avg = sum * scale; 37 | if (i >= period - 1) 38 | { 39 | T acc = T.Zero; 40 | for (var j = 0; j < period; ++j) 41 | { 42 | acc += T.Abs(avg - input[i - j]); 43 | } 44 | 45 | output[outputIndex++] = acc * scale; 46 | } 47 | } 48 | 49 | return TI_OKAY; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_MedPrice.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int MedPriceStart(T[] options) => 0; 6 | 7 | private static int MedPrice(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var (high, low) = inputs; 10 | var output = outputs[0]; 11 | 12 | for (var i = 0; i < size; ++i) 13 | { 14 | output[i] = (high[i] + low[i]) * T.CreateChecked(0.5); 15 | } 16 | 17 | return TI_OKAY; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Mfi.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int MfiStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int Mfi(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= MfiStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low, close, volume) = inputs; 22 | var output = outputs[0]; 23 | 24 | T yTyp = (high[0] + low[0] + close[0]) * (T.One / TThree); 25 | var up = BufferFactory(period); 26 | var down = BufferFactory(period); 27 | int outputIndex = default; 28 | for (var i = 1; i < size; ++i) 29 | { 30 | T typ = (high[i] + low[i] + close[i]) * (T.One / TThree); 31 | T bar = typ * volume[i]; 32 | if (typ > yTyp) 33 | { 34 | BufferPush(ref up, bar); 35 | BufferPush(ref down, T.Zero); 36 | } 37 | else if (typ < yTyp) 38 | { 39 | BufferPush(ref down, bar); 40 | BufferPush(ref up, T.Zero); 41 | } 42 | else 43 | { 44 | BufferPush(ref up, T.Zero); 45 | BufferPush(ref down, T.Zero); 46 | } 47 | 48 | yTyp = typ; 49 | if (i >= period) 50 | { 51 | output[outputIndex++] = up.sum / (up.sum + down.sum) * THundred; 52 | } 53 | } 54 | 55 | return TI_OKAY; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Min.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int MinStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Min(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= MinStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | var mini = -1; 25 | T min = input[0]; 26 | int outputIndex = default; 27 | for (int i = period - 1, trail = 0; i < size; ++i, ++trail) 28 | { 29 | T bar = input[i]; 30 | if (mini < trail) 31 | { 32 | mini = trail; 33 | min = input[mini]; 34 | int j = trail; 35 | while (++j <= i) 36 | { 37 | bar = input[j]; 38 | if (bar <= min) 39 | { 40 | min = bar; 41 | mini = j; 42 | } 43 | } 44 | } 45 | else if (bar <= min) 46 | { 47 | mini = i; 48 | min = bar; 49 | } 50 | 51 | output[outputIndex++] = min; 52 | } 53 | 54 | return TI_OKAY; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Mom.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int MomStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int Mom(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= MomStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | int outputIndex = default; 25 | for (var i = period; i < size; ++i) 26 | { 27 | output[outputIndex++] = input[i] - input[i - period]; 28 | } 29 | 30 | return TI_OKAY; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Msw.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int MswStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int Msw(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= MswStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var (sine, lead) = outputs; 23 | 24 | T tpi = TTwo * T.Pi; 25 | int sineIndex = default; 26 | int leadIndex = default; 27 | for (var i = period; i < size; ++i) 28 | { 29 | T rp = T.Zero; 30 | T ip = T.Zero; 31 | for (var j = 0; j < period; ++j) 32 | { 33 | T weight = input[i - j]; 34 | rp += T.Cos(tpi * T.CreateChecked(j) / T.CreateChecked(period)) * weight; 35 | ip += T.Sin(tpi * T.CreateChecked(j) / T.CreateChecked(period)) * weight; 36 | } 37 | 38 | T phase; 39 | if (T.Abs(rp) > T.CreateChecked(0.001)) 40 | { 41 | phase = T.Atan(ip / rp); 42 | } 43 | else 44 | { 45 | phase = tpi / TTwo * (ip < T.Zero ? T.NegativeZero : T.Zero); 46 | } 47 | 48 | if (rp < T.Zero) 49 | { 50 | phase += T.Pi; 51 | } 52 | 53 | phase += T.Pi / TTwo; 54 | if (phase < T.Zero) 55 | { 56 | phase += tpi; 57 | } 58 | 59 | if (phase > tpi) 60 | { 61 | phase -= tpi; 62 | } 63 | 64 | sine[sineIndex++] = T.Sin(phase); 65 | lead[leadIndex++] = T.Sin(phase + T.Pi / T.CreateChecked(4)); 66 | } 67 | 68 | return TI_OKAY; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Mul.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int MulStart(T[] options) => 0; 6 | 7 | private static int Mul(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple2(size, inputs[0], inputs[1], outputs[0], (d1, d2) => d1 * d2); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Natr.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int NatrStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Natr(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= NatrStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low, close) = inputs; 22 | var output = outputs[0]; 23 | 24 | T sum = high[0] - low[0]; 25 | int outputIndex = default; 26 | for (var i = 1; i < period; ++i) 27 | { 28 | CalcTrueRange(low, high, close, i, out T trueRange); 29 | sum += trueRange; 30 | } 31 | 32 | T per = T.One / T.CreateChecked(period); 33 | T val = sum / T.CreateChecked(period); 34 | output[outputIndex++] = THundred * val / close[period - 1]; 35 | for (var i = period; i < size; ++i) 36 | { 37 | CalcTrueRange(low, high, close, i, out T trueRange); 38 | val = (trueRange - val) * per + val; 39 | output[outputIndex++] = THundred * val / close[i]; 40 | } 41 | 42 | return TI_OKAY; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Nvi.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int NviStart(T[] options) => 0; 6 | 7 | private static int Nvi(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | if (size <= NviStart(options)) 10 | { 11 | return TI_OKAY; 12 | } 13 | 14 | var (close, volume) = inputs; 15 | var output = outputs[0]; 16 | 17 | T nvi = T.CreateChecked(1000); 18 | int outputIndex = default; 19 | output[outputIndex++] = nvi; 20 | for (var i = 1; i < size; ++i) 21 | { 22 | if (volume[i] < volume[i - 1]) 23 | { 24 | nvi += (close[i] - close[i - 1]) / close[i - 1] * nvi; 25 | } 26 | 27 | output[outputIndex++] = nvi; 28 | } 29 | 30 | return TI_OKAY; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Obv.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int ObvStart(T[] options) => 0; 6 | 7 | private static int Obv(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var (close, volume) = inputs; 10 | var output = outputs[0]; 11 | 12 | T prev = close[0]; 13 | T sum = T.Zero; 14 | int outputIndex = default; 15 | output[outputIndex++] = sum; 16 | for (var i = 1; i < size; ++i) 17 | { 18 | if (close[i] > prev) 19 | { 20 | sum += volume[i]; 21 | } 22 | else if (close[i] < prev) 23 | { 24 | sum -= volume[i]; 25 | } 26 | 27 | prev = close[i]; 28 | output[outputIndex++] = sum; 29 | } 30 | 31 | return TI_OKAY; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Ppo.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int PpoStart(T[] options) => 1; 6 | 7 | private static int Ppo(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var shortPeriod = Int32.CreateTruncating(options[0]); 10 | var longPeriod = Int32.CreateTruncating(options[1]); 11 | 12 | if (shortPeriod < 1 || longPeriod < 2 || longPeriod < shortPeriod) 13 | { 14 | return TI_INVALID_OPTION; 15 | } 16 | 17 | if (size <= PpoStart(options)) 18 | { 19 | return TI_OKAY; 20 | } 21 | 22 | var input = inputs[0]; 23 | var output = outputs[0]; 24 | 25 | T shortPer = TTwo / T.CreateChecked(shortPeriod + 1); 26 | T longPer = TTwo / T.CreateChecked(longPeriod + 1); 27 | T shortEma = input[0]; 28 | T longEma = input[0]; 29 | int ppoIndex = default; 30 | for (var i = 1; i < size; ++i) 31 | { 32 | shortEma = (input[i] - shortEma) * shortPer + shortEma; 33 | longEma = (input[i] - longEma) * longPer + longEma; 34 | T outEma = THundred * (shortEma - longEma) / longEma; 35 | output[ppoIndex++] = outEma; 36 | } 37 | 38 | return TI_OKAY; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Psar.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int PsarStart(T[] options) => 1; 6 | 7 | private static int Psar(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var accelStep = options[0]; 10 | var accelMax = options[1]; 11 | 12 | if (accelStep <= T.Zero || accelMax <= accelStep) 13 | { 14 | return TI_INVALID_OPTION; 15 | } 16 | 17 | if (size < 2) 18 | { 19 | return TI_OKAY; 20 | } 21 | 22 | var (high, low) = inputs; 23 | var output = outputs[0]; 24 | 25 | // Try to choose if we start as short or long. There is really no right answer here. 26 | bool lng = high[0] + low[0] <= high[1] + low[1]; 27 | T extreme = lng ? high[0] : low[0]; 28 | T sar = lng ? low[0] : high[0]; 29 | 30 | T accel = accelStep; 31 | int outputIndex = default; 32 | for (var i = 1; i < size; ++i) 33 | { 34 | sar = (extreme - sar) * accel + sar; 35 | if (lng) 36 | { 37 | if (i >= 2 && sar > low[i - 2]) 38 | { 39 | sar = low[i - 2]; 40 | } 41 | 42 | if (sar > low[i - 1]) 43 | { 44 | sar = low[i - 1]; 45 | } 46 | 47 | if (accel < accelMax && high[i] > extreme) 48 | { 49 | accel += accelStep; 50 | if (accel > accelMax) 51 | { 52 | accel = accelMax; 53 | } 54 | } 55 | 56 | if (high[i] > extreme) 57 | { 58 | extreme = high[i]; 59 | } 60 | } 61 | else 62 | { 63 | if (i >= 2 && sar < high[i - 2]) 64 | { 65 | sar = high[i - 2]; 66 | } 67 | 68 | if (sar < high[i - 1]) 69 | { 70 | sar = high[i - 1]; 71 | } 72 | 73 | if (accel < accelMax && low[i] < extreme) 74 | { 75 | accel += accelStep; 76 | if (accel > accelMax) 77 | { 78 | accel = accelMax; 79 | } 80 | } 81 | 82 | if (low[i] < extreme) 83 | { 84 | extreme = low[i]; 85 | } 86 | } 87 | 88 | if (lng && low[i] < sar || lng == false && high[i] > sar) 89 | { 90 | accel = accelStep; 91 | sar = extreme; 92 | lng = !lng; 93 | extreme = lng ? high[i] : low[i]; 94 | } 95 | 96 | output[outputIndex++] = sar; 97 | } 98 | 99 | return TI_OKAY; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Pvi.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int PviStart(T[] options) => 0; 6 | 7 | private static int Pvi(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | if (size <= PviStart(options)) 10 | { 11 | return TI_OKAY; 12 | } 13 | 14 | var (close, volume) = inputs; 15 | var output = outputs[0]; 16 | 17 | T pvi = T.CreateChecked(1000); 18 | int outputIndex = default; 19 | output[outputIndex++] = pvi; 20 | for (var i = 1; i < size; ++i) 21 | { 22 | if (volume[i] > volume[i - 1]) 23 | { 24 | pvi += (close[i] - close[i - 1]) / close[i - 1] * pvi; 25 | } 26 | 27 | output[outputIndex++] = pvi; 28 | } 29 | 30 | return TI_OKAY; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Qstick.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int QstickStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Qstick(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= QstickStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (open, close) = inputs; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | for (var i = 0; i < period; ++i) 26 | { 27 | sum += close[i] - open[i]; 28 | } 29 | 30 | T scale = T.One / T.CreateChecked(period); 31 | int outputIndex = default; 32 | output[outputIndex++] = sum * scale; 33 | for (var i = period; i < size; ++i) 34 | { 35 | sum = sum + (close[i] - open[i]) - (close[i - period] - open[i - period]); 36 | output[outputIndex++] = sum * scale; 37 | } 38 | 39 | return TI_OKAY; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Roc.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int RocStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int Roc(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= RocStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | int outputIndex = default; 25 | for (var i = period; i < size; ++i) 26 | { 27 | output[outputIndex++] = (input[i] - input[i - period]) / input[i - period]; 28 | } 29 | 30 | return TI_OKAY; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_RocR.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int RocRStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int RocR(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= RocRStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | int outputIndex = default; 25 | for (var i = period; i < size; ++i) 26 | { 27 | output[outputIndex++] = input[i] / input[i - period]; 28 | } 29 | 30 | return TI_OKAY; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Round.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int RoundStart(T[] options) => 0; 6 | 7 | private static int Round(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Round); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Rsi.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int RsiStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int Rsi(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= RsiStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T smoothUp = T.Zero; 25 | T smoothDown = T.Zero; 26 | for (var i = 1; i <= period; ++i) 27 | { 28 | T upward = input[i] > input[i - 1] ? input[i] - input[i - 1] : T.Zero; 29 | T downward = input[i] < input[i - 1] ? input[i - 1] - input[i] : T.Zero; 30 | smoothUp += upward; 31 | smoothDown += downward; 32 | } 33 | 34 | smoothUp /= T.CreateChecked(period); 35 | smoothDown /= T.CreateChecked(period); 36 | 37 | T per = T.One / T.CreateChecked(period); 38 | int outputIndex = default; 39 | output[outputIndex++] = THundred * (smoothUp / (smoothUp + smoothDown)); 40 | for (var i = period + 1; i < size; ++i) 41 | { 42 | T upward = input[i] > input[i - 1] ? input[i] - input[i - 1] : T.Zero; 43 | T downward = input[i] < input[i - 1] ? input[i - 1] - input[i] : T.Zero; 44 | smoothUp = (upward - smoothUp) * per + smoothUp; 45 | smoothDown = (downward - smoothDown) * per + smoothDown; 46 | output[outputIndex++] = THundred * (smoothUp / (smoothUp + smoothDown)); 47 | } 48 | 49 | return TI_OKAY; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Sin.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T : IFloatingPointIeee754 4 | { 5 | private static int SinStart(T[] options) => 0; 6 | 7 | private static int Sin(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Sin); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Sinh.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int SinhStart(T[] options) => 0; 6 | 7 | private static int Sinh(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Sinh); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Sma.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int SmaStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Sma(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= SmaStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | for (var i = 0; i < period; ++i) 26 | { 27 | sum += input[i]; 28 | } 29 | 30 | T scale = T.One / T.CreateChecked(period); 31 | int outputIndex = default; 32 | output[outputIndex++] = sum * scale; 33 | for (var i = period; i < size; ++i) 34 | { 35 | sum = sum + input[i] - input[i - period]; 36 | output[outputIndex++] = sum * scale; 37 | } 38 | 39 | return TI_OKAY; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Sqrt.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int SqrtStart(T[] options) => 0; 6 | 7 | private static int Sqrt(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Sqrt); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_StdDev.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int StdDevStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int StdDev(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= StdDevStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | T sum2 = T.Zero; 26 | for (var i = 0; i < period; ++i) 27 | { 28 | sum += input[i]; 29 | sum2 += input[i] * input[i]; 30 | } 31 | 32 | T scale = T.One / T.CreateChecked(period); 33 | T s2S2 = sum2 * scale - sum * scale * (sum * scale); 34 | if (s2S2 > T.Zero) 35 | { 36 | s2S2 = T.Sqrt(s2S2); 37 | } 38 | 39 | int outputIndex = default; 40 | output[outputIndex++] = s2S2; 41 | for (var i = period; i < size; ++i) 42 | { 43 | sum += input[i]; 44 | sum2 += input[i] * input[i]; 45 | 46 | sum -= input[i - period]; 47 | sum2 -= input[i - period] * input[i - period]; 48 | s2S2 = sum2 * scale - sum * scale * (sum * scale); 49 | if (s2S2 > T.Zero) 50 | { 51 | s2S2 = T.Sqrt(s2S2); 52 | } 53 | 54 | output[outputIndex++] = s2S2; 55 | } 56 | 57 | return TI_OKAY; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_StdErr.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int StdErrStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int StdErr(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= StdErrStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | T sum2 = T.Zero; 26 | for (var i = 0; i < period; ++i) 27 | { 28 | sum += input[i]; 29 | sum2 += input[i] * input[i]; 30 | } 31 | 32 | T scale = T.One / T.CreateChecked(period); 33 | T s2S2 = sum2 * scale - sum * scale * (sum * scale); 34 | if (s2S2 > T.Zero) 35 | { 36 | s2S2 = T.Sqrt(s2S2); 37 | } 38 | 39 | T mul = T.One / T.Sqrt(T.CreateChecked(period)); 40 | int outputIndex = default; 41 | output[outputIndex++] = mul * s2S2; 42 | for (var i = period; i < size; ++i) 43 | { 44 | sum += input[i]; 45 | sum2 += input[i] * input[i]; 46 | 47 | sum -= input[i - period]; 48 | sum2 -= input[i - period] * input[i - period]; 49 | s2S2 = sum2 * scale - sum * scale * (sum * scale); 50 | if (s2S2 > T.Zero) 51 | { 52 | s2S2 = T.Sqrt(s2S2); 53 | } 54 | 55 | output[outputIndex++] = mul * s2S2; 56 | } 57 | 58 | return TI_OKAY; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Stoch.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int StochStart(T[] options) => 6 | Int32.CreateTruncating(options[0]) + Int32.CreateTruncating(options[1]) + Int32.CreateTruncating(options[2]) - 3; 7 | 8 | private static int Stoch(int size, T[][] inputs, T[] options, T[][] outputs) 9 | { 10 | var kPeriod = Int32.CreateTruncating(options[0]); 11 | var kSlow = Int32.CreateTruncating(options[1]); 12 | var dPeriod = Int32.CreateTruncating(options[2]); 13 | 14 | if (kPeriod < 1 || kSlow < 1 || dPeriod < 1) 15 | { 16 | return TI_INVALID_OPTION; 17 | } 18 | 19 | if (size <= StochStart(options)) 20 | { 21 | return TI_OKAY; 22 | } 23 | 24 | var (high, low, close) = inputs; 25 | var (stoch, stochMa) = outputs; 26 | 27 | T kPer = T.One / T.CreateChecked(kSlow); 28 | T dPer = T.One / T.CreateChecked(dPeriod); 29 | 30 | int trail = default; 31 | var maxi = -1; 32 | var mini = -1; 33 | T max = high[0]; 34 | T min = low[0]; 35 | var kSum = BufferFactory(kSlow); 36 | var dSum = BufferFactory(dPeriod); 37 | int stochIndex = default; 38 | int stochMaIndex = default; 39 | for (var i = 0; i < size; ++i) 40 | { 41 | if (i >= kPeriod) 42 | { 43 | ++trail; 44 | } 45 | 46 | // Maintain highest. 47 | T bar = high[i]; 48 | if (maxi < trail) 49 | { 50 | maxi = trail; 51 | max = high[maxi]; 52 | int j = trail; 53 | while (++j <= i) 54 | { 55 | bar = high[j]; 56 | if (bar >= max) 57 | { 58 | max = bar; 59 | maxi = j; 60 | } 61 | } 62 | } 63 | else if (bar >= max) 64 | { 65 | maxi = i; 66 | max = bar; 67 | } 68 | 69 | // Maintain lowest. 70 | bar = low[i]; 71 | if (mini < trail) 72 | { 73 | mini = trail; 74 | min = low[mini]; 75 | int j = trail; 76 | while (++j <= i) 77 | { 78 | bar = low[j]; 79 | if (bar <= min) 80 | { 81 | min = bar; 82 | mini = j; 83 | } 84 | } 85 | } 86 | else if (bar <= min) 87 | { 88 | mini = i; 89 | min = bar; 90 | } 91 | 92 | // Calculate it. 93 | T kDiff = max - min; 94 | T kFast = T.IsZero(kDiff) ? T.Zero : THundred * ((close[i] - min) / kDiff); 95 | BufferPush(ref kSum, kFast); 96 | 97 | if (i >= kPeriod - 1 + kSlow - 1) 98 | { 99 | T k = kSum.sum * kPer; 100 | BufferPush(ref dSum, k); 101 | 102 | if (i >= kPeriod - 1 + kSlow - 1 + dPeriod - 1) 103 | { 104 | stoch[stochIndex++] = k; 105 | stochMa[stochMaIndex++] = dSum.sum * dPer; 106 | } 107 | } 108 | } 109 | 110 | return TI_OKAY; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_StochRsi.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int StochRsiStart(T[] options) => Int32.CreateTruncating(options[0]) * 2 - 1; 6 | 7 | private static int StochRsi(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 2) 12 | { 13 | // if period = 0 then min-max = 0. 14 | return TI_INVALID_OPTION; 15 | } 16 | 17 | if (size <= StochRsiStart(options)) 18 | { 19 | return TI_OKAY; 20 | } 21 | 22 | var input = inputs[0]; 23 | var output = outputs[0]; 24 | 25 | var rsi = BufferFactory(period); 26 | 27 | T smoothUp = T.Zero; 28 | T smoothDown = T.Zero; 29 | for (var i = 1; i <= period; ++i) 30 | { 31 | T upward = input[i] > input[i - 1] ? input[i] - input[i - 1] : T.Zero; 32 | T downward = input[i] < input[i - 1] ? input[i - 1] - input[i] : T.Zero; 33 | smoothUp += upward; 34 | smoothDown += downward; 35 | } 36 | 37 | smoothUp /= T.CreateChecked(period); 38 | smoothDown /= T.CreateChecked(period); 39 | 40 | T r = THundred * (smoothUp / (smoothUp + smoothDown)); 41 | BufferPush(ref rsi, r); 42 | 43 | T per = T.One / T.CreateChecked(period); 44 | T min = r; 45 | T max = r; 46 | int mini = default; 47 | int maxi = default; 48 | int outputIndex = default; 49 | for (var i = period + 1; i < size; ++i) 50 | { 51 | T upward = input[i] > input[i - 1] ? input[i] - input[i - 1] : T.Zero; 52 | T downward = input[i] < input[i - 1] ? input[i - 1] - input[i] : T.Zero; 53 | 54 | smoothUp = (upward - smoothUp) * per + smoothUp; 55 | smoothDown = (downward - smoothDown) * per + smoothDown; 56 | 57 | r = THundred * (smoothUp / (smoothUp + smoothDown)); 58 | 59 | if (r > max) 60 | { 61 | max = r; 62 | maxi = rsi.index; 63 | } 64 | else if (maxi == rsi.index) 65 | { 66 | max = r; 67 | for (var j = 0; j < rsi.size; ++j) 68 | { 69 | if (j == rsi.index) 70 | { 71 | continue; 72 | } 73 | 74 | if (rsi.vals[j] > max) 75 | { 76 | max = rsi.vals[j]; 77 | maxi = j; 78 | } 79 | } 80 | } 81 | 82 | if (r < min) 83 | { 84 | min = r; 85 | mini = rsi.index; 86 | } 87 | else if (mini == rsi.index) 88 | { 89 | min = r; 90 | for (var j = 0; j < rsi.size; ++j) 91 | { 92 | if (j == rsi.index) 93 | { 94 | continue; 95 | } 96 | 97 | if (rsi.vals[j] < min) 98 | { 99 | min = rsi.vals[j]; 100 | mini = j; 101 | } 102 | } 103 | } 104 | 105 | BufferQPush(ref rsi, r); 106 | 107 | if (i > period * 2 - 2) 108 | { 109 | T diff = max - min; 110 | output[outputIndex++] = T.IsZero(diff) ? T.Zero : (r - min) / diff; 111 | } 112 | } 113 | 114 | return TI_OKAY; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Sub.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int SubStart(T[] options) => 0; 6 | 7 | private static int Sub(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple2(size, inputs[0], inputs[1], outputs[0], (d1, d2) => d1 - d2); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Sum.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int SumStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Sum(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= SumStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | for (var i = 0; i < period; ++i) 26 | { 27 | sum += input[i]; 28 | } 29 | 30 | int outputIndex = default; 31 | output[outputIndex++] = sum; 32 | for (var i = period; i < size; ++i) 33 | { 34 | sum = sum + input[i] - input[i - period]; 35 | output[outputIndex++] = sum; 36 | } 37 | 38 | return TI_OKAY; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Tan.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int TanStart(T[] options) => 0; 6 | 7 | private static int Tan(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Tan); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Tanh.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int TanhStart(T[] options) => 0; 6 | 7 | private static int Tanh(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Tanh); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Tema.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int TemaStart(T[] options) => (Int32.CreateTruncating(options[0]) - 1) * 3; 6 | 7 | private static int Tema(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= TemaStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T per = TTwo / T.CreateChecked(period + 1); 25 | T per1 = T.One - per; 26 | // Calculate EMA(input) 27 | T ema = input[0]; 28 | 29 | // Calculate EMA(EMA(input)) 30 | T ema2 = T.Zero; 31 | 32 | // Calculate EMA(EMA(EMA(input))) 33 | T ema3 = T.Zero; 34 | int outputIndex = default; 35 | for (var i = 0; i < size; ++i) 36 | { 37 | ema = ema * per1 + input[i] * per; 38 | if (i == period - 1) 39 | { 40 | ema2 = ema; 41 | } 42 | 43 | if (i >= period - 1) 44 | { 45 | ema2 = ema2 * per1 + ema * per; 46 | if (i == (period - 1) * 2) 47 | { 48 | ema3 = ema2; 49 | } 50 | 51 | if (i >= (period - 1) * 2) 52 | { 53 | ema3 = ema3 * per1 + ema2 * per; 54 | if (i >= (period - 1) * 3) 55 | { 56 | output[outputIndex++] = TThree * ema - TThree * ema2 + ema3; 57 | } 58 | } 59 | } 60 | } 61 | 62 | return TI_OKAY; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_ToDeg.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int ToDegStart(T[] options) => 0; 6 | 7 | private static int ToDeg(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.RadiansToDegrees); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_ToRad.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int ToRadStart(T[] options) => 0; 6 | 7 | private static int ToRad(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.DegreesToRadians); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Tr.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int TrStart(T[] options) => 0; 6 | 7 | private static int Tr(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var (high, low, close) = inputs; 10 | var output = outputs[0]; 11 | 12 | output[0] = high[0] - low[0]; 13 | for (var i = 1; i < size; ++i) 14 | { 15 | CalcTrueRange(low, high, close, i, out T trueRange); 16 | output[i] = trueRange; 17 | } 18 | 19 | return TI_OKAY; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Trima.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int TrimaStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Trima(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= TrimaStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | if (period <= 2) 22 | { 23 | return Sma(size, inputs, options, outputs); 24 | } 25 | 26 | var input = inputs[0]; 27 | var output = outputs[0]; 28 | 29 | T weightSum = T.Zero; // Weighted sum of previous numbers, spans one period back. 30 | T leadSum = T.Zero; // Flat sum of most recent numbers. 31 | T trailSum = T.Zero; // Flat sum of oldest numbers. 32 | int leadPeriod = period % 2 == 0 ? period / 2 - 1 : period / 2; 33 | int trailPeriod = leadPeriod + 1; 34 | int w = 1; 35 | for (var i = 0; i < period - 1; ++i) 36 | { 37 | weightSum += input[i] * T.CreateChecked(w); 38 | if (i + 1 > period - leadPeriod) 39 | { 40 | leadSum += input[i]; 41 | } 42 | 43 | if (i + 1 <= trailPeriod) 44 | { 45 | trailSum += input[i]; 46 | } 47 | 48 | if (i + 1 < trailPeriod) 49 | { 50 | ++w; 51 | } 52 | 53 | if (i + 1 >= period - leadPeriod) 54 | { 55 | --w; 56 | } 57 | } 58 | 59 | // Weights for 6 period TRIMA: 1 2 3 3 2 1 = 12 60 | // Weights for 7 period TRIMA: 1 2 3 4 3 2 1 = 16 61 | T weights = T.One / T.CreateChecked(period % 2 != 0 ? (period / 2 + 1) * (period / 2 + 1) : (period / 2 + 1) * (period / 2)); 62 | int lsi = period - leadPeriod; 63 | int tsi1 = period - period + trailPeriod; 64 | int tsi2 = period - period; 65 | int outputIndex = default; 66 | // Initialize until before the first value. 67 | for (var i = period - 1; i < size; ++i) 68 | { 69 | weightSum += input[i]; 70 | output[outputIndex++] = weightSum * weights; 71 | 72 | leadSum += input[i]; 73 | 74 | // 1 2 3 4 5 4 3 2 1 75 | weightSum += leadSum; 76 | // 1 2 3 4 5 5 4 3 2 77 | weightSum -= trailSum; 78 | // 1 2 3 4 5 4 3 2 79 | 80 | /* weightSum 1 2 3 4 5 4 3 2 1 81 | leadSum 1 1 1 1 82 | trailSum 1 1 1 1 1 */ 83 | leadSum -= input[lsi++]; 84 | trailSum += input[tsi1++]; 85 | trailSum -= input[tsi2++]; 86 | } 87 | 88 | return TI_OKAY; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Trix.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int TrixStart(T[] options) => (Int32.CreateTruncating(options[0]) - 1) * 3 + 1; 6 | 7 | private static int Trix(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= TrixStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | int start = period * 3 - 2; 25 | T per = TTwo / T.CreateChecked(period + 1); 26 | T ema1 = input[0]; 27 | T ema2 = T.Zero; 28 | T ema3 = T.Zero; 29 | for (var i = 1; i < start; ++i) 30 | { 31 | ema1 = (input[i] - ema1) * per + ema1; 32 | if (i == period - 1) 33 | { 34 | ema2 = ema1; 35 | } 36 | else if (i > period - 1) 37 | { 38 | ema2 = (ema1 - ema2) * per + ema2; 39 | if (i == period * 2 - 2) 40 | { 41 | ema3 = ema2; 42 | } 43 | else if (i > period * 2 - 2) 44 | { 45 | ema3 = (ema2 - ema3) * per + ema3; 46 | } 47 | } 48 | } 49 | 50 | int outputIndex = default; 51 | for (int i = start; i < size; ++i) 52 | { 53 | ema1 = (input[i] - ema1) * per + ema1; 54 | ema2 = (ema1 - ema2) * per + ema2; 55 | T last = ema3; 56 | ema3 = (ema2 - ema3) * per + ema3; 57 | output[outputIndex++] = (ema3 - last) / ema3 * THundred; 58 | } 59 | 60 | return TI_OKAY; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Trunc.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int TruncStart(T[] options) => 0; 6 | 7 | private static int Trunc(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | Simple1(size, inputs[0], outputs[0], T.Truncate); 10 | 11 | return TI_OKAY; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Tsf.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int TsfStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Tsf(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= TsfStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T x = T.Zero; // Sum of Xs. 25 | T x2 = T.Zero; // Sum of square of Xs. 26 | T y = T.Zero; // Flat sum of previous numbers. 27 | T xy = T.Zero; // Weighted sum of previous numbers. 28 | for (var i = 0; i < period - 1; ++i) 29 | { 30 | x += T.CreateChecked(i + 1); 31 | x2 += T.CreateChecked((i + 1) * (i + 1)); 32 | xy += input[i] * T.CreateChecked(i + 1); 33 | y += input[i]; 34 | } 35 | 36 | x += T.CreateChecked(period); 37 | x2 += T.CreateChecked(period * period); 38 | 39 | T p = T.One / T.CreateChecked(period); 40 | T bd = T.One / (T.CreateChecked(period) * x2 - x * x); 41 | int outputIndex = default; 42 | for (var i = period - 1; i < size; ++i) 43 | { 44 | xy += input[i] * T.CreateChecked(period); 45 | y += input[i]; 46 | T b = (T.CreateChecked(period) * xy - x * y) * bd; 47 | T a = (y - b * x) * p; 48 | output[outputIndex++] = a + b * T.CreateChecked(period + 1); 49 | xy -= y; 50 | y -= input[i - period + 1]; 51 | } 52 | 53 | return TI_OKAY; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_TypPrice.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int TypPriceStart(T[] options) => 0; 6 | 7 | private static int TypPrice(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var (high, low, close) = inputs; 10 | var output = outputs[0]; 11 | 12 | for (var i = 0; i < size; ++i) 13 | { 14 | output[i] = (high[i] + low[i] + close[i]) * (T.One / TThree); 15 | } 16 | 17 | return TI_OKAY; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_UltOsc.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int UltOscStart(T[] options) => Int32.CreateTruncating(options[2]); 6 | 7 | private static int UltOsc(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var shortPeriod = Int32.CreateTruncating(options[0]); 10 | var mediumPeriod = Int32.CreateTruncating(options[1]); 11 | var longPeriod = Int32.CreateTruncating(options[2]); 12 | 13 | if (shortPeriod < 1 || mediumPeriod < shortPeriod || longPeriod < mediumPeriod) 14 | { 15 | return TI_INVALID_OPTION; 16 | } 17 | 18 | if (size <= UltOscStart(options)) 19 | { 20 | return TI_OKAY; 21 | } 22 | 23 | var (high, low, close) = inputs; 24 | var output = outputs[0]; 25 | 26 | var bpBuf = BufferFactory(longPeriod); 27 | var rBuf = BufferFactory(longPeriod); 28 | T bpShortSum = T.Zero; 29 | T bpMediumSum = T.Zero; 30 | T rShortSum = T.Zero; 31 | T rMediumSum = T.Zero; 32 | int outputIndex = default; 33 | for (var i = 1; i < size; ++i) 34 | { 35 | T trueLow = T.Min(low[i], close[i - 1]); 36 | T trueHigh = T.Max(high[i], close[i - 1]); 37 | T bp = close[i] - trueLow; 38 | T r = trueHigh - trueLow; 39 | bpShortSum += bp; 40 | bpMediumSum += bp; 41 | rShortSum += r; 42 | rMediumSum += r; 43 | 44 | BufferPush(ref bpBuf, bp); 45 | BufferPush(ref rBuf, r); 46 | 47 | // The long sum takes care of itself, but we're piggy-backing the medium and short sums off the same buffers. 48 | if (i > shortPeriod) 49 | { 50 | int shortIndex = bpBuf.index - shortPeriod - 1; 51 | if (shortIndex < 0) 52 | { 53 | shortIndex += longPeriod; 54 | } 55 | 56 | bpShortSum -= bpBuf.vals[shortIndex]; 57 | rShortSum -= rBuf.vals[shortIndex]; 58 | if (i > mediumPeriod) 59 | { 60 | int mediumIndex = bpBuf.index - mediumPeriod - 1; 61 | if (mediumIndex < 0) 62 | { 63 | mediumIndex += longPeriod; 64 | } 65 | 66 | bpMediumSum -= bpBuf.vals[mediumIndex]; 67 | rMediumSum -= rBuf.vals[mediumIndex]; 68 | } 69 | } 70 | 71 | if (i >= longPeriod) 72 | { 73 | T first = T.CreateChecked(4) * bpShortSum / rShortSum; 74 | T second = TTwo * bpMediumSum / rMediumSum; 75 | T third = T.One * bpBuf.sum / rBuf.sum; 76 | T ult = (first + second + third) * THundred / T.CreateChecked(7); 77 | output[outputIndex++] = ult; 78 | } 79 | } 80 | 81 | return TI_OKAY; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Var.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int VarStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Var(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= VarStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | T sum2 = T.Zero; 26 | for (var i = 0; i < period; ++i) 27 | { 28 | sum += input[i]; 29 | sum2 += input[i] * input[i]; 30 | } 31 | 32 | T scale = T.One / T.CreateChecked(period); 33 | int outputIndex = default; 34 | output[outputIndex++] = sum2 * scale - sum * scale * (sum * scale); 35 | for (var i = period; i < size; ++i) 36 | { 37 | sum += input[i]; 38 | sum2 += input[i] * input[i]; 39 | 40 | sum -= input[i - period]; 41 | sum2 -= input[i - period] * input[i - period]; 42 | 43 | output[outputIndex++] = sum2 * scale - sum * scale * (sum * scale); 44 | } 45 | 46 | return TI_OKAY; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Vhf.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int VhfStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int Vhf(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= VhfStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | T yc = input[0]; 26 | for (var i = 1; i < period; ++i) 27 | { 28 | T c = input[i]; 29 | sum += T.Abs(c - yc); 30 | yc = c; 31 | } 32 | 33 | var maxi = -1; 34 | var mini = -1; 35 | T max = input[0]; 36 | T min = input[0]; 37 | int outputIndex = default; 38 | for (int i = period, trail = 1; i < size; ++i, ++trail) 39 | { 40 | T c = input[i]; 41 | sum += T.Abs(c - yc); 42 | yc = c; 43 | if (i > period) 44 | { 45 | sum -= T.Abs(input[i - period] - input[i - period - 1]); 46 | } 47 | 48 | // Maintain highest. 49 | T bar = c; 50 | if (maxi < trail) 51 | { 52 | maxi = trail; 53 | max = input[maxi]; 54 | int j = trail; 55 | while (++j <= i) 56 | { 57 | bar = input[j]; 58 | if (bar >= max) 59 | { 60 | max = bar; 61 | maxi = j; 62 | } 63 | } 64 | } 65 | else if (bar >= max) 66 | { 67 | maxi = i; 68 | max = bar; 69 | } 70 | 71 | // Maintain lowest. 72 | bar = c; 73 | if (mini < trail) 74 | { 75 | mini = trail; 76 | min = input[mini]; 77 | int j = trail; 78 | while (++j <= i) 79 | { 80 | bar = input[j]; 81 | if (bar <= min) 82 | { 83 | min = bar; 84 | mini = j; 85 | } 86 | } 87 | } 88 | else if (bar <= min) 89 | { 90 | mini = i; 91 | min = bar; 92 | } 93 | 94 | // Calculate it. 95 | output[outputIndex++] = T.Abs(max - min) / sum; 96 | } 97 | 98 | return TI_OKAY; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Vidya.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int VidyaStart(T[] options) => Int32.CreateTruncating(options[1]) - 2; 6 | 7 | private static int Vidya(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var shortPeriod = Int32.CreateTruncating(options[0]); 10 | var longPeriod = Int32.CreateTruncating(options[1]); 11 | var alpha = options[2]; 12 | 13 | if (shortPeriod < 1 || longPeriod < shortPeriod || longPeriod < 2 || alpha < T.Zero || alpha > T.One) 14 | { 15 | return TI_INVALID_OPTION; 16 | } 17 | 18 | if (size <= VidyaStart(options)) 19 | { 20 | return TI_OKAY; 21 | } 22 | 23 | var input = inputs[0]; 24 | var output = outputs[0]; 25 | 26 | T shortSum = T.Zero; 27 | T shortSum2 = T.Zero; 28 | T longSum = T.Zero; 29 | T longSum2 = T.Zero; 30 | for (var i = 0; i < longPeriod; ++i) 31 | { 32 | longSum += input[i]; 33 | longSum2 += input[i] * input[i]; 34 | if (i >= longPeriod - shortPeriod) 35 | { 36 | shortSum += input[i]; 37 | shortSum2 += input[i] * input[i]; 38 | } 39 | } 40 | 41 | T shortDiv = T.One / T.CreateChecked(shortPeriod); 42 | T longDiv = T.One / T.CreateChecked(longPeriod); 43 | T val = input[longPeriod - 2]; 44 | int outputIndex = default; 45 | output[outputIndex++] = val; 46 | if (longPeriod - 1 < size) 47 | { 48 | var shortStdDev = T.Sqrt(shortSum2 * shortDiv - shortSum * shortDiv * (shortSum * shortDiv)); 49 | var longStdDev = T.Sqrt(longSum2 * longDiv - longSum * longDiv * (longSum * longDiv)); 50 | T k = shortStdDev / longStdDev; 51 | 52 | k *= alpha; 53 | val = (input[longPeriod - 1] - val) * k + val; 54 | output[outputIndex++] = val; 55 | } 56 | 57 | for (var i = longPeriod; i < size; ++i) 58 | { 59 | longSum += input[i]; 60 | longSum2 += input[i] * input[i]; 61 | 62 | shortSum += input[i]; 63 | shortSum2 += input[i] * input[i]; 64 | 65 | longSum -= input[i - longPeriod]; 66 | longSum2 -= input[i - longPeriod] * input[i - longPeriod]; 67 | 68 | shortSum -= input[i - shortPeriod]; 69 | shortSum2 -= input[i - shortPeriod] * input[i - shortPeriod]; 70 | 71 | T shortStdDev = T.Sqrt(shortSum2 * shortDiv - shortSum * shortDiv * (shortSum * shortDiv)); 72 | T longStdDev = T.Sqrt(longSum2 * longDiv - longSum * longDiv * (longSum * longDiv)); 73 | T k = shortStdDev / longStdDev; 74 | 75 | k *= alpha; 76 | val = (input[i] - val) * k + val; 77 | output[outputIndex++] = val; 78 | } 79 | 80 | return TI_OKAY; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Volatility.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int VolatilityStart(T[] options) => Int32.CreateTruncating(options[0]); 6 | 7 | private static int Volatility(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= VolatilityStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | T sum2 = T.Zero; 26 | for (var i = 1; i <= period; ++i) 27 | { 28 | T c = input[i] / input[i - 1] - T.One; 29 | sum += c; 30 | sum2 += c * c; 31 | } 32 | 33 | T scale = T.One / T.CreateChecked(period); 34 | T annual = T.Sqrt(T.CreateChecked(252)); // Multiplier, number of trading days in year. 35 | int outputIndex = default; 36 | output[outputIndex++] = T.Sqrt(sum2 * scale - sum * scale * (sum * scale)) * annual; 37 | for (var i = period + 1; i < size; ++i) 38 | { 39 | T c = input[i] / input[i - 1] - T.One; 40 | sum += c; 41 | sum2 += c * c; 42 | 43 | T cp = input[i - period] / input[i - period - 1] - T.One; 44 | sum -= cp; 45 | sum2 -= cp * cp; 46 | 47 | output[outputIndex++] = T.Sqrt(sum2 * scale - sum * scale * (sum * scale)) * annual; 48 | } 49 | 50 | return TI_OKAY; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Vosc.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int VoscStart(T[] options) => Int32.CreateTruncating(options[1]) - 1; 6 | 7 | private static int Vosc(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var shortPeriod = Int32.CreateTruncating(options[0]); 10 | var longPeriod = Int32.CreateTruncating(options[1]); 11 | 12 | if (shortPeriod < 1 || longPeriod < shortPeriod) 13 | { 14 | return TI_INVALID_OPTION; 15 | } 16 | 17 | if (size <= VoscStart(options)) 18 | { 19 | return TI_OKAY; 20 | } 21 | 22 | var input = inputs[0]; 23 | var output = outputs[0]; 24 | 25 | T shortSum = T.Zero; 26 | T longSum = T.Zero; 27 | for (var i = 0; i < longPeriod; ++i) 28 | { 29 | if (i >= longPeriod - shortPeriod) 30 | { 31 | shortSum += input[i]; 32 | } 33 | 34 | longSum += input[i]; 35 | } 36 | 37 | T shortDiv = T.One / T.CreateChecked(shortPeriod); 38 | T longDiv = T.One / T.CreateChecked(longPeriod); 39 | T savg = shortSum * shortDiv; 40 | T lavg = longSum * longDiv; 41 | int outputIndex = default; 42 | output[outputIndex++] = THundred * (savg - lavg) / lavg; 43 | for (var i = longPeriod; i < size; ++i) 44 | { 45 | shortSum += input[i]; 46 | shortSum -= input[i - shortPeriod]; 47 | 48 | longSum += input[i]; 49 | longSum -= input[i - longPeriod]; 50 | 51 | savg = shortSum * shortDiv; 52 | lavg = longSum * longDiv; 53 | output[outputIndex++] = THundred * (savg - lavg) / lavg; 54 | } 55 | 56 | return TI_OKAY; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Vwma.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int VwmaStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Vwma(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= VwmaStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (input, volume) = inputs; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | T vSum = T.Zero; 26 | for (var i = 0; i < period; ++i) 27 | { 28 | sum += input[i] * volume[i]; 29 | vSum += volume[i]; 30 | } 31 | 32 | int outputIndex = default; 33 | output[outputIndex++] = sum / vSum; 34 | for (var i = period; i < size; ++i) 35 | { 36 | sum += input[i] * volume[i]; 37 | sum -= input[i - period] * volume[i - period]; 38 | vSum += volume[i]; 39 | vSum -= volume[i - period]; 40 | 41 | output[outputIndex++] = sum / vSum; 42 | } 43 | 44 | return TI_OKAY; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Wad.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int WadStart(T[] options) => 1; 6 | 7 | private static int Wad(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | if (size <= WadStart(options)) 10 | { 11 | return TI_OKAY; 12 | } 13 | 14 | var (high, low, close) = inputs; 15 | var output = outputs[0]; 16 | 17 | T sum = T.Zero; 18 | T yc = close[0]; 19 | int outputIndex = default; 20 | for (var i = 1; i < size; ++i) 21 | { 22 | T c = close[i]; 23 | if (c > yc) 24 | { 25 | sum += c - T.Min(yc, low[i]); 26 | } 27 | else if (c < yc) 28 | { 29 | sum += c - T.Max(yc, high[i]); 30 | } 31 | 32 | output[outputIndex++] = sum; 33 | yc = close[i]; 34 | } 35 | 36 | return TI_OKAY; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_WcPrice.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int WcPriceStart(T[] options) => 0; 6 | 7 | private static int WcPrice(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var (high, low, close) = inputs; 10 | var output = outputs[0]; 11 | 12 | for (var i = 0; i < size; ++i) 13 | { 14 | output[i] = (high[i] + low[i] + close[i] + close[i]) * T.CreateChecked(0.25); 15 | } 16 | 17 | return TI_OKAY; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Wilders.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int WildersStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Wilders(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= WildersStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T sum = T.Zero; 25 | for (var i = 0; i < period; ++i) 26 | { 27 | sum += input[i]; 28 | } 29 | 30 | T per = T.One / T.CreateChecked(period); 31 | T val = sum / T.CreateChecked(period); 32 | int outputIndex = default; 33 | output[outputIndex++] = val; 34 | for (var i = period; i < size; ++i) 35 | { 36 | val = (input[i] - val) * per + val; 37 | output[outputIndex++] = val; 38 | } 39 | 40 | return TI_OKAY; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_WillR.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int WillRStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int WillR(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= WillRStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var (high, low, close) = inputs; 22 | var output = outputs[0]; 23 | 24 | var maxi = -1; 25 | var mini = -1; 26 | T max = high[0]; 27 | T min = low[0]; 28 | int outputIndex = default; 29 | for (int i = period - 1, trail = 0; i < size; ++i, ++trail) 30 | { 31 | // Maintain highest. 32 | T bar = high[i]; 33 | if (maxi < trail) 34 | { 35 | maxi = trail; 36 | max = high[maxi]; 37 | int j = trail; 38 | while (++j <= i) 39 | { 40 | bar = high[j]; 41 | if (bar >= max) 42 | { 43 | max = bar; 44 | maxi = j; 45 | } 46 | } 47 | } 48 | else if (bar >= max) 49 | { 50 | maxi = i; 51 | max = bar; 52 | } 53 | 54 | 55 | // Maintain lowest. 56 | bar = low[i]; 57 | if (mini < trail) 58 | { 59 | mini = trail; 60 | min = low[mini]; 61 | int j = trail; 62 | while (++j <= i) 63 | { 64 | bar = low[j]; 65 | if (bar <= min) 66 | { 67 | min = bar; 68 | mini = j; 69 | } 70 | } 71 | } 72 | else if (bar <= min) 73 | { 74 | mini = i; 75 | min = bar; 76 | } 77 | 78 | // Calculate it. 79 | T highLow = max - min; 80 | T r = T.IsZero(highLow) ? T.Zero : T.NegativeOne * THundred * ((max - close[i]) / highLow); 81 | output[outputIndex++] = r; 82 | } 83 | 84 | return TI_OKAY; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_Wma.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int WmaStart(T[] options) => Int32.CreateTruncating(options[0]) - 1; 6 | 7 | private static int Wma(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= WmaStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | T weightSum = T.Zero; // Weighted sum of previous numbers. 25 | T sum = T.Zero; // Flat sum of previous numbers. 26 | for (var i = 0; i < period - 1; ++i) 27 | { 28 | weightSum += input[i] * T.CreateChecked(i + 1); 29 | sum += input[i]; 30 | } 31 | 32 | T weights = T.CreateChecked(period * (period + 1)) / TTwo; // Weights for 6 period WMA: 1 2 3 4 5 6 33 | int outputIndex = default; 34 | for (var i = period - 1; i < size; ++i) 35 | { 36 | weightSum += input[i] * T.CreateChecked(period); 37 | sum += input[i]; 38 | 39 | output[outputIndex++] = weightSum / weights; 40 | 41 | weightSum -= sum; 42 | sum -= input[i - period + 1]; 43 | } 44 | 45 | return TI_OKAY; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Indicators/TI_ZlEma.cs: -------------------------------------------------------------------------------- 1 | namespace Tulip; 2 | 3 | internal static partial class Tinet where T: IFloatingPointIeee754 4 | { 5 | private static int ZlEmaStart(T[] options) => (Int32.CreateTruncating(options[0]) - 1) / 2 - 1; 6 | 7 | private static int ZlEma(int size, T[][] inputs, T[] options, T[][] outputs) 8 | { 9 | var period = Int32.CreateTruncating(options[0]); 10 | 11 | if (period < 1) 12 | { 13 | return TI_INVALID_OPTION; 14 | } 15 | 16 | if (size <= ZlEmaStart(options)) 17 | { 18 | return TI_OKAY; 19 | } 20 | 21 | var input = inputs[0]; 22 | var output = outputs[0]; 23 | 24 | var lag = (period - 1) / 2; 25 | T per = TTwo / T.CreateChecked(period + 1); 26 | T val = input[lag - 1]; 27 | int outputIndex = default; 28 | output[outputIndex++] = val; 29 | for (var i = lag; i < size; ++i) 30 | { 31 | T c = input[i]; 32 | T l = input[i - lag]; 33 | val = (c + (c - l) - val) * per + val; 34 | output[outputIndex++] = val; 35 | } 36 | 37 | return TI_OKAY; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Tinet.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | 3 | namespace Tulip; 4 | 5 | internal static partial class Tinet where T: IFloatingPointIeee754 6 | { 7 | private const int TI_OKAY = 0; 8 | private const int TI_INVALID_OPTION = 1; 9 | 10 | const string LookbackSuffix = "Start"; 11 | 12 | private static T TTwo = T.CreateChecked(2); 13 | private static T TThree = T.CreateChecked(3); 14 | private static T THundred = T.CreateChecked(100); 15 | 16 | public static int IndicatorRun(string name, T[][] inputs, T[] options, T[][] outputs) 17 | { 18 | try 19 | { 20 | typeof(Tinet<>).MakeGenericType(typeof(T)).InvokeMember(name, 21 | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod, 22 | Type.DefaultBinder, null, [inputs[0].Length, inputs, options, outputs]); 23 | } 24 | catch (MissingMethodException) 25 | { 26 | return TI_INVALID_OPTION; 27 | } 28 | 29 | return TI_OKAY; 30 | } 31 | 32 | public static int IndicatorStart(string name, T[] options) 33 | { 34 | try 35 | { 36 | return Convert.ToInt32(typeof(Tinet<>).MakeGenericType(typeof(T)).InvokeMember($"{name}{LookbackSuffix}", 37 | BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.InvokeMethod, 38 | Type.DefaultBinder, null, [options])); 39 | } 40 | catch (MissingMethodException) 41 | { 42 | return TI_INVALID_OPTION; 43 | } 44 | } 45 | 46 | private static (int size, int pushes, int index, T sum, T[] vals) BufferFactory(int size) => 47 | (size, 0, 0, T.Zero, new T[size]); 48 | 49 | private static void BufferPush(ref (int size, int pushes, int index, T sum, T[] vals) buffer, T val) 50 | { 51 | if (buffer.pushes >= buffer.size) 52 | { 53 | buffer.sum -= buffer.vals[buffer.index]; 54 | } 55 | 56 | buffer.sum += val; 57 | buffer.vals[buffer.index++] = val; 58 | ++buffer.pushes; 59 | if (buffer.index >= buffer.size) 60 | { 61 | buffer.index = 0; 62 | } 63 | } 64 | 65 | private static void BufferQPush(ref (int size, int pushes, int index, T sum, T[] vals) buffer, T val) 66 | { 67 | buffer.vals[buffer.index++] = val; 68 | if (buffer.index >= buffer.size) 69 | { 70 | buffer.index = 0; 71 | } 72 | } 73 | 74 | private static T BufferGet((int, int, int, T, T[]) buffer, int val) 75 | { 76 | var (size, _, index, _, vals) = buffer; 77 | return vals[(Index) ((index + size - 1 + val) % size)]; 78 | } 79 | 80 | private static void CalcTrueRange(T[] low, T[] high, T[] close, int i, out T trueRange) 81 | { 82 | T l = low[i]; 83 | T h = high[i]; 84 | T c = close[i - 1]; 85 | T ych = T.Abs(h - c); 86 | T ycl = T.Abs(l - c); 87 | T v = h - l; 88 | if (ych > v) 89 | { 90 | v = ych; 91 | } 92 | 93 | if (ycl > v) 94 | { 95 | v = ycl; 96 | } 97 | 98 | trueRange = v; 99 | } 100 | 101 | private static void CalcDirection(T[] high, T[] low, int i, out T up, out T down) 102 | { 103 | up = high[i] - high[i - 1]; 104 | down = low[i - 1] - low[i]; 105 | 106 | if (up < T.Zero) 107 | { 108 | up = T.Zero; 109 | } 110 | else if (up > down) 111 | { 112 | down = T.Zero; 113 | } 114 | 115 | if (down < T.Zero) 116 | { 117 | down = T.Zero; 118 | } 119 | else if (down > up) 120 | { 121 | up = T.Zero; 122 | } 123 | } 124 | 125 | private static void Simple1(int size, T[] input, T[] output, Func op) 126 | { 127 | for (var i = 0; i < size; ++i) 128 | { 129 | output[i] = op(input[i]); 130 | } 131 | } 132 | 133 | private static void Simple2(int size, T[] input1, T[] input2, T[] output, Func op) 134 | { 135 | for (var i = 0; i < size; ++i) 136 | { 137 | output[i] = op(input1[i], input2[i]); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Tulip.NETCore/Tulip.NETCore.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | Tulip 6 | enable 7 | Tulip.NETCore 8 | A pure port to .NET (C#) of Tulip Indicators 9 | Anatoliy Siryi 10 | Tulip Charts LLC 11 | LICENSE 12 | https://tulipindicators.org 13 | 0.9.0 14 | https://github.com/hmG3/Tulip.NETCore 15 | git 16 | tulip.png 17 | tulip;technical;analysis;financial;data;stock;market 18 | 19 | 20 | 21 | 22 | True 23 | 24 | 25 | 26 | True 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/Tulip.NETCore.Tests/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | 3 | [assembly: ExcludeFromCodeCoverage] 4 | -------------------------------------------------------------------------------- /tests/Tulip.NETCore.Tests/DataSets/extra.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./testdata.schema.json", 3 | "_": [ 4 | { 5 | "name": "abs", 6 | "inputs": [ 7 | [ -1, 1, -4, 6 ] 8 | ], 9 | "outputs": [ 10 | [ 1, 1, 4, 6 ] 11 | ] 12 | }, 13 | { 14 | "name": "acos", 15 | "inputs": [ 16 | [ 0, 0.5, 1 ] 17 | ], 18 | "outputs": [ 19 | [ 1.571, 1.047, 0 ] 20 | ] 21 | }, 22 | { 23 | "name": "add", 24 | "inputs": [ 25 | [ 1, 5, 4, 6, 7, 8, 5, 1, 0, 6 ], 26 | [ 9, 4, 5, 6, 2, 1, 4, 5, 7, 0 ] 27 | ], 28 | "outputs": [ 29 | [ 10, 9, 9, 12, 9, 9, 9, 6, 7, 6 ] 30 | ] 31 | }, 32 | { 33 | "name": "adx", 34 | "inputs": [ 35 | [ 148.1150, 148.2260, 148.0270, 149.6030, 149.7390, 150.7820, 151.2470, 151.0840, 151.8550, 151.8740, 150.9770, 151.6930, 151.8770, 151.6500, 151.3160, 150.5250, 150.0720, 150.6560, 150.6690, 149.4280, 150.1440, 151.3860, 151.1880, 150.3250, 149.6250, 149.2370, 147.6560, 147.1880, 147.7920 ], 36 | [ 147.7340, 147.7860, 147.7870, 148.0260, 149.3680, 149.9570, 150.4670, 150.4070, 150.9670, 151.0030, 150.6900, 150.3730, 151.3120, 151.0280, 150.5250, 149.7250, 149.5620, 149.8850, 149.1220, 149.1930, 149.4970, 149.8870, 149.9450, 149.4930, 148.7150, 148.3210, 146.8740, 146.8130, 147.3350 ], 37 | [ 147.8460, 148.0270, 148.0260, 149.6030, 149.6820, 150.7820, 150.4690, 151.0840, 151.8550, 151.0030, 150.6900, 151.6930, 151.3120, 151.0280, 150.5250, 149.7250, 150.0720, 150.6560, 149.1220, 149.3260, 150.1440, 151.3860, 150.2320, 149.6250, 148.8880, 148.3210, 146.8740, 147.1660, 147.7920 ] 38 | ], 39 | "options": [ 14 ], 40 | "outputs": [ 41 | [ 36.6183, 34.6383, 32.173 ] 42 | ] 43 | }, 44 | { 45 | "name": "ao", 46 | "inputs": [ 47 | [ 32.1100, 27.6200, 28.2600, 28.0200, 26.9300, 26.6500, 27.2500, 27.5800, 27.9000, 28.9000, 29.3400, 29.8200, 29.5400, 29.3000, 29.5000, 29.5000, 29.7000, 29.1400, 27.1700, 30.3400, 30.2600, 30.1400, 29.9800, 30.5500, 32.1100, 34.1600, 39.5000, 50.7800, 51.3800, 51.3400, 50.7000, 44.2300, 42.7100, 39.8200, 42.3500, 44.7100, 44.2700, 43.6700, 44.8300, 44.5500, 46.8000, 46.2400, 45.5200, 44.5500, 46.1200, 44.7100, 44.4700, 45.2800, 44.6300, 43.4300, 46.2400, 49.1300, 49.9300, 49.9300, 51.3800, 52.7800, 50.5300, 50.6100, 49.3300, 49.4100, 53.3000, 52.5800, 62.3000, 61.5700, 62.5400, 64.5400, 74.1400, 73.1700, 70.1200, 68.6400, 71.7700, 70.6400, 71.7700, 104.0400, 103.4000, 97.6200, 98.9000, 99.4600, 89.2300, 87.3400, 82.2800, 78.9900, 81.5600, 78.0300, 73.8600, 77.6700, 79.4700, 77.7100, 76.7500, 78.3100, 77.7100, 72.2500, 68.0800, 66.3100, 65.7500, 64.1400, 67.4300, 80.2800, 78.3500 ], 48 | [ 25.6900, 25.5700, 25.7300, 25.6900, 25.6900, 26.1700, 26.0500, 26.2900, 26.8900, 27.7400, 28.4600, 29.0200, 28.4600, 28.7800, 29.0600, 28.7400, 28.5000, 27.0100, 26.3300, 26.9700, 29.1800, 29.6200, 29.5400, 29.7400, 30.4700, 31.8300, 34.6400, 40.4200, 47.6800, 48.9700, 43.2700, 41.5800, 38.7300, 37.3300, 37.8900, 43.3900, 42.6700, 41.1400, 42.3900, 43.0300, 43.7500, 44.8300, 43.3500, 42.6700, 43.6700, 42.6700, 43.5900, 43.1900, 43.3100, 41.5800, 42.5500, 45.9600, 47.4000, 48.1700, 48.9700, 50.2500, 48.6100, 48.3700, 48.0500, 48.0500, 49.1700, 50.9800, 49.8500, 56.5600, 57.8000, 58.8400, 64.5400, 66.3100, 63.1000, 65.0600, 66.9500, 65.8300, 67.3900, 72.2500, 84.1300, 89.9100, 93.1200, 89.9100, 83.1700, 76.3400, 72.7300, 71.9300, 75.8600, 72.5700, 67.9100, 70.0400, 75.1400, 74.6600, 72.6900, 72.8900, 70.6400, 63.6600, 57.8000, 58.9200, 52.6600, 57.8800, 62.3400, 68.7600, 67.6700 ] 49 | ], 50 | "outputs": [ 51 | [ 11.6905, 9.3535, 8.2531, 7.8816, 7.7612, 8.2594, 8.4822, 8.1794, 8.0454, 7.9502, 7.5005, 7.2510, 6.5143, 5.7713, 5.2844, 4.9243, 4.0526, 3.7438, 3.8741, 4.1156, 4.5317, 5.4641, 6.2518, 6.0741, 5.6701, 5.0864, 4.3346, 3.8620, 4.1222, 5.2467, 7.0596, 8.9599, 10.4984, 13.1686, 14.9850, 15.7149, 16.3803, 17.1528, 16.1721, 15.3763, 18.3787, 22.3355, 25.7980, 29.8361, 33.3549, 31.7510, 28.2440, 24.0074, 18.9790, 14.7623, 11.6177, 8.6476, 7.1438, 6.6704, 5.3673, 4.5294, 4.7640, 4.1044, 1.6913, -1.3769, -4.2062, -7.7196, -10.6241, -11.4972, -9.6358, -7.9344 ] 52 | ] 53 | }, 54 | { 55 | "name": "apo", 56 | "inputs": [ 57 | [ 25, 24.875, 24.781, 24.594, 24.5, 24.625, 25.219, 27.25 ] 58 | ], 59 | "options": [ 3, 5 ], 60 | "outputs": [ 61 | [ -0.0208, -0.04, -0.0709, -0.085, -0.0547, 0.0635, 0.4308 ] 62 | ] 63 | }, 64 | { 65 | "name": "aroonosc", 66 | "inputs": [ 67 | [ 27.5, 28.125, 29, 30.313, 29.75, 28.563, 29.831, 30.969, 30.594, 31.125, 32.5, 33.844, 34.469, 34.719, 33.031, 33.625, 34, 33.813, 33.156, 32.969, 33.813 ], 68 | [ 26.219, 26.313, 26.906, 29.188, 29.188, 26.594, 27.875, 30.25, 29.75, 29.813, 30.406, 32.844, 33.813, 32.688, 32.281, 32.281, 32.938, 32.688, 31.5, 31.375, 32.656 ] 69 | ], 70 | "options": [ 5 ], 71 | "outputs": [ 72 | [ 60, 40, 40, 40, 80, 100, 100, 80, 100, 80, 60, -40, -40, -100, -60, -60 ] 73 | ] 74 | }, 75 | { 76 | "name": "asin", 77 | "inputs": [ 78 | [ 0, 0.5, 1 ] 79 | ], 80 | "outputs": [ 81 | [ 0, 0.524, 1.571 ] 82 | ] 83 | }, 84 | { 85 | "name": "atan", 86 | "inputs": [ 87 | [ 0, 0.5, 1 ] 88 | ], 89 | "outputs": [ 90 | [ 0, 0.464, 0.785 ] 91 | ] 92 | }, 93 | { 94 | "name": "avgprice", 95 | "inputs": [ 96 | [ 81.85, 81.2, 81.55, 82.91, 83.1, 83.41, 82.71, 82.7, 84.2, 84.25, 84.03, 85.45 ], 97 | [ 82.15, 81.89, 83.03, 83.3, 83.85, 83.9, 83.33, 84.3, 84.84, 85, 85.9, 86.58 ], 98 | [ 81.29, 80.64, 81.31, 82.65, 83.07, 83.11, 82.49, 82.3, 84.15, 84.11, 84.03, 85.39 ], 99 | [ 81.59, 81.06, 82.87, 83, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54 ] 100 | ], 101 | "outputs": [ 102 | [ 81.720, 81.198, 82.190, 82.965, 83.408, 83.393, 82.843, 83.323, 84.435, 84.430, 84.873, 85.990 ] 103 | ] 104 | }, 105 | { 106 | "name": "bop", 107 | "inputs": [ 108 | [ 81.85, 81.2, 81.55, 82.91, 83.1, 83.41, 82.71, 82.7, 84.2, 84.25, 84.03, 85.45 ], 109 | [ 82.15, 81.89, 83.03, 83.3, 83.85, 83.9, 83.33, 84.3, 84.84, 85, 85.9, 86.58 ], 110 | [ 81.29, 80.64, 81.31, 82.65, 83.07, 83.11, 82.49, 82.3, 84.15, 84.11, 84.03, 85.39 ], 111 | [ 81.59, 81.06, 82.87, 83, 83.61, 83.15, 82.84, 83.99, 84.55, 84.36, 85.53, 86.54 ] 112 | ], 113 | "outputs": [ 114 | [ -0.3023, -0.112, 0.7674, 0.1385, 0.6538, -0.3291, 0.1548, 0.645, 0.5072, 0.1236, 0.8021, 0.916 ] 115 | ] 116 | }, 117 | { 118 | "name": "ceil", 119 | "inputs": [ 120 | [ 0, 0.5, 1 ] 121 | ], 122 | "outputs": [ 123 | [ 0, 1, 1 ] 124 | ] 125 | }, 126 | { 127 | "name": "cmo", 128 | "inputs": [ 129 | [ 101.0313, 101.0313, 101.125, 101.9687, 102.7813, 103, 102.9687, 103.0625, 102.9375, 102.7188, 102.75, 102.9063, 102.9687 ] 130 | ], 131 | "options": [ 10 ], 132 | "outputs": [ 133 | [ 69.62, 71.4286, 71.0839 ] 134 | ] 135 | }, 136 | { 137 | "name": "cos", 138 | "inputs": [ 139 | [ 0, 0.5, 1 ] 140 | ], 141 | "outputs": [ 142 | [ 1, 0.878, 0.540 ] 143 | ] 144 | }, 145 | { 146 | "name": "cosh", 147 | "inputs": [ 148 | [ 0, 0.5, 1 ] 149 | ], 150 | "outputs": [ 151 | [ 1, 1.128, 1.543 ] 152 | ] 153 | }, 154 | { 155 | "name": "crossany", 156 | "inputs": [ 157 | [ 4, 4, 6, 6, 6, 4, 4, 6, 5, 5, 5 ], 158 | [ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 ] 159 | ], 160 | "outputs": [ 161 | [ 0, 1, 0, 0, 1, 0, 1, 0, 0, 0 ] 162 | ] 163 | }, 164 | { 165 | "name": "crossover", 166 | "inputs": [ 167 | [ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 ], 168 | [ 4, 4, 6, 6, 6, 4, 4, 6, 5, 5, 5 ] 169 | ], 170 | "outputs": [ 171 | [ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 ] 172 | ] 173 | }, 174 | { 175 | "name": "decay", 176 | "inputs": [ 177 | [ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0 ] 178 | ], 179 | "options": [ 5 ], 180 | "outputs": [ 181 | [ 0, 0, 0, 0, 1, 0.8, 0.6, 0.4, 0.2, 0, 0, 1, 0.8 ] 182 | ] 183 | }, 184 | { 185 | "name": "div", 186 | "inputs": [ 187 | [ 1, 2, 7, 4 ], 188 | [ 2, 1, 4, 7 ] 189 | ], 190 | "outputs": [ 191 | [ 0.5, 2, 1.75, 0.571 ] 192 | ] 193 | }, 194 | { 195 | "name": "edecay", 196 | "inputs": [ 197 | [ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0 ] 198 | ], 199 | "options": [ 5 ], 200 | "outputs": [ 201 | [ 0, 0, 0, 0, 1, 0.8, 0.64, 0.512, 0.4096, 0.3277, 0.2621, 1, 0.8 ] 202 | ] 203 | }, 204 | { 205 | "name": "exp", 206 | "inputs": [ 207 | [ 0.5, 1, 10 ] 208 | ], 209 | "outputs": [ 210 | [ 1.649, 2.718, 22026.466 ] 211 | ] 212 | }, 213 | { 214 | "name": "floor", 215 | "inputs": [ 216 | [ 0, 0.5, 1 ] 217 | ], 218 | "outputs": [ 219 | [ 0, 0, 1 ] 220 | ] 221 | }, 222 | { 223 | "name": "kama", 224 | "inputs": [ 225 | [ 50.25, 50.55, 52.5, 54.5, 54.1, 54.12, 55.5, 50.2, 50.45, 50.24, 50.24, 55.12, 56.54, 56.12, 56.1, 54.12, 59.54, 54.52 ] 226 | ], 227 | "options": [ 4 ], 228 | "outputs": [ 229 | [ 54.5, 54.3732, 54.2948, 54.6461, 53.827, 53.3374, 52.8621, 51.8722, 53.118, 54.4669, 55.0451, 55.4099, 55.3468, 55.7115, 55.6875 ] 230 | ] 231 | }, 232 | { 233 | "name": "lag", 234 | "inputs": [ 235 | [ 1, 2, 3, 4, 5 ] 236 | ], 237 | "options": [ 1 ], 238 | "outputs": [ 239 | [ 1, 2, 3, 4 ] 240 | ] 241 | }, 242 | { 243 | "name": "linreg", 244 | "inputs": [ 245 | [ 10.5, 11.2, 10.3, 12.1, 11.2, 10.5, 11.1, 11.1, 10.2 ] 246 | ], 247 | "options": [ 5 ], 248 | "outputs": [ 249 | [ 11.52, 10.96, 11.04, 10.78, 10.54 ] 250 | ] 251 | }, 252 | { 253 | "name": "linregintercept", 254 | "inputs": [ 255 | [ 10.5, 11.2, 10.3, 12.1, 11.2, 10.5, 11.1, 11.1, 10.2 ] 256 | ], 257 | "options": [ 5 ], 258 | "outputs": [ 259 | [ 10.6, 11.16, 11.04, 11.62, 11.1 ] 260 | ] 261 | }, 262 | { 263 | "name": "linregslope", 264 | "inputs": [ 265 | [ 6, 7, 8, 9, 10, 11 ] 266 | ], 267 | "options": [ 5 ], 268 | "outputs": [ 269 | [ 1, 1 ] 270 | ] 271 | }, 272 | { 273 | "name": "ln", 274 | "inputs": [ 275 | [ 0.5, 1, 10 ] 276 | ], 277 | "outputs": [ 278 | [ -0.693, 0, 2.303 ] 279 | ] 280 | }, 281 | { 282 | "name": "log10", 283 | "inputs": [ 284 | [ 0.5, 1, 10 ] 285 | ], 286 | "outputs": [ 287 | [ -0.301, 0, 1 ] 288 | ] 289 | }, 290 | { 291 | "name": "marketfi", 292 | "inputs": [ 293 | [ 86, 88, 92, 90, 91, 102 ], 294 | [ 70, 85, 82, 81, 90, 95 ], 295 | [ 2500, 3521, 2541, 4582, 5214, 2541 ] 296 | ], 297 | "outputs": [ 298 | [ 0.0064, 0.0009, 0.0039, 0.0020, 0.0002, 0.0028 ] 299 | ] 300 | }, 301 | { 302 | "name": "max", 303 | "inputs": [ 304 | [ 1, 6, 4, 9, 2, 3, 4 ] 305 | ], 306 | "options": [ 3 ], 307 | "outputs": [ 308 | [ 6, 9, 9, 9, 4 ] 309 | ] 310 | }, 311 | { 312 | "name": "md", 313 | "inputs": [ 314 | [ 1, 2, 3, 4, 5, 17 ] 315 | ], 316 | "options": [ 3 ], 317 | "outputs": [ 318 | [ 0.6666, 0.6666, 0.6666, 5.5555 ] 319 | ] 320 | }, 321 | { 322 | "name": "min", 323 | "inputs": [ 324 | [ 1, 5, 7, 9, 2, 3, 4 ] 325 | ], 326 | "options": [ 3 ], 327 | "outputs": [ 328 | [ 1, 5, 2, 2, 2 ] 329 | ] 330 | }, 331 | { 332 | "name": "mul", 333 | "inputs": [ 334 | [ 1, 2, 7, 4 ], 335 | [ 2, 1, 4, 7 ] 336 | ], 337 | "outputs": [ 338 | [ 2, 2, 28, 28 ] 339 | ] 340 | }, 341 | { 342 | "name": "mom", 343 | "inputs": [ 344 | [ 1, 7, 12, 4, 5, 8, 11, 2, 9 ] 345 | ], 346 | "options": [ 3 ], 347 | "outputs": [ 348 | [ 3, -2, -4, 7, -3, 1 ] 349 | ] 350 | }, 351 | { 352 | "name": "natr", 353 | "inputs": [ 354 | [ 12.3125, 12.25, 12.3125, 12.2812, 12.375, 12.375, 12.3125, 12.375, 12.5, 12.5938, 12.3438, 12.4062, 12.375, 12.4062, 12.375, 12.2812, 12.25, 12.0625, 12.3125, 12.3125 ], 355 | [ 12.1875, 12.1562, 12.1875, 12.125, 12.125, 12.2812, 12.1875, 12.1875, 12.2812, 12.3125, 12.2812, 12.2812, 12.2812, 12.2812, 11.9688, 12.0938, 12, 11.6562, 12.1562, 12.125 ], 356 | [ 12.25, 12.1875, 12.25, 12.1875, 12.375, 12.3125, 12.2188, 12.3438, 12.5, 12.375, 12.3438, 12.3438, 12.2812, 12.375, 12.0938, 12.1562, 12.0625, 11.9688, 12.1875, 12.1562 ] 357 | ], 358 | "options": [ 4 ], 359 | "outputs": [ 360 | [ 1.0256, 1.2630, 1.1419, 1.1188, 1.2103, 1.3344, 1.5790, 1.3772, 1.2857, 1.1603, 1.1160, 1.6967, 1.6510, 1.7658, 2.1840, 2.3130, 2.1248 ] 361 | ] 362 | }, 363 | { 364 | "name": "ppo", 365 | "inputs": [ 366 | [ 25, 24.875, 24.781, 24.594, 24.5, 24.625, 25.219, 27.25 ] 367 | ], 368 | "options": [ 3, 5 ], 369 | "outputs": [ 370 | [ -0.0835, -0.1605, -0.2857, -0.3441, -0.2218, 0.2554, 1.6793 ] 371 | ] 372 | }, 373 | { 374 | "name": "pvi", 375 | "inputs": [ 376 | [ 6.5313, 6.5625, 6.4688, 6.4375, 6.2188, 6.25, 6.125, 6.1563, 6.25, 6.3125, 6.4688, 6.2813, 6.125, 6.1875, 6.1563, 6.2813, 6.2188, 6.1563, 6.1875, 6.375, 6.2813, 6.2813 ], 377 | [ 10500, 6492, 6540, 8924, 5416, 4588, 16236, 2864, 3080, 5312, 12456, 3020, 12228, 5248, 6632, 4104, 4464, 6212, 3836, 7640, 3544, 4660 ] 378 | ], 379 | "outputs": [ 380 | [ 1000, 1000, 985.7219048, 980.952381, 980.952381, 980.952381, 961.3333333, 961.3333333, 975.9650006, 985.7246506, 1010.131583, 1010.131583, 984.9960913, 984.9960913, 980.0293231, 980.0293231, 970.2778652, 960.5264072, 960.5264072, 989.6332681, 989.6332681, 989.6332681 ] 381 | ] 382 | }, 383 | { 384 | "name": "roc", 385 | "inputs": [ 386 | [ 5.5625, 5.375, 5.375, 5.0625, 5.1094, 5.1094, 5.0938, 5 ] 387 | ], 388 | "options": [ 3 ], 389 | "outputs": [ 390 | [ -0.08989, -0.04941, -0.04941, 0.00618, -0.02141 ] 391 | ] 392 | }, 393 | { 394 | "name": "round", 395 | "inputs": [ 396 | [ 0, -0.5, -1.1, -1.6, 1.5, 1.4, 1.6 ] 397 | ], 398 | "outputs": [ 399 | [ 0, 0, -1, -2, 2, 1, 2 ] 400 | ] 401 | }, 402 | { 403 | "name": "sin", 404 | "inputs": [ 405 | [ 0, 0.5, 1 ] 406 | ], 407 | "outputs": [ 408 | [ 0, 0.479, 0.841 ] 409 | ] 410 | }, 411 | { 412 | "name": "sinh", 413 | "inputs": [ 414 | [ 0, 0.5, 1 ] 415 | ], 416 | "outputs": [ 417 | [ 0, 0.521, 1.175 ] 418 | ] 419 | }, 420 | { 421 | "name": "sqrt", 422 | "inputs": [ 423 | [ 1, 4, 25, 144 ] 424 | ], 425 | "outputs": [ 426 | [ 1, 2, 5, 12 ] 427 | ] 428 | }, 429 | { 430 | "name": "stddev", 431 | "inputs": [ 432 | [ 100, 101, 102, 107, 115, 123, 100, 92, 99, 58, 150 ] 433 | ], 434 | "options": [ 3 ], 435 | "outputs": [ 436 | [ 0.816496581, 2.624669291, 5.354126135, 6.531972647, 9.533566431, 13.1402689, 3.559026084, 17.90716802, 37.63272807 ] 437 | ] 438 | }, 439 | { 440 | "name": "stderr", 441 | "inputs": [ 442 | [ 100, 101, 102, 107, 115, 123, 100, 92, 99, 58, 150 ] 443 | ], 444 | "options": [ 3 ], 445 | "outputs": [ 446 | [ 0.4714, 1.5154, 3.0912, 3.7712, 5.5042, 7.5865, 2.0548, 10.3387, 21.7273 ] 447 | ] 448 | }, 449 | { 450 | "name": "stochrsi", 451 | "inputs": [ 452 | [ 37.875, 39.5, 38.75, 39.8125, 40, 39.875, 40.1875, 41.25, 41.125, 41.625, 41.25, 40.1875, 39.9375, 39.9375, 40.5, 41.9375, 42.25, 42.25, 41.875, 41.875 ] 453 | ], 454 | "options": [ 5 ], 455 | "outputs": [ 456 | [ 0.9613, 0, 0, 0, 0, 0.46, 1, 1, 1, 0.3751, 0 ] 457 | ] 458 | }, 459 | { 460 | "name": "sub", 461 | "inputs": [ 462 | [ 1, 2, 7, 4 ], 463 | [ 2, 1, 4, 7 ] 464 | ], 465 | "outputs": [ 466 | [ -1, 1, 3, -3 ] 467 | ] 468 | }, 469 | { 470 | "name": "sum", 471 | "inputs": [ 472 | [ 1, 2, 3, 4, 5, 7, 8 ] 473 | ], 474 | "options": [ 2 ], 475 | "outputs": [ 476 | [ 3, 5, 7, 9, 12, 15 ] 477 | ] 478 | }, 479 | { 480 | "name": "tan", 481 | "inputs": [ 482 | [ 0, 0.5, 1 ] 483 | ], 484 | "outputs": [ 485 | [ 0, 0.546, 1.557 ] 486 | ] 487 | }, 488 | { 489 | "name": "tanh", 490 | "inputs": [ 491 | [ 0, 0.5, 1 ] 492 | ], 493 | "outputs": [ 494 | [ 0, 0.462, 0.762 ] 495 | ] 496 | }, 497 | { 498 | "name": "todeg", 499 | "inputs": [ 500 | [ 0, 1, 2, 3.14159, -1 ] 501 | ], 502 | "outputs": [ 503 | [ 0, 57.296, 114.592, 180, -57.296 ] 504 | ] 505 | }, 506 | { 507 | "name": "torad", 508 | "inputs": [ 509 | [ 0, 90, 180, 360, -90 ] 510 | ], 511 | "outputs": [ 512 | [ 0, 1.571, 3.142, 6.283, -1.571 ] 513 | ] 514 | }, 515 | { 516 | "name": "trunc", 517 | "inputs": [ 518 | [ 0, -0.5, -1.1, -1.6, 1.5, 1.4, 1.6 ] 519 | ], 520 | "outputs": [ 521 | [ 0, 0, -1, -1, 1, 1, 1 ] 522 | ] 523 | }, 524 | { 525 | "name": "tsf", 526 | "inputs": [ 527 | [ 10.5, 11.2, 10.3, 12.1, 11.2, 10.5, 11.1, 11.1, 10.2 ] 528 | ], 529 | "options": [ 5 ], 530 | "outputs": [ 531 | [ 11.75, 10.91, 11.04, 10.57, 10.4 ] 532 | ] 533 | }, 534 | { 535 | "name": "var", 536 | "inputs": [ 537 | [ 100, 101, 102, 107, 115, 123, 100, 92, 99, 58, 150 ] 538 | ], 539 | "options": [ 3 ], 540 | "outputs": [ 541 | [ 0.666666667, 6.888888889, 28.66666667, 42.66666667, 90.88888889, 172.6666667, 12.66666667, 320.6666667, 1416.222222 ] 542 | ] 543 | }, 544 | { 545 | "name": "vidya", 546 | "inputs": [ 547 | [ 50.25, 50.55, 52.5, 54.5, 54.1, 54.12, 55.5, 50.2, 50.45, 50.24, 50.24, 55.12, 56.54, 56.12, 56.1, 54.12, 59.54, 54.52 ] 548 | ], 549 | "options": [ 3, 6, 0.2 ], 550 | "outputs": [ 551 | [ 54.1000, 54.1004, 54.2148, 53.1633, 52.5165, 52.4937, 52.4732, 52.9862, 53.7103, 53.8114, 53.8453, 53.8693, 55.3888, 55.1443 ] 552 | ] 553 | }, 554 | { 555 | "name": "volatility", 556 | "inputs": [ 557 | [ 100, 101, 102, 107, 115, 123, 100, 92, 99, 58, 150 ] 558 | ], 559 | "options": [ 3 ], 560 | "outputs": [ 561 | [ 0.292367173, 0.423342593, 0.176459427, 1.939651221, 1.670293237, 1.714813191, 3.246143771, 13.51373467 ] 562 | ] 563 | }, 564 | { 565 | "name": "vwma", 566 | "inputs": [ 567 | [ 50.25, 50.55, 52.5, 54.5, 54.1, 54.12, 55.5, 50.2, 50.45, 50.24 ], 568 | [ 12412, 12458, 15874, 12354, 12456, 12542, 15421, 19510, 12521, 12041 ] 569 | ], 570 | "options": [ 4 ], 571 | "outputs": [ 572 | [ 51.9819, 52.8828, 53.7204, 54.6075, 53.1948, 52.4340, 51.6345 ] 573 | ] 574 | }, 575 | { 576 | "name": "zlema", 577 | "inputs": [ 578 | [ 50.25, 50.55, 52.5, 54.5, 54.1, 54.12, 55.5, 50.2, 50.45, 50.24, 50.24, 55.12, 56.54, 56.12, 56.1, 54.12, 59.54, 54.52 ] 579 | ], 580 | "options": [ 6 ], 581 | "outputs": [ 582 | [ 50.55, 51.7500, 53.6643, 54.2459, 54.1014, 54.9010, 52.4378, 50.4270, 50.3850, 50.2836, 53.0597, 55.8541, 56.2158, 56.0570, 54.9321, 57.2315, 56.5711 ] 583 | ] 584 | } 585 | ] 586 | } 587 | -------------------------------------------------------------------------------- /tests/Tulip.NETCore.Tests/DataSets/testdata.schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$id": "https://github.com/hmG3/Tulip.NETCore/testdata.schema.json", 3 | "$schema": "http://json-schema.org/draft-07/schema#", 4 | "title": "JSON Schema for data used in unit tests", 5 | "type": "object", 6 | "properties": { 7 | "data": { 8 | "type": "array", 9 | "items": { 10 | "type": "object", 11 | "properties": { 12 | "name": { 13 | "type": "string", 14 | "description": "Unique name of an indicator." 15 | }, 16 | "inputs": { 17 | "type": "array", 18 | "description": "An array of input values.", 19 | "minItems": 1, 20 | "items": { 21 | "type": "array", 22 | "items": { 23 | "type": "number" 24 | } 25 | } 26 | }, 27 | "options": { 28 | "type": "array", 29 | "description": "An array of options.", 30 | "items": { 31 | "type": "number" 32 | } 33 | }, 34 | "outputs": { 35 | "type": "array", 36 | "description": "An array of output value.", 37 | "minItems": 1, 38 | "items": { 39 | "type": "array", 40 | "items": { 41 | "type": "number" 42 | } 43 | } 44 | }, 45 | "skip": { 46 | "type": "boolean", 47 | "description": "Flag indicates to skip test." 48 | } 49 | }, 50 | "additionalProperties": false 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/Tulip.NETCore.Tests/Indicator_Tests.cs: -------------------------------------------------------------------------------- 1 | using Shouldly; 2 | using Tulip.NETCore.Tests.Models; 3 | using Xunit; 4 | 5 | namespace Tulip.NETCore.Tests; 6 | 7 | public sealed class Indicator_Tests 8 | { 9 | [SkippableTheory] 10 | [JsonFileData("DataSets/untest.json", typeof(double), "_")] 11 | [JsonFileData("DataSets/atoz.json", typeof(double), "_")] 12 | [JsonFileData("DataSets/extra.json", typeof(double), "_")] 13 | #pragma warning disable xUnit1026 14 | public void Should_Calculate_CorrectOutput_With_OKStatus_For_DoubleInput(TestDataModel model, string fileName) 15 | #pragma warning restore xUnit1026 16 | { 17 | Skip.If(model.Skip, "Test marked as skipped in the dataset."); 18 | 19 | const double equalityTolerance = 0.001d; 20 | 21 | Indicators.IndicatorsDefinition.ShouldContainKey(model.Name, $"Cannot find definition for '{model.Name}"); 22 | var indicator = Indicators.IndicatorsDefinition[model.Name]; 23 | 24 | model.Options.Length.ShouldBe(indicator.Options.Length, "Number of options must match the definition"); 25 | var inputOffset = indicator.Start(model.Options); 26 | model.Inputs.Length.ShouldBe(indicator.Inputs.Length, "Number of inputs must match the definition"); 27 | var outputLength = model.Inputs[0].Length - inputOffset; 28 | outputLength.ShouldBePositive("Output array should have the correct length"); 29 | 30 | var resultOutput = new double[model.Outputs.Length][]; 31 | resultOutput.Length.ShouldBe(indicator.Outputs.Length, "Number of outputs must match the definition"); 32 | for (var i = 0; i < resultOutput.Length; i++) 33 | { 34 | resultOutput[i] = new double[outputLength]; 35 | } 36 | 37 | var returnCode = indicator.Run(model.Inputs, model.Options, resultOutput); 38 | returnCode.ShouldBe(0, "Indicator should complete with success status code TI_OK(0)"); 39 | 40 | for (var i = 0; i < resultOutput.Length; i++) 41 | { 42 | resultOutput[i].Length.ShouldBe(model.Outputs[i].Length, 43 | $"Expected and calculated length of the output values should be equal for output {i + 1}"); 44 | resultOutput[i].ShouldBe(model.Outputs[i], equalityTolerance, 45 | $"Calculated values should be within expected for output {i + 1}"); 46 | } 47 | } 48 | 49 | [SkippableTheory] 50 | [JsonFileData("DataSets/untest.json", typeof(float), "_")] 51 | [JsonFileData("DataSets/atoz.json", typeof(float), "_")] 52 | [JsonFileData("DataSets/extra.json", typeof(float), "_")] 53 | #pragma warning disable xUnit1026 54 | public void Should_Calculate_CorrectOutput_With_OKStatus_For_FloatInput(TestDataModel model, string fileName) 55 | #pragma warning restore xUnit1026 56 | { 57 | Skip.If(model.Skip, "Test marked as skipped in the dataset."); 58 | Skip.If((fileName == "untest.json" && model.Name is "ad" or "adosc") || model.Name is "kvo", 59 | "The precision of floating-point arithmetic is insufficient for calculating accurate results."); 60 | 61 | const float equalityTolerance = 0.1f; 62 | 63 | Indicators.IndicatorsDefinition.ShouldContainKey(model.Name, $"Cannot find definition for '{model.Name}"); 64 | var indicator = Indicators.IndicatorsDefinition[model.Name]; 65 | 66 | model.Options.Length.ShouldBe(indicator.Options.Length, "Number of options must match the definition"); 67 | var inputOffset = indicator.Start(model.Options); 68 | model.Inputs.Length.ShouldBe(indicator.Inputs.Length, "Number of inputs must match the definition"); 69 | var outputLength = model.Inputs[0].Length - inputOffset; 70 | outputLength.ShouldBePositive("Output array should have the correct length"); 71 | 72 | var resultOutput = new float[model.Outputs.Length][]; 73 | resultOutput.Length.ShouldBe(indicator.Outputs.Length, "Number of outputs must match the definition"); 74 | for (var i = 0; i < resultOutput.Length; i++) 75 | { 76 | resultOutput[i] = new float[outputLength]; 77 | } 78 | 79 | var returnCode = indicator.Run(model.Inputs, model.Options, resultOutput); 80 | returnCode.ShouldBe(0, "Indicator should complete with success status code TI_OK(0)"); 81 | 82 | for (var i = 0; i < resultOutput.Length; i++) 83 | { 84 | resultOutput[i].Length.ShouldBe(model.Outputs[i].Length, 85 | $"Expected and calculated length of the output values should be equal for output {i + 1}"); 86 | resultOutput[i].ShouldBe(model.Outputs[i], equalityTolerance, 87 | $"Calculated values should be within expected for output {i + 1}"); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /tests/Tulip.NETCore.Tests/JsonFileDataAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Reflection; 5 | using System.Text.Json; 6 | using Tulip.NETCore.Tests.Models; 7 | using Xunit.Sdk; 8 | 9 | namespace Tulip.NETCore.Tests; 10 | 11 | public sealed class JsonFileDataAttribute : DataAttribute 12 | { 13 | private readonly string _filePath; 14 | private readonly Type _targetCollectionType; 15 | private readonly string? _propertyName; 16 | 17 | /// 18 | /// Load data from a JSON file as the data source for a theory 19 | /// 20 | /// The absolute or relative path to the JSON file to load 21 | /// The type of values for the test model 22 | /// The name of the property on the JSON file that contains the data for the test 23 | public JsonFileDataAttribute(string filePath, Type targetModelType, string? propertyName = null) 24 | { 25 | _filePath = filePath; 26 | _targetCollectionType = typeof(IEnumerable<>).MakeGenericType(typeof(TestDataModel<>).MakeGenericType(targetModelType)); 27 | _propertyName = propertyName; 28 | } 29 | 30 | /// 31 | public override IEnumerable GetData(MethodInfo testMethod) 32 | { 33 | ArgumentNullException.ThrowIfNull(testMethod); 34 | 35 | var path = Path.IsPathRooted(_filePath) 36 | ? _filePath 37 | : Path.GetRelativePath(Directory.GetCurrentDirectory(), _filePath); 38 | 39 | if (!File.Exists(path)) 40 | { 41 | throw new FileNotFoundException($"Could not find file at path {path}"); 42 | } 43 | 44 | IEnumerable dataModels; 45 | 46 | using (var fileStream = new FileStream(_filePath, FileMode.Open, FileAccess.Read)) 47 | using (var dataDocument = JsonDocument.Parse(fileStream)) 48 | { 49 | if (!String.IsNullOrEmpty(_propertyName)) 50 | { 51 | if (!dataDocument.RootElement.TryGetProperty(_propertyName.AsSpan(), out var dataProperty) || 52 | dataProperty.ValueKind != JsonValueKind.Array) 53 | { 54 | throw new JsonException($"Could not find property {_propertyName}"); 55 | } 56 | 57 | dataModels = (IEnumerable) dataProperty.ToObject(_targetCollectionType, 58 | new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); 59 | } 60 | else 61 | { 62 | dataModels = (IEnumerable) dataDocument.ToObject(_targetCollectionType, 63 | new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); 64 | } 65 | } 66 | 67 | foreach (var dataModel in dataModels) 68 | { 69 | yield return [dataModel, Path.GetFileName(_filePath)]; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/Tulip.NETCore.Tests/Models/JsonExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.Diagnostics; 4 | using System.Text.Json; 5 | 6 | namespace Tulip.NETCore.Tests.Models; 7 | 8 | public static class JsonExtensions 9 | { 10 | public static T ToObject(this JsonElement element, JsonSerializerOptions? options = null) 11 | { 12 | var bufferWriter = new ArrayBufferWriter(); 13 | using (var writer = new Utf8JsonWriter(bufferWriter)) 14 | { 15 | element.WriteTo(writer); 16 | } 17 | 18 | var result = JsonSerializer.Deserialize(bufferWriter.WrittenSpan, options); 19 | Debug.Assert(result != null); 20 | return result; 21 | } 22 | 23 | public static T ToObject(this JsonDocument document, JsonSerializerOptions? options = null) 24 | { 25 | ArgumentNullException.ThrowIfNull(document); 26 | 27 | return document.RootElement.ToObject(options); 28 | } 29 | 30 | public static object ToObject(this JsonElement element, Type returnType, JsonSerializerOptions? options = null) 31 | { 32 | var bufferWriter = new ArrayBufferWriter(); 33 | using (var writer = new Utf8JsonWriter(bufferWriter)) 34 | { 35 | element.WriteTo(writer); 36 | } 37 | 38 | try 39 | { 40 | JsonSerializer.Deserialize(bufferWriter.WrittenSpan, returnType, options); 41 | } 42 | catch (Exception e) 43 | { 44 | Console.WriteLine(e); 45 | throw; 46 | } 47 | 48 | var result = JsonSerializer.Deserialize(bufferWriter.WrittenSpan, returnType, options); 49 | Debug.Assert(result != null); 50 | return result; 51 | } 52 | 53 | public static object ToObject(this JsonDocument document, Type returnType, JsonSerializerOptions? options = null) 54 | { 55 | ArgumentNullException.ThrowIfNull(document); 56 | 57 | return document.RootElement.ToObject(returnType, options); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/Tulip.NETCore.Tests/Models/TestDataModel.cs: -------------------------------------------------------------------------------- 1 | using System.Numerics; 2 | 3 | namespace Tulip.NETCore.Tests.Models; 4 | 5 | public class TestDataModel where T : IFloatingPointIeee754 6 | { 7 | public string Name { get; set; } = null!; 8 | 9 | public T[][] Inputs { get; set; } = null!; 10 | 11 | public T[] Options { get; set; } = []; 12 | 13 | public T[][] Outputs { get; set; } = null!; 14 | 15 | public bool Skip { get; set; } 16 | 17 | public override string ToString() => Name; 18 | } 19 | -------------------------------------------------------------------------------- /tests/Tulip.NETCore.Tests/Tulip.NETCore.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | false 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | PreserveNewest 31 | 32 | 33 | PreserveNewest 34 | 35 | 36 | PreserveNewest 37 | 38 | 39 | PreserveNewest 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /tulip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hmG3/Tulip.NETCore/c0ad7b98d07515b42cb3b104277e42a6503e9500/tulip.png --------------------------------------------------------------------------------