├── .deepsource.toml ├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ └── function-returned-unexpected-results.md └── workflows │ └── php.yml ├── .gitignore ├── .gitlab-ci.yml ├── .travis.yml ├── CHANGELOG.md ├── LICENSE.txt ├── README.md ├── composer.json ├── composer.lock ├── phpunit.xml ├── phpunit_coverage.xml ├── source ├── LupeTrader.php ├── LupeTraderFriendly.php ├── TALib │ ├── Classes │ │ ├── CandleSetting.php │ │ └── MoneyFlow.php │ ├── Core │ │ ├── Core.php │ │ ├── CycleIndicators.php │ │ ├── Lookback.php │ │ ├── MathOperators.php │ │ ├── MathTransform.php │ │ ├── MomentumIndicators.php │ │ ├── OverlapStudies.php │ │ ├── PatternRecognition.php │ │ ├── PriceTransform.php │ │ ├── StatisticFunctions.php │ │ ├── VolatilityIndicators.php │ │ └── VolumeIndicators.php │ └── Enum │ │ ├── CandleSettingType.php │ │ ├── Compatibility.php │ │ ├── MovingAverageType.php │ │ ├── RangeType.php │ │ ├── ReturnCode.php │ │ ├── ReturnMessages.php │ │ └── UnstablePeriodFunctionID.php ├── Trader.php ├── TraderFriendly.php ├── polyfill.php └── polyfill │ ├── enumErrorCode.php │ ├── enumFunctionUnstablePeriod.php │ ├── enumMaType.php │ └── functions.php └── tests ├── LupeTraderFriendlyTest.php ├── LupeTraderTest.php ├── TestingTrait.php ├── TraderFriendlyTest.php └── TraderTest.php /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | test_patterns = ["tests/*Test.php"] 4 | 5 | [[analyzers]] 6 | name = "php" 7 | enabled = true -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | max_line_length = 200 10 | tab_width = 4 11 | trim_trailing_whitespace = true 12 | 13 | [*.json] 14 | indent_size = 2 15 | 16 | [*.md] 17 | max_line_length = 0 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/function-returned-unexpected-results.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Function Returned Unexpected Results 3 | about: Use this issue template when a function returns results that were different 4 | from the expected. 5 | title: "[Function Returned Unexpected Results]" 6 | labels: 'Type: Bug, Type: Testing' 7 | assignees: jb-lopez 8 | 9 | --- 10 | 11 | ### Function Name: 12 | *Which function did not return the expected results?* 13 | 14 | ### Input Variables: 15 | *What data did you supply to the function? Feel free to shorten the data as long as it still can produce unexpected results.* 16 | 17 | ### Expected Results: 18 | *What results did you expect to receive?* 19 | 20 | ### Actual Results: 21 | *What results did you actually receive?* 22 | 23 | ### Comparison Source: 24 | *How do you know the results are wrong? What are you comparing it to?* 25 | -------------------------------------------------------------------------------- /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHPUnit Tests 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | phpunit: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | php: [8.2, 8.3, 8.4] 15 | steps: 16 | - uses: actions/checkout@v1 17 | - uses: shivammathur/setup-php@v2 18 | with: 19 | php-version: ${{ matrix.php }} 20 | tools: pecl 21 | extensions: trader 22 | - name: Install Composer dependencies 23 | run: composer install --prefer-dist --no-progress 24 | - name: Run tests 25 | run: php vendor/bin/phpunit 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS generated files # 2 | ###################### 3 | ehthumbs.db 4 | Thumbs.db 5 | 6 | # Backup Files # 7 | ################ 8 | *~ 9 | *.bak 10 | 11 | # Packages # 12 | ############ 13 | *.7z 14 | *.dmg 15 | *.gz 16 | *.iso 17 | *.jar 18 | *.rar 19 | *.tar 20 | *.zip 21 | 22 | # Logs and databases # 23 | ###################### 24 | *.log 25 | *.sql 26 | *.sqlite 27 | 28 | # IDE files # 29 | ############# 30 | nbproject 31 | *.iml 32 | *.ipr 33 | 34 | # dot files and directories # 35 | ############################# 36 | .* 37 | !/.gitignore 38 | !/.gitlab-ci.yml 39 | !/.travis.yml 40 | !/.editorconfig 41 | 42 | # Project specific files and directories # 43 | ########################################## 44 | ta-lib/ 45 | talib/ 46 | pecl/ 47 | logs/ 48 | test-results/ 49 | metrics/ 50 | vendor/ 51 | *.bat 52 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | .tests: 2 | stage: test 3 | before_script: 4 | - apt-get update -yqq > /dev/null 5 | - apt-get install git zlib1g-dev -yqq > /dev/null 6 | - echo "memory_limit = 256M" >> "$PHP_INI_DIR/php.ini" 7 | - docker-php-ext-install zip > /dev/null 8 | - pecl install xdebug > /dev/null 9 | - pecl install trader > /dev/null 10 | - docker-php-ext-enable trader 11 | - curl -sS https://getcomposer.org/installer | php > /dev/null 12 | - php composer.phar update 13 | script: 14 | - vendor/bin/phpunit --configuration ./phpunit.xml 15 | - docker-php-ext-enable xdebug 16 | - vendor/bin/phpunit --coverage-text --colors=never --configuration ./phpunit_coverage.xml 17 | cache: 18 | paths: 19 | - vendor/ 20 | 21 | test:7.0: 22 | image: php:7.0 23 | extends: .tests 24 | test:7.1: 25 | image: php:7.1 26 | extends: .tests 27 | test:7.2: 28 | image: php:7.2 29 | extends: .tests 30 | test:7.3: 31 | image: php:7.3 32 | extends: .tests 33 | test:7.4: 34 | image: php:7.4 35 | extends: .tests 36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | env: 2 | global: 3 | - CC_TEST_REPORTER_ID=0ba904403dda60fd3095aa5d56887d3b004f935f27cecc5133a3a09fcf35a69a 4 | - GIT_COMMITTED_AT=$(if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then git log -1 --pretty=format:%ct; else git log -1 --skip 1 --pretty=format:%ct; fi) 5 | 6 | language: php 7 | php: 8 | - 7.0 9 | - 7.1 10 | - 7.2 11 | - 7.3 12 | - 7.4 13 | 14 | before_script: 15 | - sudo apt-get update > /dev/null 16 | - echo "memory_limit = 256M" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini 17 | - pecl install trader > /dev/null 18 | - composer update 19 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 20 | - chmod +x ./cc-test-reporter 21 | - ./cc-test-reporter before-build 22 | 23 | script: 24 | - vendor/bin/phpunit --configuration ./phpunit_coverage.xml --coverage-clover build/logs/clover.xml 25 | 26 | after_success: 27 | - php vendor/bin/codacycoverage clover build/logs/clover.xml 28 | - wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.0.0/php-coveralls.phar 29 | - php php-coveralls.phar --verbose; 30 | - if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT; fi 31 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## 2.1.1 4 | * Figured out how to use .gitattributes to exclude files from the composer package. 5 | * Now I can add the TA-Lib C source files to the repository and not have them show up in the composer package. 🎉 6 | ## 2.1.0 7 | * Added new functions from TA-Lib v0.6.0. 8 | * [ACCBANDS](https://github.com/TA-Lib/ta-lib/blob/main/src/ta_func/ta_ACCBANDS.c) 9 | * [AVGDEV](https://github.com/TA-Lib/ta-lib/blob/main/src/ta_func/ta_AVGDEV.c) 10 | * [IMI](https://github.com/TA-Lib/ta-lib/blob/main/src/ta_func/ta_IMI.c) 11 | ## 2.0.1 12 | * Fixed [Issue #16](https://github.com/LupeCode/phpTraderNative/issues/16) 13 | * Removed version from composer.json as per their spec. 14 | ## 2.0.0 15 | * Cleaned up git repository. 16 | * Drop support for PHP 7.X and 8.0. 17 | * Updated dependencies. 18 | * Cleaned up the code and use PSR-12 formatting. 19 | * Changed the LICENSE to MIT. 20 | * Updated PHPUnit. 21 | * Added better testing data. 22 | ## 1.2.3 23 | * Small updates and bug fixes. 24 | * Added .deepsource.toml for DeepSource.io. 25 | ## 1.2.2 26 | * GitLab CI and TravisCI no longer build this package 27 | ## 1.2.1 28 | * Added PHP 7.0.x to the CI tests. 29 | * Also various things to get this to work in the CI runners. 30 | ## 1.2.0 31 | * Added the static variable to the test. Use this instead of a string just in case the string for the errors change. 32 | * Test all of the MA types that stochrsi supports. 33 | * Added some test data that matches [Issue #2](https://github.com/LupeCode/phpTraderNative/issues/2). 34 | * Added the Lupe Trader classes. 35 | * These classes will override certain functions that have been identified to produce bad results. 36 | * See [Issue #2](https://github.com/LupeCode/phpTraderNative/issues/2). 37 | * Wrote tests with hand-done math to verify that the returned results match what it should be. 38 | * There were already tests that made sure the results matched the PECL Trader extension, but what if that was wrong? 39 | * I didn't to it by hand *per se*, but in a spreadsheet. I'm not *that* good at math. 40 | * Added the Slow Stoch RSI function to solve [Issue #2](https://github.com/LupeCode/phpTraderNative/issues/2). 41 | * Moved the common testing data into a trait for classes that cannot extend the `TraderTest` class. 42 | * Added the `.gitlab-ci.yml` and `.travis.yml` files. 43 | * Hey, who can say no to free code testing and reporting, right? 44 | ## 1.1.0 45 | * Removed the MyInteger class, using plain integers now. 46 | * Made the return message constant and easier to test against. 47 | * Switched to using the core classes in a static way. This will improve operating time when the same class of functions are used multiple times in one execution. 48 | * Added tests that cause coverage to hang with a group that can be skipped with `--exclude-group exceptions` 49 | * Updated the static references. 50 | ## 1.0.5 51 | * Added an installation notice to the README. 52 | ## 1.0.4 53 | * Fixed [Issue # 1](https://github.com/LupeCode/phpTraderNative/issues/1) 54 | * Shortened the code for htDcPeriod. 55 | ## 1.0.3 56 | * Added a test that shows that the PECL version and the C/JAVA versions of TA-Lib have different defaults. 57 | * Added testing for the TraderFriendly class. 58 | * Changed the version requirement of PECL Trader to 0.4.1 for require-dev. 59 | * Removed unused internal functions. 60 | ## 1.0.2 61 | * Updated for PECL Trader version 0.4.1. 62 | * Optimized the code. 63 | * Made sure that the original copyright notice was in all of the ported code. 64 | * Set up local tests to be in multiple versions of PHP; all versions from 7.0.x to 7.2.x. 65 | ## 1.0.1 66 | * Removed the old java code that was unused. 67 | ## 1.0.0 68 | * Lots and lots of function documentation. 69 | * Moved the optional parameters in to the function definitions instead of in the body of the function. 70 | ## 0.4.0 71 | * Finished all of the classes; the library now has the same functionality as the TA-Lib code it is ported from. 72 | ## 0.3.0 73 | * Set up the basic scaffolding of the library. 74 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) Lupe Code, LLC.; Joshua Lopez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Trader Native 2 | 3 | This is a PHP port of the Trader extension for PHP, which is a port of the TA-LIB C/Java code. 4 | 5 | This port is written in PHP and without any other requirements. 6 | 7 | The goal is that this library can be used by those whom cannot install the PHP Trader extension. 8 | 9 | ![PHPUnit Tests](https://github.com/LupeCode/phpTraderNative/workflows/PHPUnit%20Tests/badge.svg) 10 | 11 | ## Requirements 12 | 13 | * PHP >= 8.2.0 14 | 15 | That's the only thing you need! As stated, you do not need to install any extensions for this library. 16 | 17 | ## Installation 18 | 19 | This library is intended to be installed with composer. 20 | 21 | ~~~ 22 | composer require lupecode/php-trader-native 23 | ~~~ 24 | 25 | ## Usage 26 | 27 | ### Drop-In Replacement 28 | 29 | This library is intended to be a drop-in replacement for the Trader extension, it comes with a polyfill for the Trader extension. 30 | 31 | ### Friendly-Named Replacement 32 | 33 | Another option that this package provides is to use functions that have an easier to understand name. 34 | 35 | If you do not want to use `adosc` because it is not descriptive enough, you can instead use `chaikinAccumulationDistributionOscillator` like this: 36 | `TraderFriendly::chaikinAccumulationDistributionOscillator($high, $low, $close, $volume, $fastPeriod, $slowPeriod)` 37 | 38 | ## Note about default values 39 | The PECL version of the TA-LIB, "Trader", does not have the correct default values for the functions. 40 | A quick look shows that many of the function use the minimum value for the optional parameters instead of the defaults used in the C/Java version of TA-LIB. 41 | Some of the tests, like `testAdOscDefaultsDifferent` pass as long as the PECL Trader library uses different defaults than those in the C/Java code. 42 | 43 | For the curious, the TA-LIB source for AdOsc can be seen [here](https://svn.php.net/viewvc/pecl/trader/trunk/ta-lib/src/ta_func/ta_ADOSC.c?revision=325828&view=markup) with defaults of 3 and 10, 44 | while the PECL Trader source can be seen [here](https://svn.php.net/viewvc/pecl/trader/trunk/functions/trader_adosc.c?revision=344243&view=markup) with defaults of 2 and 2. 45 | 46 | **This package uses the C/Java defaults and not the PECL defaults.** 47 | 48 | ## Speed 49 | 50 | Given that this library is written in pure PHP, is does run slower than the PECL extension which is written in C. 51 | My benchmarks give 5x to 30x slower depending on the function. 52 | 53 | **I welcome any help with optimizations!** 54 | I have not worked to optimize this library; it's a simple conversion from C to PHP. 55 | 56 | ## Contributing/Development 57 | 58 | ### Requirements 59 | 60 | * PHP >= 8.0.0 61 | * ext_trader >= 0.4.1 [here](https://pecl.php.net/package/trader) 62 | 63 | ### Setup 64 | 65 | Checkout the repository and then install with composer. 66 | 67 | ~~~ 68 | git checkout git@github.com:LupeCode/phpTraderNative.git 69 | cd phpTraderNative 70 | composer install --dev 71 | ~~~ 72 | 73 | ### Directory Structure 74 | 75 | * `source` - The source code for the library. 76 | * `tests` - The PHPUnit tests for the library. 77 | * `pecl` - The PECL Trader extension source code. 78 | * `talib` - The TA-LIB source code that the PECL Trader extension uses. 79 | 80 | ### Testing 81 | 82 | Two PHPUnit XML files are included, one for testing and the other for coverage. This is due to the fact that when some tests are run with coverage, PHP hangs and never finishes. 83 | 84 | Run the tests using 85 | ~~~ 86 | php -dxdebug.coverage_enable=0 ./vendor/phpunit/phpunit/phpunit --configuration ./phpunit.xml ./tests 87 | ~~~ 88 | 89 | Run the coverage using 90 | ~~~ 91 | php -dxdebug.coverage_enable=1 ./vendor/phpunit/phpunit/phpunit --configuration ./phpunit_coverage.xml ./tests 92 | ~~~ 93 | 94 | ## License 95 | 96 | Below is the copyright information for TA-LIB found in the source code. 97 | 98 | ~~~ 99 | TA-LIB Copyright (c) 1999-2007, Mario Fortier 100 | All rights reserved. 101 | 102 | Redistribution and use in source and binary forms, with or without 103 | modification, are permitted provided that the following conditions are met: 104 | 105 | - Redistributions of source code must retain the above copyright notice, 106 | this list of conditions and the following disclaimer. 107 | 108 | - Redistributions in binary form must reproduce the above copyright 109 | notice, this list of conditions and the following disclaimer in the 110 | documentation and/or other materials provided with the distribution. 111 | 112 | - Neither name of author nor the names of its contributors may be used 113 | to endorse or promote products derived from this software without 114 | specific prior written permission. 115 | 116 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 117 | ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 118 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 119 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR 120 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 121 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 122 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 123 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 124 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 125 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 126 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 127 | ~~~ 128 | 129 | Below is the license for my porting of the code to PHP. 130 | 131 | ~~~ 132 | MIT License 133 | 134 | Copyright (C) Lupe Code, LLC.; Joshua Lopez 135 | 136 | Permission is hereby granted, free of charge, to any person obtaining a copy 137 | of this software and associated documentation files (the "Software"), to deal 138 | in the Software without restriction, including without limitation the rights 139 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 140 | copies of the Software, and to permit persons to whom the Software is 141 | furnished to do so, subject to the following conditions: 142 | 143 | The above copyright notice and this permission notice shall be included in all 144 | copies or substantial portions of the Software. 145 | 146 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 147 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 148 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 149 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 150 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 151 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 152 | SOFTWARE. 153 | ~~~ 154 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lupecode/php-trader-native", 3 | "description": "An native version of the PHP Trader extension.", 4 | "type": "library", 5 | "license": "GPL-3.0-or-later", 6 | "authors":[ 7 | { 8 | "name": "Joshua Lopez", 9 | "email": "joshua@lupecode.com", 10 | "homepage": "https://github.com/jb-lopez", 11 | "role": "Developer" 12 | } 13 | ], 14 | "version": "v2.2.0", 15 | "require": { 16 | "php": ">=8.2.0" 17 | }, 18 | "require-dev": { 19 | "ext-ds": "*", 20 | "ext-trader": ">=0.4.1", 21 | "phpbench/phpbench": "~1.2.14", 22 | "phpunit/phpunit": "~10.3.1" 23 | }, 24 | "minimum-stability": "dev", 25 | "prefer-stable": true, 26 | "autoload": { 27 | "psr-4": { 28 | "LupeCode\\phpTraderNative\\": "source/", 29 | "LupeCode\\phpTraderNativeTest\\": "tests/" 30 | }, 31 | "files": [ 32 | "source/polyfill.php" 33 | ] 34 | }, 35 | "scripts": { 36 | "test": "phpunit" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | tests 20 | tests/benchmark 21 | 22 | 23 | 24 | 25 | dev_trader 26 | 27 | 28 | 29 | 30 | 31 | ./source 32 | 33 | 34 | ./vendor 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /phpunit_coverage.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | tests 20 | tests/benchmark 21 | 22 | 23 | 24 | 25 | exceptions 26 | 27 | 28 | 29 | 30 | 31 | 32 | ./source 33 | 34 | 35 | ./vendor 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /source/LupeTrader.php: -------------------------------------------------------------------------------- 1 | acos($x), $real); 22 | } 23 | 24 | /** 25 | * Chaikin A/D Line 26 | * 27 | * This indicator is a volume based indicator developed by Marc Chaikin which measures the cumulative flow of money into and out of an instrument. 28 | * The A/D line is calculated by multiplying the specific period’s volume with a multiplier that is based on the relationship of the closing price to the high-low range. 29 | * The A/D Line is formed by the running total of the Money Flow Volume. This indicator can be used to assert an underlying trend or to predict reversals. 30 | * 31 | * The combination of a high positive multiplier value and high volume indicates buying pressure. 32 | * So even with a downtrend in prices when there is an uptrend in the Accumulation Distribution Line there is indication for buying pressure (accumulation) that may result to a bullish reversal. 33 | * 34 | * Conversely a low negative multiplier value combined with, again, high volumes indicates selling pressure (distribution). 35 | * 36 | * @param array $high High price, array of real values. 37 | * @param array $low Low price, array of real values. 38 | * @param array $close Closing price, array of real values. 39 | * @param array $volume Volume traded, array of real values. 40 | * 41 | * @return array Returns an array with calculated data. 42 | * @throws \Exception 43 | */ 44 | public static function chaikinAccumulationDistributionLine(array $high, array $low, array $close, array $volume): array 45 | { 46 | $count = self::verifyArrayCounts([$high, $low, $close, $volume]); 47 | $result = new \SplFixedArray($count + 1); 48 | $moneyFlowVolume = 0.0; 49 | for ($i = 0; $i <= $count; $i++) { 50 | $denominator = $high[$i] - $low[$i]; 51 | if ($denominator > 0) { 52 | $moneyFlowMultiplier = (($close[$i] - $low[$i]) - ($high[$i] - $close[$i])) / $denominator; 53 | $moneyFlowVolume += ($moneyFlowMultiplier * $volume[$i]); 54 | } 55 | $result[$i] = $moneyFlowVolume; 56 | } 57 | 58 | return $result->toArray(); 59 | } 60 | 61 | public static function ad(array $high, array $low, array $close, array $volume): array 62 | { 63 | return self::chaikinAccumulationDistributionLine($high, $low, $close, $volume); 64 | } 65 | 66 | /** 67 | * Calculates the vector addition of real0 to real1 and returns the resulting vector. 68 | * 69 | * @param array $real0 Array of real values. 70 | * @param array $real1 Array of real values. 71 | * 72 | * @return array Returns an array with calculated data. 73 | * @throws \Exception 74 | */ 75 | public static function add(array $real0, array $real1): array 76 | { 77 | return array_map(fn($x, $y) => $x + $y, $real0, $real1); 78 | } 79 | 80 | /** 81 | * Chaikin A/D Oscillator 82 | * 83 | * Chaikin Oscillator is positive when the 3-day EMA moves higher than the 10-day EMA and vice versa. 84 | * 85 | * The Chaikin Oscillator is the continuation of the Chaikin A/D Line and is used to observe changes in the A/D Line. 86 | * 87 | * The oscillator is based on the difference between the 3-day Exponential Moving Average (EMA) of the A/D Line and the 10-day EMA of the A/D Line and hence adds momentum to the A/D Line. 88 | * It is helpful for investors to use the Oscillator in order to determine the appropriate timing of trend reversals. 89 | * 90 | * When the Chaikin Oscillator turns positive there is indication that the A/D Line will increase and hence a Bullish (buy) signal will be generated. On the other hand a move into negative territory indicates a Bearish (sell) signal. 91 | * 92 | * @param array $high High price, array of real values. 93 | * @param array $low Low price, array of real values. 94 | * @param array $close Closing price, array of real values. 95 | * @param array $volume Volume traded, array of real values. 96 | * @param int $fastPeriod [OPTIONAL] [DEFAULT 3, SUGGESTED 4-200] Number of period for the fast MA. Valid range from 2 to 100000. 97 | * @param int $slowPeriod [OPTIONAL] [DEFAULT 10, SUGGESTED 4-200] Number of period for the slow MA. Valid range from 2 to 100000. 98 | * 99 | * @return array Returns an array with calculated data. 100 | * @throws \Exception 101 | */ 102 | public static function chaikinOscillator(array $high, array $low, array $close, array $volume, int $fastPeriod = 3, int $slowPeriod = 10): array 103 | { 104 | $count = self::verifyArrayCounts([$high, $low, $close, $volume]); 105 | $fastK = 2 / ($fastPeriod + 1); 106 | $slowK = 2 / ($slowPeriod + 1); 107 | $oneMinusFastK = 1 - $fastK; 108 | $oneMinusSlowK = 1 - $slowK; 109 | 110 | $ad = self::ad($high, $low, $close, $volume); 111 | $fastEma = $slowEma = $ad[0]; 112 | $output = []; 113 | 114 | for ($i = 1; $i <= $count; $i++) { 115 | $fastEma = ($fastK * $ad[$i]) + ($oneMinusFastK * $fastEma); 116 | $slowEma = ($slowK * $ad[$i]) + ($oneMinusSlowK * $slowEma); 117 | $output[$i] = $fastEma - $slowEma; 118 | } 119 | 120 | return self::adjustIndexes(array_slice($output, $slowPeriod - 2), $slowPeriod - 1); 121 | } 122 | 123 | public static function adosc(array $high, array $low, array $close, array $volume, int $fastPeriod = 3, int $slowPeriod = 10): array 124 | { 125 | return self::chaikinOscillator($high, $low, $close, $volume, $fastPeriod, $slowPeriod); 126 | } 127 | 128 | /** 129 | * Average Directional Movement Index 130 | * 131 | * Developed by J. Welles Wilder and described in his book “New Concepts in Technical Trading Systems”, the Average Directional Movement Index (ADX) is a technical indicator that describes if a market or a financial instrument is trending or not. 132 | * 133 | * The ADX is a combination of two other indicators developed by Wilder, the positive directional indicator (+DI) and the negative directional indicator (-DI). 134 | * 135 | * Wilder recommends buying when +DI is higher than -DI, and selling when +DI is lower than -DI. 136 | * 137 | * The ADX indicates trend strength, not trend direction, and it is a lagging indicator. 138 | * 139 | * ADX range is between 0 and 100. Generally ADX readings below 20 indicate trend weakness, and readings above 40 indicate trend strength. 140 | * An extremely strong trend is indicated by readings above 50. 141 | * 142 | * @param array $high High price, array of real values. 143 | * @param array $low Low price, array of real values. 144 | * @param array $close Closing price, array of real values. 145 | * @param int $timePeriod [OPTIONAL] [DEFAULT 14, SUGGESTED 4-200] Number of period. Valid range from 2 to 100000. 146 | * 147 | * @return array Returns an array with calculated data. 148 | * @throws \Exception 149 | */ 150 | public static function averageDirectionalMovementIndex(array $high, array $low, array $close, int $timePeriod = 14): array 151 | { 152 | $count = self::verifyArrayCounts([$high, $low, $close]); 153 | $plus = self::plusDI($high, $low, $close, $timePeriod); 154 | $minus = self::minusDI($high, $low, $close, $timePeriod); 155 | $sum = self::add($plus, $minus); 156 | $result = new \SplFixedArray($count + 1); 157 | for ($i = 0; $i <= $count; $i++) { 158 | $result[$i] = $sum[$i] > 0 ? 100 * abs($plus[$i] - $minus[$i]) / $sum[$i] : 0; 159 | } 160 | 161 | return $result->toArray(); 162 | } 163 | 164 | /** 165 | * Plus Directional Indicator 166 | * 167 | * @param array $high High price, array of real values. 168 | * @param array $low Low price, array of real values. 169 | * @param array $close Closing price, array of real values. 170 | * @param int $timePeriod [OPTIONAL] [DEFAULT 14, SUGGESTED 1-200] Number of period. Valid range from 1 to 100000. 171 | * 172 | * @return array Returns an array with calculated data. 173 | * @throws \Exception 174 | */ 175 | public static function plusDI(array $high, array $low, array $close, int $timePeriod = 14): array 176 | { 177 | return self::plus_di($high, $low, $close, $timePeriod); 178 | } 179 | 180 | /** 181 | * Minus Directional Indicator 182 | * 183 | * @param array $high High price, array of real values. 184 | * @param array $low Low price, array of real values. 185 | * @param array $close Closing price, array of real values. 186 | * @param int $timePeriod [OPTIONAL] [DEFAULT 14, SUGGESTED 1-200] Number of period. Valid range from 1 to 100000. 187 | * 188 | * @return array Returns an array with calculated data. 189 | * @throws \Exception 190 | */ 191 | public static function minusDI(array $high, array $low, array $close, int $timePeriod = 14): array 192 | { 193 | return self::minus_di($high, $low, $close, $timePeriod); 194 | } 195 | 196 | /** 197 | * Slow Stochastic Relative Strength Index 198 | * 199 | * @param array $real Array of real values. 200 | * @param int $rsi_period [OPTIONAL] [DEFAULT 14, SUGGESTED 4-200] Number of period. Valid range from 2 to 100000. 201 | * @param int $fastK_Period [OPTIONAL] [DEFAULT 5, SUGGESTED 1-200] Time period for building the Fast-K line. Valid range from 1 to 100000. 202 | * @param int $slowK_Period [OPTIONAL] [DEFAULT 3, SUGGESTED 1-200] Smoothing for making the Slow-K line. Valid range from 1 to 100000, usually set to 3. 203 | * @param int $slowK_MAType [OPTIONAL] [DEFAULT TRADER_MA_TYPE_SMA] Type of Moving Average for Slow-K. MovingAverageType::* series of constants should be used. 204 | * @param int $slowD_Period [OPTIONAL] [DEFAULT 3, SUGGESTED 1-200] Smoothing for making the Slow-D line. Valid range from 1 to 100000. 205 | * @param int $slowD_MAType [OPTIONAL] [DEFAULT TRADER_MA_TYPE_SMA] Type of Moving Average for Slow-D. MovingAverageType::* series of constants should be used. 206 | * 207 | * @return array Returns an array with calculated data. [SlowK => [...], SlowD => [...]] 208 | * @throws \Exception 209 | */ 210 | public static function slowstochrsi( 211 | array $real, 212 | int $rsi_period = 14, 213 | int $fastK_Period = 5, 214 | int $slowK_Period = 3, 215 | int $slowK_MAType = MovingAverageType::SMA->value, 216 | int $slowD_Period = 3, 217 | int $slowD_MAType = MovingAverageType::SMA->value 218 | ): array { 219 | $real = \array_values($real); 220 | $endIdx = count($real) - 1; 221 | $rsi = []; 222 | self::checkForError(self::getMomentumIndicators()::rsi(0, $endIdx, $real, $rsi_period, self::$outBegIdx, self::$outNBElement, $rsi)); 223 | $rsi = array_values($rsi); 224 | $endIdx = self::verifyArrayCounts([&$rsi]); 225 | $outSlowK = []; 226 | $outSlowD = []; 227 | self::checkForError( 228 | self::getMomentumIndicators()::stoch( 229 | 0, 230 | $endIdx, 231 | $rsi, 232 | $rsi, 233 | $rsi, 234 | $fastK_Period, 235 | $slowK_Period, 236 | $slowK_MAType, 237 | $slowD_Period, 238 | $slowD_MAType, 239 | self::$outBegIdx, 240 | self::$outNBElement, 241 | $outSlowK, 242 | $outSlowD 243 | ) 244 | ); 245 | 246 | return [ 247 | 'SlowK' => self::adjustIndexes($outSlowK, self::$outBegIdx), 248 | 'SlowD' => self::adjustIndexes($outSlowD, self::$outBegIdx), 249 | ]; 250 | } 251 | 252 | } 253 | -------------------------------------------------------------------------------- /source/LupeTraderFriendly.php: -------------------------------------------------------------------------------- 1 | [...], SlowD => [...]] 22 | * @throws \Exception 23 | */ 24 | public static function slowStochasticRelativeStrengthIndex( 25 | array $real, 26 | int $rsi_period = 14, 27 | int $fastK_Period = 5, 28 | int $slowK_Period = 3, 29 | int $slowK_MAType = MovingAverageType::SMA->value, 30 | int $slowD_Period = 3, 31 | int $slowD_MAType = MovingAverageType::SMA->value 32 | ): array { 33 | return LupeTrader::slowstochrsi($real, $rsi_period, $fastK_Period, $slowK_Period, $slowK_MAType, $slowD_Period, $slowD_MAType); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /source/TALib/Classes/CandleSetting.php: -------------------------------------------------------------------------------- 1 | settingType = $settingType; 62 | $this->rangeType = $rangeType; 63 | $this->avgPeriod = $avgPeriod; 64 | $this->factor = $factor; 65 | } 66 | 67 | public function CopyFrom(CandleSetting $source) 68 | { 69 | $this->settingType = $source->settingType; 70 | $this->rangeType = $source->rangeType; 71 | $this->avgPeriod = $source->avgPeriod; 72 | $this->factor = $source->factor; 73 | } 74 | 75 | public function CandleSetting(CandleSetting $that) 76 | { 77 | $this->settingType = $that->settingType; 78 | $this->rangeType = $that->rangeType; 79 | $this->avgPeriod = $that->avgPeriod; 80 | $this->factor = $that->factor; 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /source/TALib/Classes/MoneyFlow.php: -------------------------------------------------------------------------------- 1 | value; 67 | 68 | /** 69 | * Core constructor. 70 | * 71 | * These settings would be set above, but are not allowed to be defaults for static variables. 72 | */ 73 | public static function construct(): void 74 | { 75 | static::$candleSettings = [ 76 | /* real body is long when it's longer than the average of the 10 previous candles' real body */ 77 | new CandleSetting(CandleSettingType::BodyLong->value, RangeType::RealBody->value, 10, 1.), 78 | /* real body is very long when it's longer than 3 times the average of the 10 previous candles' real body */ 79 | new CandleSetting(CandleSettingType::BodyVeryLong->value, RangeType::RealBody->value, 10, 3.), 80 | /* real body is short when it's shorter than the average of the 10 previous candles' real bodies */ 81 | new CandleSetting(CandleSettingType::BodyShort->value, RangeType::RealBody->value, 10, 1.), 82 | /* real body is like doji's body when it's shorter than 10% the average of the 10 previous candles' high-low range */ 83 | new CandleSetting(CandleSettingType::BodyDoji->value, RangeType::HighLow->value, 10, 0.1), 84 | /* shadow is long when it's longer than the real body */ 85 | new CandleSetting(CandleSettingType::ShadowLong->value, RangeType::RealBody->value, 0, 1.), 86 | /* shadow is very long when it's longer than 2 times the real body */ 87 | new CandleSetting(CandleSettingType::ShadowVeryLong->value, RangeType::RealBody->value, 0, 2.), 88 | /* shadow is short when it's shorter than half the average of the 10 previous candles' sum of shadows */ 89 | new CandleSetting(CandleSettingType::ShadowShort->value, RangeType::Shadows->value, 10, 1.), 90 | /* shadow is very short when it's shorter than 10% the average of the 10 previous candles' high-low range */ 91 | new CandleSetting(CandleSettingType::ShadowVeryShort->value, RangeType::HighLow->value, 10, 0.1), 92 | /* when measuring distance between parts of candles or width of gaps "near" means "<= 20% of the average of the 5 previous candles' high-low range" */ 93 | new CandleSetting(CandleSettingType::Near->value, RangeType::HighLow->value, 5, 0.2), 94 | /* when measuring distance between parts of candles or width of gaps "far" means ">= 60% of the average of the 5 previous candles' high-low range" */ 95 | new CandleSetting(CandleSettingType::Far->value, RangeType::HighLow->value, 5, 0.6), 96 | /* when measuring distance between parts of candles or width of gaps "equal" means "<= 5% of the average of the 5 previous candles' high-low range" */ 97 | new CandleSetting(CandleSettingType::Equal->value, RangeType::HighLow->value, 5, 0.05), 98 | ]; 99 | // Changed to correct array size to avoid the "Uncaught ErrorException: Undefined array key 22" 100 | // static::$unstablePeriod = \array_pad([], UnstablePeriodFunctionID::ALL - 2, 0); 101 | static::$unstablePeriod = \array_pad([], UnstablePeriodFunctionID::ALL->value, 0); 102 | } 103 | 104 | public static function setUnstablePeriod(int $functionID, int $unstablePeriod): void 105 | { 106 | static::$unstablePeriod[$functionID] = $unstablePeriod; 107 | } 108 | 109 | public static function getUnstablePeriod(int $functionID): int 110 | { 111 | return static::$unstablePeriod[$functionID]; 112 | } 113 | 114 | public static function setCompatibility(int $compatibility): void 115 | { 116 | static::$compatibility = $compatibility; 117 | } 118 | 119 | public static function getCompatibility(): int 120 | { 121 | return static::$compatibility; 122 | } 123 | 124 | protected static function double(int $size): array 125 | { 126 | return \array_pad([], $size, 0.); 127 | } 128 | 129 | protected static function validateStartEndIndexes(int $startIdx, int $endIdx): int 130 | { 131 | if ($startIdx < 0) { 132 | return ReturnCode::OutOfRangeStartIndex->value; 133 | } 134 | if (($endIdx < 0) || ($endIdx < $startIdx)) { 135 | return ReturnCode::OutOfRangeEndIndex->value; 136 | } 137 | 138 | return ReturnCode::Success->value; 139 | } 140 | 141 | protected static function TA_INT_PO( 142 | int $startIdx, 143 | int $endIdx, 144 | array $inReal, 145 | int $optInFastPeriod, 146 | int $optInSlowPeriod, 147 | int $optInMethod_2, 148 | int &$outBegIdx, 149 | int &$outNBElement, 150 | array &$outReal, 151 | array &$tempBuffer, 152 | bool $doPercentageOutput 153 | ): int { 154 | $outBegIdx1 = 0; 155 | $outNbElement1 = 0; 156 | $outBegIdx2 = 0; 157 | $outNbElement2 = 0; 158 | if ($optInSlowPeriod < $optInFastPeriod) { 159 | $tempInteger = $optInSlowPeriod; 160 | $optInSlowPeriod = $optInFastPeriod; 161 | $optInFastPeriod = $tempInteger; 162 | } 163 | $ReturnCode = OverlapStudies::movingAverage($startIdx, $endIdx, $inReal, $optInFastPeriod, $optInMethod_2, $outBegIdx2, $outNbElement2, $tempBuffer); 164 | if ($ReturnCode === ReturnCode::Success->value) { 165 | $ReturnCode = OverlapStudies::movingAverage($startIdx, $endIdx, $inReal, $optInSlowPeriod, $optInMethod_2, $outBegIdx1, $outNbElement1, $outReal); 166 | if ($ReturnCode === ReturnCode::Success->value) { 167 | $tempInteger = $outBegIdx1 - $outBegIdx2; 168 | if ($doPercentageOutput) { 169 | for ($i = 0, $j = $tempInteger; $i < $outNbElement1; $i++, $j++) { 170 | $tempReal = $outReal[$i]; 171 | if (!(((-0.00000001) < $tempReal) && ($tempReal < 0.00000001))) { 172 | $outReal[$i] = (($tempBuffer[$j] - $tempReal) / $tempReal) * 100.0; 173 | } else { 174 | $outReal[$i] = 0.0; 175 | } 176 | } 177 | } else { 178 | for ($i = 0, $j = $tempInteger; $i < $outNbElement1; $i++, $j++) { 179 | $outReal[$i] = $tempBuffer[$j] - $outReal[$i]; 180 | } 181 | } 182 | $outBegIdx = $outBegIdx1; 183 | $outNBElement = $outNbElement1; 184 | } 185 | } 186 | if ($ReturnCode !== ReturnCode::Success->value) { 187 | $outBegIdx = 0; 188 | $outNBElement = 0; 189 | } 190 | 191 | return $ReturnCode; 192 | } 193 | 194 | protected static function TA_INT_MACD( 195 | int $startIdx, 196 | int $endIdx, 197 | array $inReal, 198 | int $optInFastPeriod, 199 | int $optInSlowPeriod, 200 | int $optInSignalPeriod_2, 201 | int &$outBegIdx, 202 | int &$outNBElement, 203 | array &$outMACD, 204 | array &$outMACDSignal, 205 | array &$outMACDHist 206 | ): int { 207 | //double[] $slowEMABuffer; 208 | //double[] $fastEMABuffer; 209 | //double $k1, $k2; 210 | //ReturnCode $ReturnCode; 211 | //int $tempInteger; 212 | $outBegIdx1 = 0; 213 | $outNbElement1 = 0; 214 | $outBegIdx2 = 0; 215 | $outNbElement2 = 0; 216 | //int $lookbackTotal, $lookbackSignal; 217 | //int $i; 218 | if ($optInSlowPeriod < $optInFastPeriod) { 219 | $tempInteger = $optInSlowPeriod; 220 | $optInSlowPeriod = $optInFastPeriod; 221 | $optInFastPeriod = $tempInteger; 222 | } 223 | if ($optInSlowPeriod !== 0) { 224 | $k1 = (2.0 / ((double)($optInSlowPeriod + 1))); 225 | } else { 226 | $optInSlowPeriod = 26; 227 | $k1 = 0.075; 228 | } 229 | if ($optInFastPeriod !== 0) { 230 | $k2 = (2.0 / ((double)($optInFastPeriod + 1))); 231 | } else { 232 | $optInFastPeriod = 12; 233 | $k2 = 0.15; 234 | } 235 | $lookbackSignal = Lookback::emaLookback($optInSignalPeriod_2); 236 | $lookbackTotal = $lookbackSignal; 237 | $lookbackTotal += Lookback::emaLookback($optInSlowPeriod); 238 | if ($startIdx < $lookbackTotal) { 239 | $startIdx = $lookbackTotal; 240 | } 241 | if ($startIdx > $endIdx) { 242 | $outBegIdx = 0; 243 | $outNBElement = 0; 244 | 245 | return ReturnCode::Success->value; 246 | } 247 | $tempInteger = ($endIdx - $startIdx) + 1 + $lookbackSignal; 248 | $fastEMABuffer = static::double($tempInteger); 249 | $slowEMABuffer = static::double($tempInteger); 250 | $tempInteger = $startIdx - $lookbackSignal; 251 | $ReturnCode = static::TA_INT_EMA($tempInteger, $endIdx, $inReal, $optInSlowPeriod, $k1, $outBegIdx1, $outNbElement1, $slowEMABuffer); 252 | if ($ReturnCode !== ReturnCode::Success->value) { 253 | $outBegIdx = 0; 254 | $outNBElement = 0; 255 | 256 | return $ReturnCode; 257 | } 258 | $ReturnCode = static::TA_INT_EMA($tempInteger, $endIdx, $inReal, $optInFastPeriod, $k2, $outBegIdx2, $outNbElement2, $fastEMABuffer); 259 | if ($ReturnCode !== ReturnCode::Success->value) { 260 | $outBegIdx = 0; 261 | $outNBElement = 0; 262 | 263 | return $ReturnCode; 264 | } 265 | if (($outBegIdx1 !== $tempInteger) || 266 | ($outBegIdx2 !== $tempInteger) || 267 | ($outNbElement1 !== $outNbElement2) || 268 | ($outNbElement1 !== ($endIdx - $startIdx) + 1 + $lookbackSignal)) { 269 | $outBegIdx = 0; 270 | $outNBElement = 0; 271 | 272 | return ReturnCode::InternalError->value; 273 | } 274 | for ($i = 0; $i < $outNbElement1; $i++) { 275 | $fastEMABuffer[$i] -= $slowEMABuffer[$i]; 276 | } 277 | //System::arraycopy($fastEMABuffer, $lookbackSignal, $outMACD, 0, ($endIdx - $startIdx) + 1); 278 | $outMACD = \array_slice($fastEMABuffer, $lookbackSignal, ($endIdx - $startIdx) + 1); 279 | $ReturnCode = static::TA_INT_EMA(0, $outNbElement1 - 1, $fastEMABuffer, $optInSignalPeriod_2, (2.0 / ((double)($optInSignalPeriod_2 + 1))), $outBegIdx2, $outNbElement2, $outMACDSignal); 280 | if ($ReturnCode !== ReturnCode::Success->value) { 281 | $outBegIdx = 0; 282 | $outNBElement = 0; 283 | 284 | return $ReturnCode; 285 | } 286 | for ($i = 0; $i < $outNbElement2; $i++) { 287 | $outMACDHist[$i] = $outMACD[$i] - $outMACDSignal[$i]; 288 | } 289 | $outBegIdx = $startIdx; 290 | $outNBElement = $outNbElement2; 291 | 292 | return ReturnCode::Success->value; 293 | } 294 | 295 | protected static function TA_INT_EMA(int $startIdx, int $endIdx, $inReal, int $optInTimePeriod, float $optInK_1, int &$outBegIdx, int &$outNBElement, array &$outReal): int 296 | { 297 | //double $tempReal, $prevMA; 298 | //int $i, $today, $outIdx, $lookbackTotal; 299 | $lookbackTotal = Lookback::emaLookback($optInTimePeriod); 300 | if ($startIdx < $lookbackTotal) { 301 | $startIdx = $lookbackTotal; 302 | } 303 | if ($startIdx > $endIdx) { 304 | $outBegIdx = 0; 305 | $outNBElement = 0; 306 | 307 | return ReturnCode::Success->value; 308 | } 309 | $outBegIdx = $startIdx; 310 | if ((static::$compatibility) === Compatibility::Default->value) { 311 | $today = $startIdx - $lookbackTotal; 312 | $i = $optInTimePeriod; 313 | $tempReal = 0.0; 314 | while ($i-- > 0) { 315 | $tempReal += $inReal[$today++]; 316 | } 317 | $prevMA = $tempReal / $optInTimePeriod; 318 | } else { 319 | $prevMA = $inReal[0]; 320 | $today = 1; 321 | } 322 | while ($today <= $startIdx) { 323 | $prevMA = (($inReal[$today++] - $prevMA) * $optInK_1) + $prevMA; 324 | } 325 | $outReal[0] = $prevMA; 326 | $outIdx = 1; 327 | while ($today <= $endIdx) { 328 | $prevMA = (($inReal[$today++] - $prevMA) * $optInK_1) + $prevMA; 329 | $outReal[$outIdx++] = $prevMA; 330 | } 331 | $outNBElement = $outIdx; 332 | 333 | return ReturnCode::Success->value; 334 | } 335 | 336 | protected static function TA_INT_SMA(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 337 | { 338 | //double $periodTotal, $tempReal; 339 | //int $i, $outIdx, $trailingIdx, $lookbackTotal; 340 | $lookbackTotal = ($optInTimePeriod - 1); 341 | if ($startIdx < $lookbackTotal) { 342 | $startIdx = $lookbackTotal; 343 | } 344 | if ($startIdx > $endIdx) { 345 | $outBegIdx = 0; 346 | $outNBElement = 0; 347 | 348 | return ReturnCode::Success->value; 349 | } 350 | $periodTotal = 0; 351 | $trailingIdx = $startIdx - $lookbackTotal; 352 | $i = $trailingIdx; 353 | if ($optInTimePeriod > 1) { 354 | while ($i < $startIdx) { 355 | $periodTotal += $inReal[$i++]; 356 | } 357 | } 358 | $outIdx = 0; 359 | do { 360 | $periodTotal += $inReal[$i++]; 361 | $tempReal = $periodTotal; 362 | $periodTotal -= $inReal[$trailingIdx++]; 363 | $outReal[$outIdx++] = $tempReal / $optInTimePeriod; 364 | } while ($i <= $endIdx); 365 | $outNBElement = $outIdx; 366 | $outBegIdx = $startIdx; 367 | 368 | return ReturnCode::Success->value; 369 | } 370 | 371 | protected static function TA_INT_stddev_using_precalc_ma(array $inReal, array $inMovAvg, int $inMovAvgBegIdx, int $inMovAvgNbElement, int $timePeriod, array &$output): int 372 | { 373 | //double $tempReal, $periodTotal2, $meanValue2; 374 | //int $outIdx; 375 | //int $startSum, $endSum; 376 | $startSum = 1 + $inMovAvgBegIdx - $timePeriod; 377 | $endSum = $inMovAvgBegIdx; 378 | $periodTotal2 = 0; 379 | for ($outIdx = $startSum; $outIdx < $endSum; $outIdx++) { 380 | $tempReal = $inReal[$outIdx]; 381 | $tempReal *= $tempReal; 382 | $periodTotal2 += $tempReal; 383 | } 384 | for ($outIdx = 0; $outIdx < $inMovAvgNbElement; $outIdx++, $startSum++, $endSum++) { 385 | $tempReal = $inReal[$endSum]; 386 | $tempReal *= $tempReal; 387 | $periodTotal2 += $tempReal; 388 | $meanValue2 = $periodTotal2 / $timePeriod; 389 | $tempReal = $inReal[$startSum]; 390 | $tempReal *= $tempReal; 391 | $periodTotal2 -= $tempReal; 392 | $tempReal = $inMovAvg[$outIdx]; 393 | $tempReal *= $tempReal; 394 | $meanValue2 -= $tempReal; 395 | if (!($meanValue2 < 0.00000001)) { 396 | $output[$outIdx] = sqrt($meanValue2); 397 | } else { 398 | $output[$outIdx] = 0.0; 399 | } 400 | } 401 | 402 | return ReturnCode::Success->value; 403 | } 404 | 405 | protected static function TA_INT_VAR(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 406 | { 407 | //double $tempReal, $periodTotal1, $periodTotal2, $meanValue1, $meanValue2; 408 | //int $i, $outIdx, $trailingIdx, $nbInitialElementNeeded; 409 | $nbInitialElementNeeded = ($optInTimePeriod - 1); 410 | if ($startIdx < $nbInitialElementNeeded) { 411 | $startIdx = $nbInitialElementNeeded; 412 | } 413 | if ($startIdx > $endIdx) { 414 | $outBegIdx = 0; 415 | $outNBElement = 0; 416 | 417 | return ReturnCode::Success->value; 418 | } 419 | $periodTotal1 = 0; 420 | $periodTotal2 = 0; 421 | $trailingIdx = $startIdx - $nbInitialElementNeeded; 422 | $i = $trailingIdx; 423 | if ($optInTimePeriod > 1) { 424 | while ($i < $startIdx) { 425 | $tempReal = $inReal[$i++]; 426 | $periodTotal1 += $tempReal; 427 | $tempReal *= $tempReal; 428 | $periodTotal2 += $tempReal; 429 | } 430 | } 431 | $outIdx = 0; 432 | do { 433 | $tempReal = $inReal[$i++]; 434 | $periodTotal1 += $tempReal; 435 | $tempReal *= $tempReal; 436 | $periodTotal2 += $tempReal; 437 | $meanValue1 = $periodTotal1 / $optInTimePeriod; 438 | $meanValue2 = $periodTotal2 / $optInTimePeriod; 439 | $tempReal = $inReal[$trailingIdx++]; 440 | $periodTotal1 -= $tempReal; 441 | $tempReal *= $tempReal; 442 | $periodTotal2 -= $tempReal; 443 | $outReal[$outIdx++] = $meanValue2 - $meanValue1 * $meanValue1; 444 | } while ($i <= $endIdx); 445 | $outNBElement = $outIdx; 446 | $outBegIdx = $startIdx; 447 | 448 | return ReturnCode::Success->value; 449 | } 450 | } 451 | -------------------------------------------------------------------------------- /source/TALib/Core/MathOperators.php: -------------------------------------------------------------------------------- 1 | value; 64 | } 65 | 66 | public static function div(int $startIdx, int $endIdx, array $inReal0, array $inReal1, int &$outBegIdx, int &$outNBElement, array &$outReal): int 67 | { 68 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 69 | return $RetCode; 70 | } 71 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 72 | $outReal[$outIdx] = $inReal0[$i] / $inReal1[$i]; 73 | } 74 | $outNBElement = $outIdx; 75 | $outBegIdx = $startIdx; 76 | 77 | return ReturnCode::Success->value; 78 | } 79 | 80 | public static function max(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 81 | { 82 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 83 | return $RetCode; 84 | } 85 | if ($optInTimePeriod === (PHP_INT_MIN)) { 86 | $optInTimePeriod = 30; 87 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 88 | return ReturnCode::BadParam->value; 89 | } 90 | $nbInitialElementNeeded = ($optInTimePeriod - 1); 91 | if ($startIdx < $nbInitialElementNeeded) { 92 | $startIdx = $nbInitialElementNeeded; 93 | } 94 | if ($startIdx > $endIdx) { 95 | $outBegIdx = 0; 96 | $outNBElement = 0; 97 | 98 | return ReturnCode::Success->value; 99 | } 100 | $outIdx = 0; 101 | $today = $startIdx; 102 | $trailingIdx = $startIdx - $nbInitialElementNeeded; 103 | $highestIdx = -1; 104 | $highest = 0.0; 105 | while ($today <= $endIdx) { 106 | $tmp = $inReal[$today]; 107 | if ($highestIdx < $trailingIdx) { 108 | $highestIdx = $trailingIdx; 109 | $highest = $inReal[$highestIdx]; 110 | $i = $highestIdx; 111 | while (++$i <= $today) { 112 | $tmp = $inReal[$i]; 113 | if ($tmp > $highest) { 114 | $highestIdx = $i; 115 | $highest = $tmp; 116 | } 117 | } 118 | } elseif ($tmp >= $highest) { 119 | $highestIdx = $today; 120 | $highest = $tmp; 121 | } 122 | $outReal[$outIdx++] = $highest; 123 | $trailingIdx++; 124 | $today++; 125 | } 126 | $outBegIdx = $startIdx; 127 | $outNBElement = $outIdx; 128 | 129 | return ReturnCode::Success->value; 130 | } 131 | 132 | public static function maxIndex(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outInteger): int 133 | { 134 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 135 | return $RetCode; 136 | } 137 | if ($optInTimePeriod === (PHP_INT_MIN)) { 138 | $optInTimePeriod = 30; 139 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 140 | return ReturnCode::BadParam->value; 141 | } 142 | $nbInitialElementNeeded = ($optInTimePeriod - 1); 143 | if ($startIdx < $nbInitialElementNeeded) { 144 | $startIdx = $nbInitialElementNeeded; 145 | } 146 | if ($startIdx > $endIdx) { 147 | $outBegIdx = 0; 148 | $outNBElement = 0; 149 | 150 | return ReturnCode::Success->value; 151 | } 152 | $outIdx = 0; 153 | $today = $startIdx; 154 | $trailingIdx = $startIdx - $nbInitialElementNeeded; 155 | $highestIdx = -1; 156 | $highest = 0.0; 157 | while ($today <= $endIdx) { 158 | $tmp = $inReal[$today]; 159 | if ($highestIdx < $trailingIdx) { 160 | $highestIdx = $trailingIdx; 161 | $highest = $inReal[$highestIdx]; 162 | $i = $highestIdx; 163 | while (++$i <= $today) { 164 | $tmp = $inReal[$i]; 165 | if ($tmp > $highest) { 166 | $highestIdx = $i; 167 | $highest = $tmp; 168 | } 169 | } 170 | } elseif ($tmp >= $highest) { 171 | $highestIdx = $today; 172 | $highest = $tmp; 173 | } 174 | $outInteger[$outIdx++] = $highestIdx; 175 | $trailingIdx++; 176 | $today++; 177 | } 178 | $outBegIdx = $startIdx; 179 | $outNBElement = $outIdx; 180 | 181 | return ReturnCode::Success->value; 182 | } 183 | 184 | public static function min(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 185 | { 186 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 187 | return $RetCode; 188 | } 189 | if ($optInTimePeriod === (PHP_INT_MIN)) { 190 | $optInTimePeriod = 30; 191 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 192 | return ReturnCode::BadParam->value; 193 | } 194 | $nbInitialElementNeeded = ($optInTimePeriod - 1); 195 | if ($startIdx < $nbInitialElementNeeded) { 196 | $startIdx = $nbInitialElementNeeded; 197 | } 198 | if ($startIdx > $endIdx) { 199 | $outBegIdx = 0; 200 | $outNBElement = 0; 201 | 202 | return ReturnCode::Success->value; 203 | } 204 | $outIdx = 0; 205 | $today = $startIdx; 206 | $trailingIdx = $startIdx - $nbInitialElementNeeded; 207 | $lowestIdx = -1; 208 | $lowest = 0.0; 209 | while ($today <= $endIdx) { 210 | $tmp = $inReal[$today]; 211 | if ($lowestIdx < $trailingIdx) { 212 | $lowestIdx = $trailingIdx; 213 | $lowest = $inReal[$lowestIdx]; 214 | $i = $lowestIdx; 215 | while (++$i <= $today) { 216 | $tmp = $inReal[$i]; 217 | if ($tmp < $lowest) { 218 | $lowestIdx = $i; 219 | $lowest = $tmp; 220 | } 221 | } 222 | } elseif ($tmp <= $lowest) { 223 | $lowestIdx = $today; 224 | $lowest = $tmp; 225 | } 226 | $outReal[$outIdx++] = $lowest; 227 | $trailingIdx++; 228 | $today++; 229 | } 230 | $outBegIdx = $startIdx; 231 | $outNBElement = $outIdx; 232 | 233 | return ReturnCode::Success->value; 234 | } 235 | 236 | public static function minIndex(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outInteger): int 237 | { 238 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 239 | return $RetCode; 240 | } 241 | if ($optInTimePeriod === (PHP_INT_MIN)) { 242 | $optInTimePeriod = 30; 243 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 244 | return ReturnCode::BadParam->value; 245 | } 246 | $nbInitialElementNeeded = ($optInTimePeriod - 1); 247 | if ($startIdx < $nbInitialElementNeeded) { 248 | $startIdx = $nbInitialElementNeeded; 249 | } 250 | if ($startIdx > $endIdx) { 251 | $outBegIdx = 0; 252 | $outNBElement = 0; 253 | 254 | return ReturnCode::Success->value; 255 | } 256 | $outIdx = 0; 257 | $today = $startIdx; 258 | $trailingIdx = $startIdx - $nbInitialElementNeeded; 259 | $lowestIdx = -1; 260 | $lowest = 0.0; 261 | while ($today <= $endIdx) { 262 | $tmp = $inReal[$today]; 263 | if ($lowestIdx < $trailingIdx) { 264 | $lowestIdx = $trailingIdx; 265 | $lowest = $inReal[$lowestIdx]; 266 | $i = $lowestIdx; 267 | while (++$i <= $today) { 268 | $tmp = $inReal[$i]; 269 | if ($tmp < $lowest) { 270 | $lowestIdx = $i; 271 | $lowest = $tmp; 272 | } 273 | } 274 | } elseif ($tmp <= $lowest) { 275 | $lowestIdx = $today; 276 | $lowest = $tmp; 277 | } 278 | $outInteger[$outIdx++] = $lowestIdx; 279 | $trailingIdx++; 280 | $today++; 281 | } 282 | $outBegIdx = $startIdx; 283 | $outNBElement = $outIdx; 284 | 285 | return ReturnCode::Success->value; 286 | } 287 | 288 | public static function minMax(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outMin, array &$outMax): int 289 | { 290 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 291 | return $RetCode; 292 | } 293 | if ($optInTimePeriod === (PHP_INT_MIN)) { 294 | $optInTimePeriod = 30; 295 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 296 | return ReturnCode::BadParam->value; 297 | } 298 | $nbInitialElementNeeded = ($optInTimePeriod - 1); 299 | if ($startIdx < $nbInitialElementNeeded) { 300 | $startIdx = $nbInitialElementNeeded; 301 | } 302 | if ($startIdx > $endIdx) { 303 | $outBegIdx = 0; 304 | $outNBElement = 0; 305 | 306 | return ReturnCode::Success->value; 307 | } 308 | $outIdx = 0; 309 | $today = $startIdx; 310 | $trailingIdx = $startIdx - $nbInitialElementNeeded; 311 | $highestIdx = -1; 312 | $highest = 0.0; 313 | $lowestIdx = -1; 314 | $lowest = 0.0; 315 | while ($today <= $endIdx) { 316 | $tmpLow = $tmpHigh = $inReal[$today]; 317 | if ($highestIdx < $trailingIdx) { 318 | $highestIdx = $trailingIdx; 319 | $highest = $inReal[$highestIdx]; 320 | $i = $highestIdx; 321 | while (++$i <= $today) { 322 | $tmpHigh = $inReal[$i]; 323 | if ($tmpHigh > $highest) { 324 | $highestIdx = $i; 325 | $highest = $tmpHigh; 326 | } 327 | } 328 | } elseif ($tmpHigh >= $highest) { 329 | $highestIdx = $today; 330 | $highest = $tmpHigh; 331 | } 332 | if ($lowestIdx < $trailingIdx) { 333 | $lowestIdx = $trailingIdx; 334 | $lowest = $inReal[$lowestIdx]; 335 | $i = $lowestIdx; 336 | while (++$i <= $today) { 337 | $tmpLow = $inReal[$i]; 338 | if ($tmpLow < $lowest) { 339 | $lowestIdx = $i; 340 | $lowest = $tmpLow; 341 | } 342 | } 343 | } elseif ($tmpLow <= $lowest) { 344 | $lowestIdx = $today; 345 | $lowest = $tmpLow; 346 | } 347 | $outMax[$outIdx] = $highest; 348 | $outMin[$outIdx] = $lowest; 349 | $outIdx++; 350 | $trailingIdx++; 351 | $today++; 352 | } 353 | $outBegIdx = $startIdx; 354 | $outNBElement = $outIdx; 355 | 356 | return ReturnCode::Success->value; 357 | } 358 | 359 | public static function minMaxIndex(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outMinIdx, array &$outMaxIdx): int 360 | { 361 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 362 | return $RetCode; 363 | } 364 | if ($optInTimePeriod === (PHP_INT_MIN)) { 365 | $optInTimePeriod = 30; 366 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 367 | return ReturnCode::BadParam->value; 368 | } 369 | $nbInitialElementNeeded = ($optInTimePeriod - 1); 370 | if ($startIdx < $nbInitialElementNeeded) { 371 | $startIdx = $nbInitialElementNeeded; 372 | } 373 | if ($startIdx > $endIdx) { 374 | $outBegIdx = 0; 375 | $outNBElement = 0; 376 | 377 | return ReturnCode::Success->value; 378 | } 379 | $outIdx = 0; 380 | $today = $startIdx; 381 | $trailingIdx = $startIdx - $nbInitialElementNeeded; 382 | $highestIdx = -1; 383 | $highest = 0.0; 384 | $lowestIdx = -1; 385 | $lowest = 0.0; 386 | while ($today <= $endIdx) { 387 | $tmpLow = $tmpHigh = $inReal[$today]; 388 | if ($highestIdx < $trailingIdx) { 389 | $highestIdx = $trailingIdx; 390 | $highest = $inReal[$highestIdx]; 391 | $i = $highestIdx; 392 | while (++$i <= $today) { 393 | $tmpHigh = $inReal[$i]; 394 | if ($tmpHigh > $highest) { 395 | $highestIdx = $i; 396 | $highest = $tmpHigh; 397 | } 398 | } 399 | } elseif ($tmpHigh >= $highest) { 400 | $highestIdx = $today; 401 | $highest = $tmpHigh; 402 | } 403 | if ($lowestIdx < $trailingIdx) { 404 | $lowestIdx = $trailingIdx; 405 | $lowest = $inReal[$lowestIdx]; 406 | $i = $lowestIdx; 407 | while (++$i <= $today) { 408 | $tmpLow = $inReal[$i]; 409 | if ($tmpLow < $lowest) { 410 | $lowestIdx = $i; 411 | $lowest = $tmpLow; 412 | } 413 | } 414 | } elseif ($tmpLow <= $lowest) { 415 | $lowestIdx = $today; 416 | $lowest = $tmpLow; 417 | } 418 | $outMaxIdx[$outIdx] = $highestIdx; 419 | $outMinIdx[$outIdx] = $lowestIdx; 420 | $outIdx++; 421 | $trailingIdx++; 422 | $today++; 423 | } 424 | $outBegIdx = $startIdx; 425 | $outNBElement = $outIdx; 426 | 427 | return ReturnCode::Success->value; 428 | } 429 | 430 | public static function mult(int $startIdx, int $endIdx, array $inReal0, array $inReal1, int &$outBegIdx, int &$outNBElement, array &$outReal): int 431 | { 432 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 433 | return $RetCode; 434 | } 435 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 436 | $outReal[$outIdx] = $inReal0[$i] * $inReal1[$i]; 437 | } 438 | $outNBElement = $outIdx; 439 | $outBegIdx = $startIdx; 440 | 441 | return ReturnCode::Success->value; 442 | } 443 | 444 | public static function sub(int $startIdx, int $endIdx, array $inReal0, array $inReal1, int &$outBegIdx, int &$outNBElement, array &$outReal): int 445 | { 446 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 447 | return $RetCode; 448 | } 449 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 450 | $outReal[$outIdx] = $inReal0[$i] - $inReal1[$i]; 451 | } 452 | $outNBElement = $outIdx; 453 | $outBegIdx = $startIdx; 454 | 455 | return ReturnCode::Success->value; 456 | } 457 | 458 | public static function sum(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 459 | { 460 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 461 | return $RetCode; 462 | } 463 | if ($optInTimePeriod === (PHP_INT_MIN)) { 464 | $optInTimePeriod = 30; 465 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 466 | return ReturnCode::BadParam->value; 467 | } 468 | $lookbackTotal = ($optInTimePeriod - 1); 469 | if ($startIdx < $lookbackTotal) { 470 | $startIdx = $lookbackTotal; 471 | } 472 | if ($startIdx > $endIdx) { 473 | $outBegIdx = 0; 474 | $outNBElement = 0; 475 | 476 | return ReturnCode::Success->value; 477 | } 478 | $periodTotal = 0; 479 | $trailingIdx = $startIdx - $lookbackTotal; 480 | $i = $trailingIdx; 481 | if ($optInTimePeriod > 1) { 482 | while ($i < $startIdx) { 483 | $periodTotal += $inReal[$i++]; 484 | } 485 | } 486 | $outIdx = 0; 487 | do { 488 | $periodTotal += $inReal[$i++]; 489 | $tempReal = $periodTotal; 490 | $periodTotal -= $inReal[$trailingIdx++]; 491 | $outReal[$outIdx++] = $tempReal; 492 | } while ($i <= $endIdx); 493 | $outNBElement = $outIdx; 494 | $outBegIdx = $startIdx; 495 | 496 | return ReturnCode::Success->value; 497 | } 498 | 499 | } 500 | -------------------------------------------------------------------------------- /source/TALib/Core/MathTransform.php: -------------------------------------------------------------------------------- 1 | value; 74 | } 75 | 76 | /** 77 | * @param int $startIdx 78 | * @param int $endIdx 79 | * @param float[] $inReal 80 | * @param int $outBegIdx 81 | * @param int $outNBElement 82 | * @param float[] $outReal 83 | * 84 | * @return int 85 | */ 86 | public static function asin(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 87 | { 88 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 89 | return $RetCode; 90 | } 91 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 92 | $outReal[$outIdx] = asin($inReal[$i]); 93 | } 94 | $outNBElement = $outIdx; 95 | $outBegIdx = $startIdx; 96 | 97 | return ReturnCode::Success->value; 98 | } 99 | 100 | /** 101 | * @param int $startIdx 102 | * @param int $endIdx 103 | * @param float[] $inReal 104 | * @param int $outBegIdx 105 | * @param int $outNBElement 106 | * @param float[] $outReal 107 | * 108 | * @return int 109 | */ 110 | public static function atan(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 111 | { 112 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 113 | return $RetCode; 114 | } 115 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 116 | $outReal[$outIdx] = atan($inReal[$i]); 117 | } 118 | $outNBElement = $outIdx; 119 | $outBegIdx = $startIdx; 120 | 121 | return ReturnCode::Success->value; 122 | } 123 | 124 | /** 125 | * @param int $startIdx 126 | * @param int $endIdx 127 | * @param array $inReal 128 | * @param int $outBegIdx 129 | * @param int $outNBElement 130 | * @param array $outReal 131 | * 132 | * @return int 133 | */ 134 | public static function ceil(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 135 | { 136 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 137 | return $RetCode; 138 | } 139 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 140 | $outReal[$outIdx] = ceil($inReal[$i]); 141 | } 142 | $outNBElement = $outIdx; 143 | $outBegIdx = $startIdx; 144 | 145 | return ReturnCode::Success->value; 146 | } 147 | 148 | /** 149 | * @param int $startIdx 150 | * @param int $endIdx 151 | * @param array $inReal 152 | * @param int $outBegIdx 153 | * @param int $outNBElement 154 | * @param array $outReal 155 | * 156 | * @return int 157 | */ 158 | public static function cos(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 159 | { 160 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 161 | return $RetCode; 162 | } 163 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 164 | $outReal[$outIdx] = cos($inReal[$i]); 165 | } 166 | $outNBElement = $outIdx; 167 | $outBegIdx = $startIdx; 168 | 169 | return ReturnCode::Success->value; 170 | } 171 | 172 | /** 173 | * @param int $startIdx 174 | * @param int $endIdx 175 | * @param array $inReal 176 | * @param int $outBegIdx 177 | * @param int $outNBElement 178 | * @param array $outReal 179 | * 180 | * @return int 181 | */ 182 | public static function cosh(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 183 | { 184 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 185 | return $RetCode; 186 | } 187 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 188 | $outReal[$outIdx] = cosh($inReal[$i]); 189 | } 190 | $outNBElement = $outIdx; 191 | $outBegIdx = $startIdx; 192 | 193 | return ReturnCode::Success->value; 194 | } 195 | 196 | /** 197 | * @param int $startIdx 198 | * @param int $endIdx 199 | * @param array $inReal 200 | * @param int $outBegIdx 201 | * @param int $outNBElement 202 | * @param array $outReal 203 | * 204 | * @return int 205 | */ 206 | public static function exp(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 207 | { 208 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 209 | return $RetCode; 210 | } 211 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 212 | $outReal[$outIdx] = exp($inReal[$i]); 213 | } 214 | $outNBElement = $outIdx; 215 | $outBegIdx = $startIdx; 216 | 217 | return ReturnCode::Success->value; 218 | } 219 | 220 | /** 221 | * @param int $startIdx 222 | * @param int $endIdx 223 | * @param array $inReal 224 | * @param int $outBegIdx 225 | * @param int $outNBElement 226 | * @param array $outReal 227 | * 228 | * @return int 229 | */ 230 | public static function floor(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 231 | { 232 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 233 | return $RetCode; 234 | } 235 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 236 | $outReal[$outIdx] = floor($inReal[$i]); 237 | } 238 | $outNBElement = $outIdx; 239 | $outBegIdx = $startIdx; 240 | 241 | return ReturnCode::Success->value; 242 | } 243 | 244 | /** 245 | * @param int $startIdx 246 | * @param int $endIdx 247 | * @param array $inReal 248 | * @param int $outBegIdx 249 | * @param int $outNBElement 250 | * @param array $outReal 251 | * 252 | * @return int 253 | */ 254 | public static function ln(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 255 | { 256 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 257 | return $RetCode; 258 | } 259 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 260 | $outReal[$outIdx] = log($inReal[$i]); 261 | } 262 | $outNBElement = $outIdx; 263 | $outBegIdx = $startIdx; 264 | 265 | return ReturnCode::Success->value; 266 | } 267 | 268 | /** 269 | * @param int $startIdx 270 | * @param int $endIdx 271 | * @param array $inReal 272 | * @param int $outBegIdx 273 | * @param int $outNBElement 274 | * @param array $outReal 275 | * 276 | * @return int 277 | */ 278 | public static function log10(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 279 | { 280 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 281 | return $RetCode; 282 | } 283 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 284 | $outReal[$outIdx] = log10($inReal[$i]); 285 | } 286 | $outNBElement = $outIdx; 287 | $outBegIdx = $startIdx; 288 | 289 | return ReturnCode::Success->value; 290 | } 291 | 292 | /** 293 | * @param int $startIdx 294 | * @param int $endIdx 295 | * @param array $inReal 296 | * @param int $outBegIdx 297 | * @param int $outNBElement 298 | * @param array $outReal 299 | * 300 | * @return int 301 | */ 302 | public static function sin(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 303 | { 304 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 305 | return $RetCode; 306 | } 307 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 308 | $outReal[$outIdx] = sin($inReal[$i]); 309 | } 310 | $outNBElement = $outIdx; 311 | $outBegIdx = $startIdx; 312 | 313 | return ReturnCode::Success->value; 314 | } 315 | 316 | /** 317 | * @param int $startIdx 318 | * @param int $endIdx 319 | * @param array $inReal 320 | * @param int $outBegIdx 321 | * @param int $outNBElement 322 | * @param array $outReal 323 | * 324 | * @return int 325 | */ 326 | public static function sinh(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 327 | { 328 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 329 | return $RetCode; 330 | } 331 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 332 | $outReal[$outIdx] = sinh($inReal[$i]); 333 | } 334 | $outNBElement = $outIdx; 335 | $outBegIdx = $startIdx; 336 | 337 | return ReturnCode::Success->value; 338 | } 339 | 340 | /** 341 | * @param int $startIdx 342 | * @param int $endIdx 343 | * @param array $inReal 344 | * @param int $outBegIdx 345 | * @param int $outNBElement 346 | * @param array $outReal 347 | * 348 | * @return int 349 | */ 350 | public static function sqrt(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 351 | { 352 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 353 | return $RetCode; 354 | } 355 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 356 | $outReal[$outIdx] = sqrt($inReal[$i]); 357 | } 358 | $outNBElement = $outIdx; 359 | $outBegIdx = $startIdx; 360 | 361 | return ReturnCode::Success->value; 362 | } 363 | 364 | /** 365 | * @param int $startIdx 366 | * @param int $endIdx 367 | * @param array $inReal 368 | * @param int $outBegIdx 369 | * @param int $outNBElement 370 | * @param array $outReal 371 | * 372 | * @return int 373 | */ 374 | public static function tan(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 375 | { 376 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 377 | return $RetCode; 378 | } 379 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 380 | $outReal[$outIdx] = tan($inReal[$i]); 381 | } 382 | $outNBElement = $outIdx; 383 | $outBegIdx = $startIdx; 384 | 385 | return ReturnCode::Success->value; 386 | } 387 | 388 | /** 389 | * @param int $startIdx 390 | * @param int $endIdx 391 | * @param array $inReal 392 | * @param int $outBegIdx 393 | * @param int $outNBElement 394 | * @param array $outReal 395 | * 396 | * @return int 397 | */ 398 | public static function tanh(int $startIdx, int $endIdx, array $inReal, int &$outBegIdx, int &$outNBElement, array &$outReal): int 399 | { 400 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 401 | return $RetCode; 402 | } 403 | for ($i = $startIdx, $outIdx = 0; $i <= $endIdx; $i++, $outIdx++) { 404 | $outReal[$outIdx] = tanh($inReal[$i]); 405 | } 406 | $outNBElement = $outIdx; 407 | $outBegIdx = $startIdx; 408 | 409 | return ReturnCode::Success->value; 410 | } 411 | 412 | } 413 | -------------------------------------------------------------------------------- /source/TALib/Core/PriceTransform.php: -------------------------------------------------------------------------------- 1 | value; 65 | } 66 | 67 | public static function medPrice(int $startIdx, int $endIdx, array $inHigh, array $inLow, int &$outBegIdx, int &$outNBElement, array &$outReal): int 68 | { 69 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 70 | return $RetCode; 71 | } 72 | $outIdx = 0; 73 | for ($i = $startIdx; $i <= $endIdx; $i++) { 74 | $outReal[$outIdx++] = ($inHigh[$i] + $inLow[$i]) / 2.0; 75 | } 76 | $outNBElement = $outIdx; 77 | $outBegIdx = $startIdx; 78 | 79 | return ReturnCode::Success->value; 80 | } 81 | 82 | public static function typPrice(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int &$outBegIdx, int &$outNBElement, array &$outReal): int 83 | { 84 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 85 | return $RetCode; 86 | } 87 | $outIdx = 0; 88 | for ($i = $startIdx; $i <= $endIdx; $i++) { 89 | $outReal[$outIdx++] = ($inHigh[$i] + 90 | $inLow[$i] + 91 | $inClose[$i]) / 3.0; 92 | } 93 | $outNBElement = $outIdx; 94 | $outBegIdx = $startIdx; 95 | 96 | return ReturnCode::Success->value; 97 | } 98 | 99 | public static function wclPrice(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int &$outBegIdx, int &$outNBElement, array &$outReal): int 100 | { 101 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 102 | return $RetCode; 103 | } 104 | $outIdx = 0; 105 | for ($i = $startIdx; $i <= $endIdx; $i++) { 106 | $outReal[$outIdx++] = ($inHigh[$i] + 107 | $inLow[$i] + 108 | ($inClose[$i] * 2.0)) / 4.0; 109 | } 110 | $outNBElement = $outIdx; 111 | $outBegIdx = $startIdx; 112 | 113 | return ReturnCode::Success->value; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /source/TALib/Core/StatisticFunctions.php: -------------------------------------------------------------------------------- 1 | $endIdx) { 65 | $outBegIdx = 0; 66 | $outNBElement = 0; 67 | 68 | return ReturnCode::Success->value; 69 | } 70 | 71 | $outBegIdx = $today; 72 | 73 | $outIdx = 0; 74 | 75 | while ($today <= $endIdx) { 76 | $todaySum = 0.0; 77 | for ($i = 0; $i < $optInTimePeriod; $i++) { 78 | $todaySum += $inReal[$today - $i]; 79 | } 80 | 81 | $todayDev = 0.0; 82 | for ($i = 0; $i < $optInTimePeriod; $i++) { 83 | $todayDev += abs($inReal[$today - $i] - $todaySum / $optInTimePeriod); 84 | } 85 | $outReal[$outIdx] = $todayDev / $optInTimePeriod; 86 | 87 | $outIdx++; 88 | $today++; 89 | } 90 | 91 | $outNBElement = $outIdx; 92 | 93 | return ReturnCode::Success->value; 94 | } 95 | 96 | public static function beta(int $startIdx, int $endIdx, array $inReal0, array $inReal1, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 97 | { 98 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 99 | return $RetCode; 100 | } 101 | if ($optInTimePeriod === (PHP_INT_MIN)) { 102 | $optInTimePeriod = 5; 103 | } elseif (($optInTimePeriod < 1) || ($optInTimePeriod > 100000)) { 104 | return ReturnCode::BadParam->value; 105 | } 106 | $nbInitialElementNeeded = $optInTimePeriod; 107 | if ($startIdx < $nbInitialElementNeeded) { 108 | $startIdx = $nbInitialElementNeeded; 109 | } 110 | if ($startIdx > $endIdx) { 111 | $outBegIdx = 0; 112 | $outNBElement = 0; 113 | 114 | return ReturnCode::Success->value; 115 | } 116 | $trailingIdx = $startIdx - $nbInitialElementNeeded; 117 | $last_price_x = $trailing_last_price_x = $inReal0[$trailingIdx]; 118 | $last_price_y = $trailing_last_price_y = $inReal1[$trailingIdx]; 119 | $i = ++$trailingIdx; 120 | $S_xx = $S_xy = $S_x = $S_y = 0; 121 | while ($i < $startIdx) { 122 | $tmp_real = $inReal0[$i]; 123 | if (!(((-0.00000001) < $last_price_x) && ($last_price_x < 0.00000001))) { 124 | $x = ($tmp_real - $last_price_x) / $last_price_x; 125 | } else { 126 | $x = 0.0; 127 | } 128 | $last_price_x = $tmp_real; 129 | $tmp_real = $inReal1[$i++]; 130 | if (!(((-0.00000001) < $last_price_y) && ($last_price_y < 0.00000001))) { 131 | $y = ($tmp_real - $last_price_y) / $last_price_y; 132 | } else { 133 | $y = 0.0; 134 | } 135 | $last_price_y = $tmp_real; 136 | $S_xx += $x * $x; 137 | $S_xy += $x * $y; 138 | $S_x += $x; 139 | $S_y += $y; 140 | } 141 | $outIdx = 0; 142 | $n = (double)$optInTimePeriod; 143 | do { 144 | $tmp_real = $inReal0[$i]; 145 | if (!(((-0.00000001) < $last_price_x) && ($last_price_x < 0.00000001))) { 146 | $x = ($tmp_real - $last_price_x) / $last_price_x; 147 | } else { 148 | $x = 0.0; 149 | } 150 | $last_price_x = $tmp_real; 151 | $tmp_real = $inReal1[$i++]; 152 | if (!(((-0.00000001) < $last_price_y) && ($last_price_y < 0.00000001))) { 153 | $y = ($tmp_real - $last_price_y) / $last_price_y; 154 | } else { 155 | $y = 0.0; 156 | } 157 | $last_price_y = $tmp_real; 158 | $S_xx += $x * $x; 159 | $S_xy += $x * $y; 160 | $S_x += $x; 161 | $S_y += $y; 162 | $tmp_real = $inReal0[$trailingIdx]; 163 | if (!(((-0.00000001) < $trailing_last_price_x) && ($trailing_last_price_x < 0.00000001))) { 164 | $x = ($tmp_real - $trailing_last_price_x) / $trailing_last_price_x; 165 | } else { 166 | $x = 0.0; 167 | } 168 | $trailing_last_price_x = $tmp_real; 169 | $tmp_real = $inReal1[$trailingIdx++]; 170 | if (!(((-0.00000001) < $trailing_last_price_y) && ($trailing_last_price_y < 0.00000001))) { 171 | $y = ($tmp_real - $trailing_last_price_y) / $trailing_last_price_y; 172 | } else { 173 | $y = 0.0; 174 | } 175 | $trailing_last_price_y = $tmp_real; 176 | $tmp_real = ($n * $S_xx) - ($S_x * $S_x); 177 | if (!(((-0.00000001) < $tmp_real) && ($tmp_real < 0.00000001))) { 178 | $outReal[$outIdx++] = (($n * $S_xy) - ($S_x * $S_y)) / $tmp_real; 179 | } else { 180 | $outReal[$outIdx++] = 0.0; 181 | } 182 | $S_xx -= $x * $x; 183 | $S_xy -= $x * $y; 184 | $S_x -= $x; 185 | $S_y -= $y; 186 | } while ($i <= $endIdx); 187 | $outNBElement = $outIdx; 188 | $outBegIdx = $startIdx; 189 | 190 | return ReturnCode::Success->value; 191 | } 192 | 193 | public static function correl(int $startIdx, int $endIdx, array $inReal0, array $inReal1, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 194 | { 195 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 196 | return $RetCode; 197 | } 198 | if ($optInTimePeriod === (PHP_INT_MIN)) { 199 | $optInTimePeriod = 30; 200 | } elseif (($optInTimePeriod < 1) || ($optInTimePeriod > 100000)) { 201 | return ReturnCode::BadParam->value; 202 | } 203 | $lookbackTotal = $optInTimePeriod - 1; 204 | if ($startIdx < $lookbackTotal) { 205 | $startIdx = $lookbackTotal; 206 | } 207 | if ($startIdx > $endIdx) { 208 | $outBegIdx = 0; 209 | $outNBElement = 0; 210 | 211 | return ReturnCode::Success->value; 212 | } 213 | $outBegIdx = $startIdx; 214 | $trailingIdx = $startIdx - $lookbackTotal; 215 | $sumXY = $sumX = $sumY = $sumX2 = $sumY2 = 0.0; 216 | for ($today = $trailingIdx; $today <= $startIdx; $today++) { 217 | $x = $inReal0[$today]; 218 | $sumX += $x; 219 | $sumX2 += $x * $x; 220 | $y = $inReal1[$today]; 221 | $sumXY += $x * $y; 222 | $sumY += $y; 223 | $sumY2 += $y * $y; 224 | } 225 | $trailingX = $inReal0[$trailingIdx]; 226 | $trailingY = $inReal1[$trailingIdx++]; 227 | $tempReal = ($sumX2 - (($sumX * $sumX) / $optInTimePeriod)) * ($sumY2 - (($sumY * $sumY) / $optInTimePeriod)); 228 | if (!($tempReal < 0.00000001)) { 229 | $outReal[0] = ($sumXY - (($sumX * $sumY) / $optInTimePeriod)) / sqrt($tempReal); 230 | } else { 231 | $outReal[0] = 0.0; 232 | } 233 | $outIdx = 1; 234 | while ($today <= $endIdx) { 235 | $sumX -= $trailingX; 236 | $sumX2 -= $trailingX * $trailingX; 237 | $sumXY -= $trailingX * $trailingY; 238 | $sumY -= $trailingY; 239 | $sumY2 -= $trailingY * $trailingY; 240 | $x = $inReal0[$today]; 241 | $sumX += $x; 242 | $sumX2 += $x * $x; 243 | $y = $inReal1[$today++]; 244 | $sumXY += $x * $y; 245 | $sumY += $y; 246 | $sumY2 += $y * $y; 247 | $trailingX = $inReal0[$trailingIdx]; 248 | $trailingY = $inReal1[$trailingIdx++]; 249 | $tempReal = ($sumX2 - (($sumX * $sumX) / $optInTimePeriod)) * ($sumY2 - (($sumY * $sumY) / $optInTimePeriod)); 250 | if (!($tempReal < 0.00000001)) { 251 | $outReal[$outIdx++] = ($sumXY - (($sumX * $sumY) / $optInTimePeriod)) / sqrt($tempReal); 252 | } else { 253 | $outReal[$outIdx++] = 0.0; 254 | } 255 | } 256 | $outNBElement = $outIdx; 257 | 258 | return ReturnCode::Success->value; 259 | } 260 | 261 | public static function linearReg(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 262 | { 263 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 264 | return $RetCode; 265 | } 266 | if ($optInTimePeriod === (PHP_INT_MIN)) { 267 | $optInTimePeriod = 14; 268 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 269 | return ReturnCode::BadParam->value; 270 | } 271 | $lookbackTotal = Lookback::linearRegLookback($optInTimePeriod); 272 | if ($startIdx < $lookbackTotal) { 273 | $startIdx = $lookbackTotal; 274 | } 275 | if ($startIdx > $endIdx) { 276 | $outBegIdx = 0; 277 | $outNBElement = 0; 278 | 279 | return ReturnCode::Success->value; 280 | } 281 | $outIdx = 0; 282 | $today = $startIdx; 283 | $SumX = $optInTimePeriod * ($optInTimePeriod - 1) * 0.5; 284 | $SumXSqr = $optInTimePeriod * ($optInTimePeriod - 1) * (2 * $optInTimePeriod - 1) / 6; 285 | $Divisor = $SumX * $SumX - $optInTimePeriod * $SumXSqr; 286 | while ($today <= $endIdx) { 287 | $SumXY = 0; 288 | $SumY = 0; 289 | for ($i = $optInTimePeriod; $i-- != 0;) { 290 | $SumY += $tempValue1 = $inReal[$today - $i]; 291 | $SumXY += (double)$i * $tempValue1; 292 | } 293 | $m = ($optInTimePeriod * $SumXY - $SumX * $SumY) / $Divisor; 294 | $b = ($SumY - $m * $SumX) / (double)$optInTimePeriod; 295 | $outReal[$outIdx++] = $b + $m * (double)($optInTimePeriod - 1); 296 | $today++; 297 | } 298 | $outBegIdx = $startIdx; 299 | $outNBElement = $outIdx; 300 | 301 | return ReturnCode::Success->value; 302 | } 303 | 304 | public static function linearRegAngle(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 305 | { 306 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 307 | return $RetCode; 308 | } 309 | if ($optInTimePeriod === (PHP_INT_MIN)) { 310 | $optInTimePeriod = 14; 311 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 312 | return ReturnCode::BadParam->value; 313 | } 314 | $lookbackTotal = Lookback::linearRegAngleLookback($optInTimePeriod); 315 | if ($startIdx < $lookbackTotal) { 316 | $startIdx = $lookbackTotal; 317 | } 318 | if ($startIdx > $endIdx) { 319 | $outBegIdx = 0; 320 | $outNBElement = 0; 321 | 322 | return ReturnCode::Success->value; 323 | } 324 | $outIdx = 0; 325 | $today = $startIdx; 326 | $SumX = $optInTimePeriod * ($optInTimePeriod - 1) * 0.5; 327 | $SumXSqr = $optInTimePeriod * ($optInTimePeriod - 1) * (2 * $optInTimePeriod - 1) / 6; 328 | $Divisor = $SumX * $SumX - $optInTimePeriod * $SumXSqr; 329 | while ($today <= $endIdx) { 330 | $SumXY = 0; 331 | $SumY = 0; 332 | for ($i = $optInTimePeriod; $i-- != 0;) { 333 | $SumY += $tempValue1 = $inReal[$today - $i]; 334 | $SumXY += (double)$i * $tempValue1; 335 | } 336 | $m = ($optInTimePeriod * $SumXY - $SumX * $SumY) / $Divisor; 337 | $outReal[$outIdx++] = atan($m) * (180.0 / 3.14159265358979323846); 338 | $today++; 339 | } 340 | $outBegIdx = $startIdx; 341 | $outNBElement = $outIdx; 342 | 343 | return ReturnCode::Success->value; 344 | } 345 | 346 | public static function linearRegIntercept(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 347 | { 348 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 349 | return $RetCode; 350 | } 351 | if ($optInTimePeriod === (PHP_INT_MIN)) { 352 | $optInTimePeriod = 14; 353 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 354 | return ReturnCode::BadParam->value; 355 | } 356 | $lookbackTotal = Lookback::linearRegInterceptLookback($optInTimePeriod); 357 | if ($startIdx < $lookbackTotal) { 358 | $startIdx = $lookbackTotal; 359 | } 360 | if ($startIdx > $endIdx) { 361 | $outBegIdx = 0; 362 | $outNBElement = 0; 363 | 364 | return ReturnCode::Success->value; 365 | } 366 | $outIdx = 0; 367 | $today = $startIdx; 368 | $SumX = $optInTimePeriod * ($optInTimePeriod - 1) * 0.5; 369 | $SumXSqr = $optInTimePeriod * ($optInTimePeriod - 1) * (2 * $optInTimePeriod - 1) / 6; 370 | $Divisor = $SumX * $SumX - $optInTimePeriod * $SumXSqr; 371 | while ($today <= $endIdx) { 372 | $SumXY = 0; 373 | $SumY = 0; 374 | for ($i = $optInTimePeriod; $i-- != 0;) { 375 | $SumY += $tempValue1 = $inReal[$today - $i]; 376 | $SumXY += (double)$i * $tempValue1; 377 | } 378 | $m = ($optInTimePeriod * $SumXY - $SumX * $SumY) / $Divisor; 379 | $outReal[$outIdx++] = ($SumY - $m * $SumX) / (double)$optInTimePeriod; 380 | $today++; 381 | } 382 | $outBegIdx = $startIdx; 383 | $outNBElement = $outIdx; 384 | 385 | return ReturnCode::Success->value; 386 | } 387 | 388 | public static function linearRegSlope(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 389 | { 390 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 391 | return $RetCode; 392 | } 393 | if ($optInTimePeriod === (PHP_INT_MIN)) { 394 | $optInTimePeriod = 14; 395 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 396 | return ReturnCode::BadParam->value; 397 | } 398 | $lookbackTotal = Lookback::linearRegSlopeLookback($optInTimePeriod); 399 | if ($startIdx < $lookbackTotal) { 400 | $startIdx = $lookbackTotal; 401 | } 402 | if ($startIdx > $endIdx) { 403 | $outBegIdx = 0; 404 | $outNBElement = 0; 405 | 406 | return ReturnCode::Success->value; 407 | } 408 | $outIdx = 0; 409 | $today = $startIdx; 410 | $SumX = $optInTimePeriod * ($optInTimePeriod - 1) * 0.5; 411 | $SumXSqr = $optInTimePeriod * ($optInTimePeriod - 1) * (2 * $optInTimePeriod - 1) / 6; 412 | $Divisor = $SumX * $SumX - $optInTimePeriod * $SumXSqr; 413 | while ($today <= $endIdx) { 414 | $SumXY = 0; 415 | $SumY = 0; 416 | for ($i = $optInTimePeriod; $i-- != 0;) { 417 | $SumY += $tempValue1 = $inReal[$today - $i]; 418 | $SumXY += (double)$i * $tempValue1; 419 | } 420 | $outReal[$outIdx++] = ($optInTimePeriod * $SumXY - $SumX * $SumY) / $Divisor; 421 | $today++; 422 | } 423 | $outBegIdx = $startIdx; 424 | $outNBElement = $outIdx; 425 | 426 | return ReturnCode::Success->value; 427 | } 428 | 429 | public static function stdDev(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, float $optInNbDev, int &$outBegIdx, int &$outNBElement, array &$outReal): int 430 | { 431 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 432 | return $RetCode; 433 | } 434 | if ($optInTimePeriod === (PHP_INT_MIN)) { 435 | $optInTimePeriod = 5; 436 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 437 | return ReturnCode::BadParam->value; 438 | } 439 | if ($optInNbDev === (-4e+37)) { 440 | $optInNbDev = 1.000000e+0; 441 | } elseif (($optInNbDev < -3.000000e+37) || ($optInNbDev > 3.000000e+37)) { 442 | return ReturnCode::BadParam->value; 443 | } 444 | $retCode = static::TA_INT_VAR( 445 | $startIdx, 446 | $endIdx, 447 | $inReal, 448 | $optInTimePeriod, 449 | $outBegIdx, 450 | $outNBElement, 451 | $outReal 452 | ); 453 | if ($retCode !== ReturnCode::Success->value) { 454 | return $retCode; 455 | } 456 | if ($optInNbDev != 1.0) { 457 | for ($i = 0; $i < $outNBElement; $i++) { 458 | $outReal[$i] = sqrt($outReal[$i]) * $optInNbDev; 459 | } 460 | } else { 461 | for ($i = 0; $i < $outNBElement; $i++) { 462 | $outReal[$i] = sqrt($outReal[$i]); 463 | } 464 | } 465 | 466 | return ReturnCode::Success->value; 467 | } 468 | 469 | public static function tsf(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 470 | { 471 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 472 | return $RetCode; 473 | } 474 | if ($optInTimePeriod === (PHP_INT_MIN)) { 475 | $optInTimePeriod = 14; 476 | } elseif (($optInTimePeriod < 2) || ($optInTimePeriod > 100000)) { 477 | return ReturnCode::BadParam->value; 478 | } 479 | $lookbackTotal = Lookback::tsfLookback($optInTimePeriod); 480 | if ($startIdx < $lookbackTotal) { 481 | $startIdx = $lookbackTotal; 482 | } 483 | if ($startIdx > $endIdx) { 484 | $outBegIdx = 0; 485 | $outNBElement = 0; 486 | 487 | return ReturnCode::Success->value; 488 | } 489 | $outIdx = 0; 490 | $today = $startIdx; 491 | $SumX = $optInTimePeriod * ($optInTimePeriod - 1) * 0.5; 492 | $SumXSqr = $optInTimePeriod * ($optInTimePeriod - 1) * (2 * $optInTimePeriod - 1) / 6; 493 | $Divisor = $SumX * $SumX - $optInTimePeriod * $SumXSqr; 494 | while ($today <= $endIdx) { 495 | $SumXY = 0; 496 | $SumY = 0; 497 | for ($i = $optInTimePeriod; $i-- != 0;) { 498 | $SumY += $tempValue1 = $inReal[$today - $i]; 499 | $SumXY += (double)$i * $tempValue1; 500 | } 501 | $m = ($optInTimePeriod * $SumXY - $SumX * $SumY) / $Divisor; 502 | $b = ($SumY - $m * $SumX) / (double)$optInTimePeriod; 503 | $outReal[$outIdx++] = $b + $m * (double)$optInTimePeriod; 504 | $today++; 505 | } 506 | $outBegIdx = $startIdx; 507 | $outNBElement = $outIdx; 508 | 509 | return ReturnCode::Success->value; 510 | } 511 | 512 | public static function variance(int $startIdx, int $endIdx, array $inReal, int $optInTimePeriod, float $optInNbDev, int &$outBegIdx, int &$outNBElement, array &$outReal): int 513 | { 514 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 515 | return $RetCode; 516 | } 517 | if ($optInTimePeriod === (PHP_INT_MIN)) { 518 | $optInTimePeriod = 5; 519 | } elseif (($optInTimePeriod < 1) || ($optInTimePeriod > 100000)) { 520 | return ReturnCode::BadParam->value; 521 | } 522 | if ($optInNbDev === (-4e+37)) { 523 | $optInNbDev = 1.000000e+0; 524 | } elseif (($optInNbDev < -3.000000e+37) || ($optInNbDev > 3.000000e+37)) { 525 | return ReturnCode::BadParam->value; 526 | } 527 | 528 | return static::TA_INT_VAR( 529 | $startIdx, 530 | $endIdx, 531 | $inReal, 532 | $optInTimePeriod, 533 | $outBegIdx, 534 | $outNBElement, 535 | $outReal 536 | ); 537 | } 538 | } 539 | -------------------------------------------------------------------------------- /source/TALib/Core/VolatilityIndicators.php: -------------------------------------------------------------------------------- 1 | 100000) { 64 | return ReturnCode::BadParam->value; 65 | } 66 | $outBegIdx = 0; 67 | $outNBElement = 0; 68 | $lookbackTotal = Lookback::natrLookback($optInTimePeriod); 69 | if ($startIdx < $lookbackTotal) { 70 | $startIdx = $lookbackTotal; 71 | } 72 | if ($startIdx > $endIdx) { 73 | return ReturnCode::Success->value; 74 | } 75 | if ($optInTimePeriod <= 1) { 76 | return self::trueRange( 77 | $startIdx, 78 | $endIdx, 79 | $inHigh, 80 | $inLow, 81 | $inClose, 82 | $outBegIdx, 83 | $outNBElement, 84 | $outReal 85 | ); 86 | } 87 | $tempBuffer = static::double($lookbackTotal + ($endIdx - $startIdx) + 1); 88 | $retCode = self::trueRange( 89 | $startIdx - $lookbackTotal + 1, 90 | $endIdx, 91 | $inHigh, 92 | $inLow, 93 | $inClose, 94 | $outBegIdx1, 95 | $outNbElement1, 96 | $tempBuffer 97 | ); 98 | if ($retCode !== ReturnCode::Success->value) { 99 | return $retCode; 100 | } 101 | $retCode = static::TA_INT_SMA( 102 | $optInTimePeriod - 1, 103 | $optInTimePeriod - 1, 104 | $tempBuffer, 105 | $optInTimePeriod, 106 | $outBegIdx1, 107 | $outNbElement1, 108 | $prevATRTemp 109 | ); 110 | if ($retCode !== ReturnCode::Success->value) { 111 | return $retCode; 112 | } 113 | $prevATR = $prevATRTemp[0]; 114 | $today = $optInTimePeriod; 115 | $outIdx = static::$unstablePeriod[UnstablePeriodFunctionID::NATR->value]; 116 | while ($outIdx > 0) { 117 | $prevATR *= $optInTimePeriod - 1; 118 | $prevATR += $tempBuffer[$today++]; 119 | $prevATR /= $optInTimePeriod; 120 | $outIdx--; 121 | } 122 | $outIdx = 1; 123 | $tempValue = $inClose[$today]; 124 | if (!(-0.00000001 < $tempValue && $tempValue < 0.00000001)) { 125 | $outReal[0] = $prevATR / $tempValue * 100.0; 126 | } else { 127 | $outReal[0] = 0.0; 128 | } 129 | $nbATR = $endIdx - $startIdx + 1; 130 | while (--$nbATR > 0) { 131 | $prevATR *= $optInTimePeriod - 1; 132 | $prevATR += $tempBuffer[$today++]; 133 | $prevATR /= $optInTimePeriod; 134 | $tempValue = $inClose[$today]; 135 | if (!(-0.00000001 < $tempValue && $tempValue < 0.00000001)) { 136 | $outReal[$outIdx] = $prevATR / $tempValue * 100.0; 137 | } else { 138 | $outReal[0] = 0.0; 139 | } 140 | $outIdx++; 141 | } 142 | $outBegIdx = $startIdx; 143 | $outNBElement = $outIdx; 144 | 145 | return $retCode; 146 | } 147 | 148 | public static function trueRange(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int &$outBegIdx, int &$outNBElement, array &$outReal): int 149 | { 150 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 151 | return $RetCode; 152 | } 153 | if ($startIdx < 1) { 154 | $startIdx = 1; 155 | } 156 | if ($startIdx > $endIdx) { 157 | $outBegIdx = 0; 158 | $outNBElement = 0; 159 | 160 | return ReturnCode::Success->value; 161 | } 162 | $outIdx = 0; 163 | $today = $startIdx; 164 | while ($today <= $endIdx) { 165 | $tempLT = $inLow[$today]; 166 | $tempHT = $inHigh[$today]; 167 | $tempCY = $inClose[$today - 1]; 168 | $greatest = $tempHT - $tempLT; 169 | $val2 = abs($tempCY - $tempHT); 170 | if ($val2 > $greatest) { 171 | $greatest = $val2; 172 | } 173 | $val3 = abs($tempCY - $tempLT); 174 | if ($val3 > $greatest) { 175 | $greatest = $val3; 176 | } 177 | $outReal[$outIdx++] = $greatest; 178 | $today++; 179 | } 180 | $outNBElement = $outIdx; 181 | $outBegIdx = $startIdx; 182 | 183 | return ReturnCode::Success->value; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /source/TALib/Core/VolumeIndicators.php: -------------------------------------------------------------------------------- 1 | 0.0) { 69 | $ad += ((($close - $low) - ($high - $close)) / $tmp) * ((double)$inVolume[$currentBar]); 70 | } 71 | $outReal[$outIdx++] = $ad; 72 | $currentBar++; 73 | $nbBar--; 74 | } 75 | 76 | return ReturnCode::Success->value; 77 | } 78 | 79 | public static function adOsc( 80 | int $startIdx, 81 | int $endIdx, 82 | array $inHigh, 83 | array $inLow, 84 | array $inClose, 85 | array $inVolume, 86 | int $optInFastPeriod, 87 | int $optInSlowPeriod, 88 | int &$outBegIdx, 89 | int &$outNBElement, 90 | array &$outReal 91 | ): int { 92 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 93 | return $RetCode; 94 | } 95 | if ($optInFastPeriod === (PHP_INT_MIN)) { 96 | $optInFastPeriod = 3; 97 | } elseif (($optInFastPeriod < 2) || ($optInFastPeriod > 100000)) { 98 | return ReturnCode::BadParam->value; 99 | } 100 | if ($optInSlowPeriod === (PHP_INT_MIN)) { 101 | $optInSlowPeriod = 10; 102 | } elseif (($optInSlowPeriod < 2) || ($optInSlowPeriod > 100000)) { 103 | return ReturnCode::BadParam->value; 104 | } 105 | if ($optInFastPeriod < $optInSlowPeriod) { 106 | $slowestPeriod = $optInSlowPeriod; 107 | } else { 108 | $slowestPeriod = $optInFastPeriod; 109 | } 110 | $lookbackTotal = Lookback::emaLookback($slowestPeriod); 111 | if ($startIdx < $lookbackTotal) { 112 | $startIdx = $lookbackTotal; 113 | } 114 | if ($startIdx > $endIdx) { 115 | $outBegIdx = 0; 116 | $outNBElement = 0; 117 | 118 | return ReturnCode::Success->value; 119 | } 120 | $outBegIdx = $startIdx; 121 | $today = $startIdx - $lookbackTotal; 122 | $ad = 0.0; 123 | $fastK = (2.0 / ((double)($optInFastPeriod + 1))); 124 | $one_minus_fastK = 1.0 - $fastK; 125 | $slowK = (2.0 / ((double)($optInSlowPeriod + 1))); 126 | $one_minus_slowK = 1.0 - $slowK; 127 | { 128 | $high = $inHigh[$today]; 129 | $low = $inLow[$today]; 130 | $tmp = $high - $low; 131 | $close = $inClose[$today]; 132 | if ($tmp > 0.0) { 133 | $ad += ((($close - $low) - ($high - $close)) / $tmp) * ((double)$inVolume[$today]); 134 | } 135 | $today++; 136 | } 137 | $fastEMA = $ad; 138 | $slowEMA = $ad; 139 | while ($today < $startIdx) { 140 | { 141 | $high = $inHigh[$today]; 142 | $low = $inLow[$today]; 143 | $tmp = $high - $low; 144 | $close = $inClose[$today]; 145 | if ($tmp > 0.0) { 146 | $ad += ((($close - $low) - ($high - $close)) / $tmp) * ((double)$inVolume[$today]); 147 | } 148 | $today++; 149 | } 150 | $fastEMA = ($fastK * $ad) + ($one_minus_fastK * $fastEMA); 151 | $slowEMA = ($slowK * $ad) + ($one_minus_slowK * $slowEMA); 152 | } 153 | $outIdx = 0; 154 | while ($today <= $endIdx) { 155 | { 156 | $high = $inHigh[$today]; 157 | $low = $inLow[$today]; 158 | $tmp = $high - $low; 159 | $close = $inClose[$today]; 160 | if ($tmp > 0.0) { 161 | $ad += ((($close - $low) - ($high - $close)) / $tmp) * ((double)$inVolume[$today]); 162 | } 163 | $today++; 164 | } 165 | $fastEMA = ($fastK * $ad) + ($one_minus_fastK * $fastEMA); 166 | $slowEMA = ($slowK * $ad) + ($one_minus_slowK * $slowEMA); 167 | $outReal[$outIdx++] = $fastEMA - $slowEMA; 168 | } 169 | $outNBElement = $outIdx; 170 | 171 | return ReturnCode::Success->value; 172 | } 173 | 174 | public static function atr(int $startIdx, int $endIdx, array $inHigh, array $inLow, array $inClose, int $optInTimePeriod, int &$outBegIdx, int &$outNBElement, array &$outReal): int 175 | { 176 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 177 | return $RetCode; 178 | } 179 | $outBegIdx1 = 0; 180 | $outNbElement1 = 0; 181 | $prevATRTemp = static::double(1); 182 | if ($optInTimePeriod === (PHP_INT_MIN)) { 183 | $optInTimePeriod = 14; 184 | } elseif (($optInTimePeriod < 1) || ($optInTimePeriod > 100000)) { 185 | return ReturnCode::BadParam->value; 186 | } 187 | $outBegIdx = 0; 188 | $outNBElement = 0; 189 | $lookbackTotal = Lookback::atrLookback($optInTimePeriod); 190 | if ($startIdx < $lookbackTotal) { 191 | $startIdx = $lookbackTotal; 192 | } 193 | if ($startIdx > $endIdx) { 194 | return ReturnCode::Success->value; 195 | } 196 | if ($optInTimePeriod <= 1) { 197 | return VolatilityIndicators::trueRange($startIdx, $endIdx, $inHigh, $inLow, $inClose, $outBegIdx, $outNBElement, $outReal); 198 | } 199 | $tempBuffer = static::double($lookbackTotal + ($endIdx - $startIdx) + 1); 200 | $retCode = VolatilityIndicators::trueRange(($startIdx - $lookbackTotal + 1), $endIdx, $inHigh, $inLow, $inClose, $outBegIdx1, $outNbElement1, $tempBuffer); 201 | if ($retCode !== ReturnCode::Success->value) { 202 | return $retCode; 203 | } 204 | $retCode = static::TA_INT_SMA($optInTimePeriod - 1, $optInTimePeriod - 1, $tempBuffer, $optInTimePeriod, $outBegIdx1, $outNbElement1, $prevATRTemp); 205 | if ($retCode !== ReturnCode::Success->value) { 206 | return $retCode; 207 | } 208 | $prevATR = $prevATRTemp[0]; 209 | $today = $optInTimePeriod; 210 | $outIdx = (static::$unstablePeriod[UnstablePeriodFunctionID::ATR->value]); 211 | while ($outIdx > 0) { 212 | $prevATR *= $optInTimePeriod - 1; 213 | $prevATR += $tempBuffer[$today++]; 214 | $prevATR /= $optInTimePeriod; 215 | $outIdx--; 216 | } 217 | $outIdx = 1; 218 | $outReal[0] = $prevATR; 219 | $nbATR = ($endIdx - $startIdx) + 1; 220 | while (--$nbATR > 0) { 221 | $prevATR *= $optInTimePeriod - 1; 222 | $prevATR += $tempBuffer[$today++]; 223 | $prevATR /= $optInTimePeriod; 224 | $outReal[$outIdx++] = $prevATR; 225 | } 226 | $outBegIdx = $startIdx; 227 | $outNBElement = $outIdx; 228 | 229 | return $retCode; 230 | } 231 | 232 | public static function obv(int $startIdx, int $endIdx, array $inReal, array &$inVolume, int &$outBegIdx, int &$outNBElement, array &$outReal): int 233 | { 234 | if ($RetCode = static::validateStartEndIndexes($startIdx, $endIdx)) { 235 | return $RetCode; 236 | } 237 | $prevOBV = $inVolume[$startIdx]; 238 | $prevReal = $inReal[$startIdx]; 239 | $outIdx = 0; 240 | for ($i = $startIdx; $i <= $endIdx; $i++) { 241 | $tempReal = $inReal[$i]; 242 | if ($tempReal > $prevReal) { 243 | $prevOBV += $inVolume[$i]; 244 | } elseif ($tempReal < $prevReal) { 245 | $prevOBV -= $inVolume[$i]; 246 | } 247 | $outReal[$outIdx++] = $prevOBV; 248 | $prevReal = $tempReal; 249 | } 250 | $outBegIdx = $startIdx; 251 | $outNBElement = $outIdx; 252 | 253 | return ReturnCode::Success->value; 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /source/TALib/Enum/CandleSettingType.php: -------------------------------------------------------------------------------- 1 | ReturnMessages::Success->value, 61 | self::BadParam => ReturnMessages::BadParam->value, 62 | self::OutOfRangeStartIndex => ReturnMessages::OutOfRangeStartIndex->value, 63 | self::OutOfRangeEndIndex => ReturnMessages::OutOfRangeEndIndex->value, 64 | self::AllocError => ReturnMessages::AllocError->value, 65 | self::InternalError => ReturnMessages::InternalError->value, 66 | self::UnevenParameters => ReturnMessages::UnevenParameters->value, 67 | default => 'Unknown return code', 68 | }; 69 | } 70 | public function message(): string 71 | { 72 | return match ($this->value) { 73 | self::Success => ReturnMessages::Success->value, 74 | self::BadParam => ReturnMessages::BadParam->value, 75 | self::OutOfRangeStartIndex => ReturnMessages::OutOfRangeStartIndex->value, 76 | self::OutOfRangeEndIndex => ReturnMessages::OutOfRangeEndIndex->value, 77 | self::AllocError => ReturnMessages::AllocError->value, 78 | self::InternalError => ReturnMessages::InternalError->value, 79 | self::UnevenParameters => ReturnMessages::UnevenParameters->value, 80 | default => 'Unknown return code', 81 | }; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /source/TALib/Enum/ReturnMessages.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | use LupeCode\phpTraderNative\Trader; 13 | 14 | /* 15 | * Define any missing Trader constants 16 | */ 17 | // ENUM TRADER_MA_TYPE 18 | require_once __DIR__ . '/polyfill/enumMaType.php'; 19 | if (!defined('TRADER_REAL_MIN')) { 20 | define('TRADER_REAL_MIN', (-3e+37)); 21 | } 22 | if (!defined('TRADER_REAL_MAX')) { 23 | define('TRADER_REAL_MAX', (3e+37)); 24 | } 25 | 26 | if (!defined('TRADER_INTEGER_MIN')) { 27 | define('TRADER_INTEGER_MIN', (-2147483647 + 1)); 28 | } 29 | if (!defined('TRADER_INTEGER_MAX')) { 30 | define('TRADER_INTEGER_MAX', (2147483647)); 31 | } 32 | // ENUM TRADER_FUNC_UNST 33 | require_once __DIR__ . '/polyfill/enumFunctionUnstablePeriod.php'; 34 | // ENUM TRADER_COMPATIBILITY 35 | if (!defined('TRADER_COMPATIBILITY_DEFAULT')) { 36 | define('TRADER_COMPATIBILITY_DEFAULT', 0); 37 | } 38 | if (!defined('TRADER_COMPATIBILITY_METASTOCK')) { 39 | define('TRADER_COMPATIBILITY_METASTOCK', 1); 40 | } 41 | // ENUM TRADER_ERR 42 | require_once __DIR__ . '/polyfill/enumErrorCode.php'; 43 | 44 | /* 45 | * Define any missing Trader functions 46 | */ 47 | if (!function_exists('trader_set_unstable_period')) { 48 | function trader_set_unstable_period(int $functionId, int $timePeriod): void { Trader::set_unstable_period($functionId, $timePeriod); } 49 | } 50 | if (!function_exists('trader_get_unstable_period')) { 51 | function trader_get_unstable_period(int $functionId): int { return Trader::get_unstable_period($functionId); } 52 | } 53 | if (!function_exists('trader_set_compat')) { 54 | function trader_set_compat(int $compatId): void { Trader::set_compat($compatId); } 55 | } 56 | if (!function_exists('trader_get_compat')) { 57 | function trader_get_compat(): int { return Trader::get_compat(); } 58 | } 59 | 60 | require_once __DIR__ . '/polyfill/functions.php'; 61 | 62 | /* 63 | * Define friendly aliases for all functions 64 | */ 65 | function traderMathArcCosine(array $real) { return \LupeCode\phpTraderNative\TraderFriendly::mathArcCosine($real); } 66 | -------------------------------------------------------------------------------- /source/polyfill/enumErrorCode.php: -------------------------------------------------------------------------------- 1 | value; 21 | $optInSlowD_MAType = MovingAverageType::SMA->value; 22 | $Output = LupeTraderFriendly::slowStochasticRelativeStrengthIndex($this->Close, $rsi_period, $optInFastK_Period, $optInSlowK_Period, $optInSlowK_MAType, $optInSlowD_Period, $optInSlowD_MAType); 23 | $traderRsi = \trader_rsi($this->Close, $rsi_period); 24 | [$traderSlowK, $traderSlowD] = \trader_stoch($traderRsi, $traderRsi, $traderRsi, $optInFastK_Period, $optInSlowK_Period, $optInSlowK_MAType, $optInSlowD_Period, $optInSlowD_MAType); 25 | $this->assertEqualsWithDelta($traderSlowK, $this->adjustForPECL($Output['SlowK']), 0.1); 26 | $this->assertEqualsWithDelta($traderSlowD, $this->adjustForPECL($Output['SlowD']), 0.1); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/LupeTraderTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(\trader_acos($in), $this->adjustForPECL(LupeTrader::acos($in))); 20 | } 21 | 22 | public function testAd(): void 23 | { 24 | $Output = LupeTrader::ad($this->High, $this->Low, $this->Close, $this->Volume); 25 | $traderAccDist = \trader_ad($this->High, $this->Low, $this->Close, $this->Volume); 26 | $this->assertEqualsWithDelta($traderAccDist, $this->adjustForPECL($Output), 0.1); 27 | } 28 | 29 | public function testAdd(): void 30 | { 31 | $this->assertEquals(\trader_add($this->High, $this->Low), $this->adjustForPECL(LupeTrader::add($this->High, $this->Low))); 32 | } 33 | 34 | public function testAdOscVs() 35 | { 36 | $optInFastPeriod = 3; 37 | $optInSlowPeriod = 10; 38 | $this->assertEquals( 39 | Trader::adosc($this->High, $this->Low, $this->Close, $this->Volume, $optInFastPeriod, $optInSlowPeriod), 40 | LupeTrader::adosc($this->High, $this->Low, $this->Close, $this->Volume, $optInFastPeriod, $optInSlowPeriod) 41 | ); 42 | 43 | $optInFastPeriod = 5; 44 | $optInSlowPeriod = 12; 45 | $this->assertEqualsWithDelta( 46 | Trader::adosc($this->High, $this->Low, $this->Close, $this->Volume, $optInFastPeriod, $optInSlowPeriod), 47 | LupeTrader::adosc($this->High, $this->Low, $this->Close, $this->Volume, $optInFastPeriod, $optInSlowPeriod), 48 | 0.1 49 | ); 50 | 51 | $optInFastPeriod = 3; 52 | $optInSlowPeriod = 15; 53 | $this->assertEqualsWithDelta( 54 | Trader::adosc($this->High, $this->Low, $this->Close, $this->Volume, $optInFastPeriod, $optInSlowPeriod), 55 | LupeTrader::adosc($this->High, $this->Low, $this->Close, $this->Volume, $optInFastPeriod, $optInSlowPeriod), 56 | 0.1 57 | ); 58 | } 59 | 60 | public function testAdOsc(): void 61 | { 62 | $optInFastPeriod = 3; 63 | $optInSlowPeriod = 10; 64 | $this->assertEqualsWithDelta( 65 | \trader_adosc($this->High, $this->Low, $this->Close, $this->Volume, $optInFastPeriod, $optInSlowPeriod), 66 | LupeTrader::adosc($this->High, $this->Low, $this->Close, $this->Volume, $optInFastPeriod, $optInSlowPeriod), 67 | 0.1 68 | ); 69 | 70 | $optInFastPeriod = 5; 71 | $optInSlowPeriod = 12; 72 | $this->assertEqualsWithDelta( 73 | \trader_adosc($this->High, $this->Low, $this->Close, $this->Volume, $optInFastPeriod, $optInSlowPeriod), 74 | LupeTrader::adosc($this->High, $this->Low, $this->Close, $this->Volume, $optInFastPeriod, $optInSlowPeriod), 75 | 0.1 76 | ); 77 | } 78 | 79 | public function testSlowStochRsi(): void 80 | { 81 | $rsi_period = 14; 82 | $optInFastK_Period = 3; 83 | $optInSlowK_Period = 10; 84 | $optInSlowD_Period = 5; 85 | $optInSlowK_MAType = MovingAverageType::SMA->value; 86 | $optInSlowD_MAType = MovingAverageType::SMA->value; 87 | $Output = LupeTrader::slowstochrsi($this->Close, $rsi_period, $optInFastK_Period, $optInSlowK_Period, $optInSlowK_MAType, $optInSlowD_Period, $optInSlowD_MAType); 88 | $traderRsi = \trader_rsi($this->Close, $rsi_period); 89 | [$traderSlowK, $traderSlowD] = \trader_stoch($traderRsi, $traderRsi, $traderRsi, $optInFastK_Period, $optInSlowK_Period, $optInSlowK_MAType, $optInSlowD_Period, $optInSlowD_MAType); 90 | $this->assertEqualsWithDelta($traderSlowK, $this->adjustForPECL($Output['SlowK']), 0.1); 91 | $this->assertEqualsWithDelta($traderSlowD, $this->adjustForPECL($Output['SlowD']), 0.1); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /tests/TestingTrait.php: -------------------------------------------------------------------------------- 1 | 9 | protected $Open = [ 10 | '37.560001', '36.889999', '36.25', '35.580002', '35.869999', '36.400002', '37.639999', '37.369999', 11 | '37.369999', '37.27', '37.43', '37.389999', '37.43', '36.630001', '36.700001', '36.970001', '36.82', 12 | '36.84', '36.900002', '36.669998', '37.110001', '36.66', '37.25', '37.73', '36.880001', '36.130001', 13 | '36.630001', '36.849998', '36.459999', '36.049999', '36.939999', '37.09', '37.439999', '37.029999', 14 | '36.91', '37.389999', '37.279999', '37.169998', '36.970001', '36.549999', '37.060001', '35.810001', 15 | '36.25', '36.650002', '36.509998', '36.84', '37.52', '37.32', '37.470001', '37.50', '37.919998', 16 | '37.099998', '36.459999', '36.240002', '37.130001', '37.470001', '36.939999', '36.650002', 17 | '36.380001', '36.849998', '35.860001', '36.400002', '36.700001', '36.549999', '36.540001', 18 | '36.509998', '36.610001', '35.049999', '35.389999', '34.450001', '35.43', '34.68', '35.00', 19 | '34.540001', '33.880001', '33.029999', '33.32', '33.75', '33.290001', '33.59', '33.09', '33.66', 20 | '33.68', '32.91', '32.709999', '32.810001', '32.040001', '31.309999', '31.68', '30.74', '30.41', 21 | '31.200001', '30.65', '30.190001', '29.559999', '29.469999', '27.98', '27.120001', '26.459999', 22 | '27.110001', '26.639999', '27.610001', '29.059999', '27.91', '28.450001', '29.32', '29.27', '29.10', 23 | '30.59', '29.90', '29.76', '29.959999', '29.969999', '28.75', '28.98', '29.84', '29.280001', 24 | '29.690001', '30.889999', '30.58', '30.65', '30.51', '30.969999', '31.67', '31.549999', '32.119999', 25 | '33.220001', '33.849998', '33.630001', '33.900002', '34.189999', '34.240002', '32.990002', 26 | '33.119999', '33.099998', '33.810001', '33.150002', '32.529999', '32.939999', '34.009998', 27 | '34.490002', '35.799999', '34.240002', '34.759998', '34.34', '35.59', '35.00', '33.869999', 28 | '33.029999', '32.790001', '32.77', '33.09', '33.00', '32.860001', '33.209999', '32.889999', 29 | '33.009998', '32.23', '32.779999', '33.200001', '34.209999', '33.450001', '34.07', '34.939999', 30 | '35.02', '34.889999', '35.150002', '35.459999', '35.139999', '34.869999', '34.299999', '34.970001', 31 | '33.130001', '32.650002', '31.26', '32.060001', '33.490002', '33.259998', '33.639999', '32.419998', 32 | '32.279999', '32.560001', '32.709999', '32.349998', '31.790001', '31.309999', '30.790001', 33 | '30.799999', '28.620001', '28.950001', '28.65', '28.58', '29.030001', '29.66', '29.469999', 34 | '30.559999', '30.780001', '31.200001', '30.51', '31.309999', '31.139999', '29.719999', '30.68', 35 | '31.09', '31.35', '30.40', '32.200001', '32.00', '31.860001', '32.009998', '31.49', '32.91', 36 | '33.32', '33.360001', '32.459999', '32.98', '29.02', '33.580002', '34.77', '35.669998', '35.779999', 37 | '36.240002', '35.82', '35.080002', '34.290001', '36.740002', '36.869999', '36.52', '37.34', '37.32', 38 | '36.610001', '36.669998', '37.560001', '37.50', '37.919998', '37.639999', '38.310001', '39.580002', 39 | '39.299999', '38.57', '39.610001', '39.98', '39.189999', '38.700001', '38.509998', '38.650002', 40 | '38.25', '38.349998', '38.23', '37.200001', '38.240002', '38.759998', '39.259998', '39.459999', 41 | ]; 42 | protected $High = [ 43 | '38.080002', '37.580002', '37.080002', '36.139999', '36.009998', '36.950001', '37.779999', '37.52', 44 | '37.549999', '37.68', '37.439999', '37.450001', '37.599998', '37.439999', '36.91', '37.139999', 45 | '37.73', '37.299999', '36.990002', '37.200001', '37.110001', '37.220001', '37.740002', '38.080002', 46 | '37.880001', '36.849998', '36.959999', '37.630001', '36.950001', '36.720001', '37.23', '37.50', 47 | '37.700001', '37.790001', '37.07', '37.509998', '37.630001', '37.580002', '37.349998', '37.310001', 48 | '37.450001', '36.09', '36.41', '36.740002', '36.98', '37.439999', '37.59', '37.540001', '37.540001', 49 | '38.09', '38.119999', '38.189999', '36.73', '37.00', '37.150002', '37.50', '37.349998', '36.830002', 50 | '36.849998', '36.919998', '37.25', '37.00', '36.919998', '37.50', '36.880001', '37.02', '37.279999', 51 | '36.43', '35.450001', '34.869999', '35.700001', '35.610001', '36.099998', '35.209999', '34.549999', 52 | '34.080002', '33.459999', '34.09', '33.860001', '33.599998', '33.52', '33.82', '34.380001', '33.93', 53 | '33.209999', '33.240002', '32.849998', '32.459999', '31.90', '31.360001', '31.120001', '31.379999', 54 | '31.209999', '30.23', '30.139999', '29.66', '29.440001', '27.32', '26.969999', '27.809999', 55 | '27.690001', '27.969999', '29.139999', '29.23', '28.610001', '30.23', '29.790001', '29.51', 56 | '30.629999', '30.530001', '30.190001', '30.389999', '30.52', '29.799999', '29.110001', '30.00', 57 | '29.77', '30.57', '31.17', '30.969999', '30.75', '31.540001', '31.190001', '32.459999', '32.330002', 58 | '32.349998', '33.689999', '34.040001', '34.169998', '33.91', '34.740002', '34.580002', '34.209999', 59 | '33.299999', '33.630001', '34.080002', '33.880001', '33.389999', '33.27', '34.150002', '34.73', 60 | '35.84', '34.98', '34.919998', '35.200001', '35.720001', '36.389999', '33.889999', '33.830002', 61 | '33.09', '33.52', '33.189999', '33.869999', '33.299999', '33.50', '33.02', '33.119999', '32.990002', 62 | '33.040001', '33.849998', '34.23', '34.130001', '34.080002', '35.200001', '35.299999', '35.240002', 63 | '35.34', '35.48', '35.700001', '35.150002', '35.27', '35.470001', '33.490002', '33.360001', '31.90', 64 | '32.43', '33.59', '33.630001', '33.860001', '33.490002', '32.490002', '32.830002', '33.02', '32.689999', 65 | '32.50', '31.99', '31.190001', '31.200001', '30.709999', '29.00', '29.110001', '29.23', '29.219999', 66 | '29.709999', '29.57', '30.57', '30.92', '31.74', '30.799999', '31.33', '31.77', '31.23', '30.92', 67 | '31.43', '31.59', '31.799999', '32.830002', '32.290001', '32.740002', '32.029999', '32.099998', 68 | '32.990002', '33.57', '33.77', '32.619999', '33.029999', '32.279999', '34.02', '34.950001', 69 | '35.669998', '36.189999', '36.240002', '36.349998', '36.610001', '34.77', '36.77', '37.689999', 70 | '36.759998', '37.52', '37.709999', '37.209999', '36.98', '37.57', '37.689999', '37.919998', 71 | '37.919998', '38.310001', '39.580002', '39.779999', '39.630001', '39.849998', '39.98', '39.790001', 72 | '38.959999', '38.799999', '39.029999', '38.799999', '38.419998', '38.68', '37.490002', '38.380001', 73 | '39.119999', '39.639999', '39.779999', 74 | ]; 75 | protected $Low = [ 76 | '37.43', '36.889999', '36.25', '35.50', '35.049999', '36.09', '37.41', '37.169998', '37.200001', 77 | '37.240002', '36.810001', '36.540001', '37.099998', '36.630001', '36.419998', '36.59', '36.709999', 78 | '36.84', '36.630001', '36.52', '36.50', '36.580002', '36.560001', '37.50', '36.84', '36.07', 79 | '35.470001', '36.77', '36.43', '35.880001', '36.669998', '36.889999', '37.25', '36.880001', 80 | '36.380001', '36.790001', '37.200001', '37.139999', '36.919998', '36.490002', '36.860001', 81 | '35.740002', '35.91', '36.32', '36.150002', '36.509998', '36.630001', '36.91', '37.18', '37.16', 82 | '37.349998', '37.00', '36.110001', '35.880001', '36.419998', '36.849998', '36.900002', '36.279999', 83 | '36.32', '35.619999', '35.720001', '36.310001', '36.220001', '36.540001', '36.310001', '36.50', 84 | '36.439999', '35.009998', '34.619999', '33.93', '34.709999', '34.68', '35.00', '34.380001', 85 | '33.869999', '33.00', '33.110001', '33.509998', '32.84', '32.09', '32.779999', '32.84', '33.59', 86 | '32.759998', '32.419998', '32.77', '32.040001', '31.309999', '31.219999', '30.24', '29.799999', 87 | '30.51', '30.389999', '29.700001', '29.389999', '29.059999', '27.940001', '26.719999', '26.15', 88 | '26.84', '26.51', '26.48', '27.73', '27.709999', '26.57', '28.129999', '28.790001', '28.51', 89 | '28.60', '29.450001', '29.620001', '29.66', '29.309999', '28.190001', '27.440001', '29.309999', 90 | '28.59', '28.709999', '29.33', '30.209999', '29.74', '30.00', '30.02', '31.60', '31.530001', 91 | '31.139999', '33.18', '33.349998', '33.599998', '33.349998', '34.099998', '33.990002', '32.970001', 92 | '32.689999', '32.869999', '33.110001', '32.91', '32.50', '32.209999', '32.82', '33.91', '33.150002', 93 | '34.029999', '34.470001', '34.18', '34.099998', '34.77', '33.470001', '32.849998', '32.439999', 94 | '32.75', '32.610001', '32.869999', '32.689999', '32.580002', '32.32', '32.619999', '32.119999', 95 | '32.169998', '33.130001', '33.029999', '33.259998', '33.080002', '33.459999', '34.16', '34.75', 96 | '34.650002', '35.07', '35.049999', '34.700001', '34.299999', '33.880001', '33.00', '32.240002', 97 | '31.209999', '31.01', '32.77', '33.18', '33.16', '32.400002', '31.77', '32.32', '32.549999', 98 | '31.99', '31.73', '31.209999', '30.66', '30.35', '28.43', '28.440001', '28.49', '27.85', 99 | '27.200001', '28.91', '28.85', '29.629999', '30.17', '30.879999', '30.41', '30.809999', '30.99', 100 | '29.41', '30.00', '30.629999', '31.030001', '30.35', '30.860001', '31.440001', '31.799999', 101 | '31.379999', '31.23', '32.23', '32.93', '32.950001', '31.110001', '31.540001', '29.00', '32.91', 102 | '33.869999', '35.029999', '35.599998', '35.77', '35.720001', '34.82', '33.849998', '35.73', 103 | '36.869999', '36.150002', '36.299999', '37.23', '36.599998', '36.369999', '36.619999', '37.299999', 104 | '37.380001', '37.27', '37.650002', '38.669998', '39.060001', '38.259998', '39.349998', '39.259998', 105 | '38.93', '38.599998', '38.150002', '38.439999', '38.099998', '37.779999', '37.52', '36.939999', 106 | '36.580002', '38.459999', '39.189999', '39.150002', 107 | ]; 108 | protected $Close = [ 109 | '37.990002', '37.560001', '36.860001', '36.040001', '35.220001', '36.240002', '37.779999', 110 | '37.360001', '37.400002', '37.290001', '36.939999', '37.389999', '37.32', '37.400002', '36.470001', 111 | '36.830002', '37.349998', '36.970001', '36.73', '37.07', '36.599998', '37.150002', '36.650002', 112 | '37.939999', '37.82', '36.759998', '35.59', '37.529999', '36.66', '36.50', '37.02', '37.240002', 113 | '37.27', '37.48', '36.48', '37.029999', '37.369999', '37.439999', '37.18', '37.23', '36.939999', 114 | '36.00', '36.009998', '36.529999', '36.599998', '36.59', '36.950001', '37.110001', '37.23', '37.48', 115 | '37.669998', '37.84', '36.330002', '36.52', '36.509998', '37.169998', '37.310001', '36.66', '36.48', 116 | '36.07', '36.169998', '36.66', '36.41', '37.02', '36.48', '36.810001', '36.560001', '36.32', 117 | '35.23', '34.860001', '34.799999', '35.41', '35.470001', '35.169998', '34.279999', '34.009998', 118 | '33.259998', '33.580002', '33.810001', '32.82', '33.509998', '32.93', '33.959999', '33.860001', 119 | '32.880001', '32.91', '32.799999', '31.790001', '31.370001', '31.360001', '30.950001', '30.67', 120 | '31.17', '30.040001', '29.42', '29.370001', '29.280001', '27.040001', '26.76', '27.10', '26.82', 121 | '27.049999', '27.969999', '29.15', '27.68', '29.059999', '29.57', '29.51', '28.75', '29.690001', 122 | '29.98', '29.780001', '29.75', '29.309999', '28.780001', '29.74', '29.139999', '30.32', '29.440001', 123 | '30.690001', '30.17', '30.629999', '30.16', '32.16', '32.200001', '31.40', '33.259998', '33.369999', 124 | '34.040001', '33.599998', '34.110001', '34.450001', '34.189999', '32.970001', '32.950001', '33.23', 125 | '33.779999', '33.029999', '32.59', '32.91', '34.630001', '34.400002', '34.849998', '34.68', '34.91', 126 | '34.34', '35.650002', '33.709999', '33.810001', '32.939999', '33.16', '32.959999', '33.360001', 127 | '33.110001', '32.630001', '32.98', '32.860001', '32.950001', '32.189999', '33.23', '33.380001', 128 | '33.990002', '33.68', '34.200001', '35.119999', '35.07', '34.720001', '35.27', '35.619999', 129 | '35.049999', '35.189999', '34.299999', '33.400002', '33.169998', '31.67', '31.120001', '32.830002', 130 | '33.50', '33.369999', '33.48', '32.09', '32.34', '32.860001', '32.52', '32.369999', '31.870001', 131 | '30.959999', '30.85', '30.709999', '28.91', '28.91', '28.26', '27.60', '29.129999', '29.34', 132 | '29.74', '30.40', '31.17', '30.74', '30.93', '31.40', '31.040001', '30.32', '31.43', '31.15', 133 | '31.52', '30.90', '31.58', '32.540001', '31.77', '31.60', '32.240002', '33.139999', '33.689999', 134 | '32.52', '31.74', '31.309999', '32.93', '34.099998', '35.189999', '35.689999', '36.099998', 135 | '36.240002', '35.93', '34.490002', '36.029999', '37.150002', '36.669998', '36.459999', '37.25', 136 | '37.119999', '36.689999', '36.669998', '37.419998', '37.669998', '37.720001', '37.84', '38.849998', 137 | '39.209999', '39.240002', '39.73', '39.540001', '39.68', '38.91', '38.380001', '38.630001', 138 | '38.759998', '37.919998', '37.610001', '37.23', '38.23', '38.610001', '39.380001', '39.330002', 139 | ]; 140 | protected $Volume = [ 141 | '10602800', '15209900', '10808200', '10990700', '14108600', '19657800', '7710900', '6279200', 142 | '5574600', '8957300', '12593900', '10634000', '10213200', '13023100', '6519800', '11216500', 143 | '14759100', '9181900', '14757100', '8426100', '12059100', '12179300', '22177600', '12873600', 144 | '14645900', '16285700', '33984900', '10623800', '7785800', '34330700', '7127500', '8497900', 145 | '11340300', '16995100', '11073200', '11170900', '10010800', '7178500', '6157400', '9218200', 146 | '13718900', '9668700', '9088800', '6585500', '12006800', '13175900', '16401800', '10083100', 147 | '9832100', '9106700', '11658700', '30761900', '20460600', '17738300', '19046900', '16620000', 148 | '17166400', '10447800', '13750800', '20273400', '38789100', '19445000', '12443000', '20227300', 149 | '13656200', '18679000', '19849800', '23603400', '12981000', '14118600', '12217700', '11613900', 150 | '13551300', '20552600', '9335700', '11002500', '10672300', '8252700', '12380800', '19452000', 151 | '12653200', '21043800', '22924000', '22946600', '11361400', '10462200', '14850900', '21168500', 152 | '16683500', '19907400', '12613500', '17072300', '21465100', '20735000', '15319400', '12949700', 153 | '20187900', '13011900', '11188200', '9098800', '14324800', '24498200', '16379600', '28548200', 154 | '55707000', '36544700', '12905700', '18979500', '15589300', '13534600', '11426200', '23176900', 155 | '16298100', '16282500', '20458900', '20053000', '17572600', '15031400', '16692400', '13198500', 156 | '17140100', '26322800', '21209100', '16047800', '14301100', '25194400', '7423300', '5933500', 157 | '12621900', '11020800', '3470700', '13469200', '15531300', '9106700', '16921800', '13243500', 158 | '12689400', '16140900', '15949600', '14936200', '16174200', '45101900', '19852600', '12047900', 159 | '16109600', '17080500', '56708100', '11020400', '17534700', '5316100', '13257200', '13364700', 160 | '11899700', '12498500', '14222100', '11105600', '10270400', '11087800', '11063300', '14273100', 161 | '11804100', '11511000', '13645500', '16608600', '15679100', '17501800', '12394300', '15206700', 162 | '17418900', '10796200', '15174300', '26380900', '15810300', '24308600', '18501200', '30390400', 163 | '19349200', '10856500', '12209300', '19403800', '11282800', '15873800', '12580300', '11830400', 164 | '14245400', '12224800', '10963900', '12883300', '29250500', '14467600', '18317900', '41895400', 165 | '49541300', '12817500', '19109900', '12755400', '15614900', '18745000', '20538300', '17094200', 166 | '22179700', '25661600', '22186900', '14365400', '14330900', '47058500', '26873900', '13854100', 167 | '13748900', '15688100', '19903500', '13609000', '9366100', '19105100', '25414500', '15752300', 168 | '23163400', '18373600', '15604400', '8976700', '11445300', '7933600', '12241100', '28987700', 169 | '31405700', '13833200', '11351400', '11845500', '12299700', '6844000', '9051900', '9887300', 170 | '15446100', '9542700', '10290600', '10333100', '12942700', '8590700', '9267400', '17714800', 171 | '14861600', '14752100', '14864300', '8462400', '9147300', '9506500', '8713200', '10057600', 172 | '16909400', '20529200', '19432500', '11803400', '7713000', '6398400', 173 | ]; 174 | // 175 | 176 | /** 177 | * @param array $outReal 178 | * @param int $precision 179 | * @param int $mode 180 | * 181 | * @return array 182 | */ 183 | protected function adjustForPECL(array $outReal, int $precision = 3, int $mode = \PHP_ROUND_HALF_DOWN): array 184 | { 185 | $newOutReal = []; 186 | foreach ($outReal as $index => $inDouble) { 187 | $newOutReal[$index] = round($inDouble, $precision, $mode); 188 | } 189 | 190 | return $newOutReal; 191 | } 192 | } 193 | --------------------------------------------------------------------------------