├── .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 | 
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 |
--------------------------------------------------------------------------------