205 Commits

Author SHA1 Message Date
Saleh Mir
7953fbc90b Implement PNL and PNL% for inverse futures 2021-03-09 17:47:45 +01:00
Saleh Mir
e3bbd8a4fc Implement the contract_size property 2021-03-09 16:34:04 +01:00
Saleh Mir
53992318ff add contract_size as a config to inverse futures 2021-03-09 16:20:50 +01:00
Saleh Mir
25fb188331 fix leverage property for inverse futures 2021-03-09 15:03:22 +01:00
Saleh Mir
b2ec3d42ce Merge branch 'master' into inverse-futures 2021-03-08 17:25:17 +01:00
Saleh Mir
816bdf0aea fix issue where market orders are not detected as reduce_only 2021-03-08 17:08:34 +01:00
Saleh Mir
71adce0cca add unit test: test_can_detect_inverse_futures 2021-03-08 15:44:30 +01:00
Markus
34001e860d Merge remote-tracking branch 'origin/master' 2021-03-08 15:12:25 +01:00
Markus
ff4f2f8ac2 Move np_ffill to helpers and add test. 2021-03-08 15:11:55 +01:00
Saleh Mir
5b7996adda Merge pull request #143 from discohead/futures-leverage-fix
strategy.leverage instead of exchange.futures_leverage
2021-03-08 13:00:36 +01:00
pyup.io bot
21925e5f23 Update arrow from 1.0.2 to 1.0.3 (#147) 2021-03-08 12:40:04 +01:00
Markus
60aaeedda0 Fix wrong np type declarations. 2021-03-07 18:08:35 +01:00
Markus
3d10b5d5a2 Merge remote-tracking branch 'origin/master' 2021-03-06 22:54:58 +01:00
Markus
60238aea43 Fix crossed. Wrong np type declaration in check. 2021-03-06 22:54:34 +01:00
pyup.io bot
e97b88df45 Update peewee from 3.14.1 to 3.14.2 (#145) 2021-03-06 10:50:03 +01:00
pyup.io bot
043666b96f Update numba from 0.53.0rc2 to 0.53.0rc3 (#146) 2021-03-06 10:49:55 +01:00
Markus
cff6b63ae5 Add new timeframes to anchor helper. 2021-03-05 18:14:08 +01:00
Markus
5fd0bf5383 Use only numpy in crossed and add more tests. 2021-03-05 17:53:09 +01:00
Saleh Mir
8b359f06df fix 👷‍♂️ 2021-03-05 17:35:04 +01:00
Saleh Mir
b0ca75394d add support for inverse futures' symbol to app_currency() helper function 2021-03-05 17:34:20 +01:00
Saleh Mir
b7c07d2913 fix 2021-03-05 17:09:28 +01:00
Saleh Mir
9c252a50d4 Add support for inverse_futures for the single_route_backtest test utility 2021-03-04 19:51:32 +01:00
Manuel Ebert
f93aebbc81 Only use integers in readable_duration output (#142)
While typed as `int`, the seconds parameter is often passed in as a float in reports, leading to outputs like this:

```python
readable_duration(184722.0, 3)
# 2.0 days, 3.0 hours, 18.0 minutes
```

This is neither pretty nor correct, converting seconds to integer first always gives us the desired output:

```python
readable_duration(184722.0, 3)
# 2 days, 3 hours, 18 minutes
```
2021-03-04 11:29:32 +01:00
pyup.io bot
797a914e4e Update pydash from 4.9.2 to 4.9.3 (#144) 2021-03-04 11:27:22 +01:00
discohead
d995163498 revert test changes 2021-03-03 14:31:27 -08:00
discohead
5c2f0b6c38 conditionally return total_cost with leverage 2021-03-03 14:30:11 -08:00
discohead
328c16ca69 rename to StrategyMock 2021-03-03 14:20:36 -08:00
discohead
ccd231b51c fix tests 2021-03-03 12:30:18 -08:00
discohead
a4daf13513 strategy.leverage instead of exchange.futures_leverage
This seems like the fix for https://github.com/jesse-ai/jesse/issues/122

Also a fixes a bug in the `mode` property of `Position`
2021-03-03 10:59:29 -08:00
Saleh Mir
243d4bb9dc initial implementation of InverseFuturesExchange 2021-03-03 18:15:59 +01:00
Saleh Mir
b4cc5be234 add "Binance Inverse Futures" to config file 2021-03-03 16:58:32 +01:00
pyup.io bot
5f025cc72a Update websocket-client from 0.57.0 to 0.58.0 (#141) 2021-03-03 10:52:31 +01:00
Saleh Mir
8a052cb55b Clean comments 2021-03-02 16:01:25 +01:00
Saleh Mir
d7216eac15 Merge pull request #140 from jesse-ai/master
update to latest changes on master
2021-03-02 15:49:13 +01:00
Saleh Mir
d76027fba6 Merge pull request #138 from jesse-ai/pyup-update-pandas-1.2.2-to-1.2.3
Update pandas to 1.2.3
2021-03-02 15:47:09 +01:00
Saleh Mir
dffdf15cfa Merge pull request #139 from jesse-ai/inverse-futures
Inverse futures: import-candle driver only
2021-03-02 15:46:04 +01:00
Saleh Mir
61e679acfd implement driver for importing candle from 'Binance Inverse Futures' 2021-03-02 15:44:31 +01:00
pyup-bot
58df1a1499 Update pandas from 1.2.2 to 1.2.3 2021-03-02 15:38:10 +01:00
Saleh Mir
51dcea96eb remove empty comments 2021-03-02 15:17:28 +01:00
pyup.io bot
0bc6f94b58 Update arrow from 1.0.1 to 1.0.2 (#137) 2021-03-01 11:06:42 +01:00
pyup.io bot
4a01f536df Update arrow from 1.0.0 to 1.0.1 (#136) 2021-02-28 15:28:53 +01:00
pyup.io bot
14e0a73ce4 Update numba from 0.53.0rc1.post1 to 0.53.0rc2 (#135) 2021-02-27 14:35:58 +01:00
pyup.io bot
4820ed2ddd Update arrow from 0.17.0 to 1.0.0 (#134) 2021-02-27 14:35:46 +01:00
Markus
8af63a0f3a Use class_iter to validate timeframe. 2021-02-26 11:49:13 +01:00
Markus
95023593bc Add 45m, 12h, 3D timeframes. 2021-02-24 18:25:54 +01:00
pyup.io bot
b30a5e2de0 Update tabulate from 0.8.8 to 0.8.9 (#132) 2021-02-24 18:07:42 +01:00
pyup.io bot
a2b39c303d Update scipy from 1.6.0 to 1.6.1 (#129) 2021-02-24 18:07:30 +01:00
Jônatas Castro
1df4dbc223 Update rvi.py (#131) 2021-02-21 13:49:09 +01:00
Markus
6454cb2255 Add vwap. 2021-02-20 13:04:16 +01:00
Markus
76d1ee029b Add vwap. 2021-02-19 18:30:50 +01:00
Markus
a3bf9631b6 Merge remote-tracking branch 'origin/master' 2021-02-19 18:04:57 +01:00
Markus
4b0acbdcba Add rvi. 2021-02-19 18:03:24 +01:00
pyup.io bot
b5547b4768 Update tabulate from 0.8.7 to 0.8.8 (#128) 2021-02-19 17:38:45 +01:00
Markus
2795ebaeb5 Use talib for speed. 2021-02-17 21:36:27 +01:00
Markus
6f2e17d05a Merge remote-tracking branch 'origin/master' 2021-02-16 14:15:37 +01:00
Markus
157b161b83 Add stc. 2021-02-16 14:15:25 +01:00
Markus K
3e5b8ae508 Update numba for 3.9 support. 2021-02-16 12:59:14 +01:00
Saleh Mir
a5692e2ab5 Merge branch 'master' of github.com:jesse-ai/jesse 2021-02-16 11:18:42 +01:00
Saleh Mir
b4624fcf96 Send errors to a second telegram bot used only for errors 2021-02-16 11:18:36 +01:00
Markus
51d35e43c3 Add devstop. 2021-02-15 18:39:16 +01:00
Markus
31b4f1dc3c Add rsmk. 2021-02-15 18:33:35 +01:00
Markus
8e79331ffb Fix tests. 2021-02-15 18:24:18 +01:00
Markus
85ef8cf9d5 Add kaufmanstop and safezonestop. 2021-02-15 18:17:47 +01:00
Markus
0b19b16f9e Use numba for speed. 2021-02-15 17:43:26 +01:00
Markus
cc4c3207eb Add high_pass_2_pole and supersmoother_3_pole. Use numba for speed. 2021-02-15 17:18:11 +01:00
Markus
7b0951f083 Use numba to speed up indicators. Add fwma. 2021-02-15 16:58:13 +01:00
Markus
77b71d8164 Use numpy instead of math. 2021-02-15 16:06:06 +01:00
Markus
94b74b834d Use numba to speed up custom indicators. 2021-02-15 15:52:09 +01:00
Markus
e004ff7f4a Use numba to speed up Correlation Cycle. 2021-02-15 14:34:24 +01:00
Markus
0858bbac3b Use numba to speed up CG. 2021-02-15 14:26:48 +01:00
Markus
f4069a14c0 Add numba to requirements 2021-02-15 14:06:52 +01:00
Markus
4b9edda1ec Fix indicator tests. 210 -> 240 2021-02-15 11:18:52 +01:00
Markus
bc303a04e9 Add Kaufman Efficiency indicator & Reformat 2021-02-15 10:43:28 +01:00
Markus
831130c605 Use actual warmup_candles_num in indicators. 2021-02-15 10:17:50 +01:00
pyup.io bot
9eee3e70d7 Update pandas from 1.2.1 to 1.2.2 (#126) 2021-02-10 19:50:16 +01:00
Markus K
4f3bcabfe6 Update .gitignore 2021-02-08 11:19:11 +01:00
Markus K
9c080b4bab Delete test.md 2021-02-08 10:55:08 +01:00
pyup.io bot
0392cac894 Update numpy from 1.20.0 to 1.20.1 (#124) 2021-02-08 10:27:26 +01:00
pyup.io bot
a42fc9d9ab Update peewee from 3.14.0 to 3.14.1 (#125) 2021-02-08 10:27:14 +01:00
Markus K
21b76aa8c8 With typing (#123)
* Add typing.
2021-02-07 20:34:00 +01:00
Saleh Mir
580b91db21 Merge branch 'master' of github.com:jesse-ai/jesse 2021-02-05 11:54:40 +01:00
Saleh Mir
4a5162aaaf fix tests 2021-02-05 11:36:23 +01:00
Saleh Mir
508bd0acde store DailyBalance of different assets into DB in live mode 2021-02-05 11:32:36 +01:00
Saleh Mir
5d93e8b0d6 fix a typo 2021-02-05 11:03:57 +01:00
Markus
46f7ef1973 Add 1W Timeframe 2021-02-05 10:15:24 +01:00
Markus
18d609b08d Merge remote-tracking branch 'origin/master' 2021-02-05 09:55:54 +01:00
Markus
1cfcd8bbb0 Add 1W Timeframe 2021-02-05 09:55:08 +01:00
Saleh Mir
8a17c2fb15 display "strategy name" and "account balance" in CLOSED position message 2021-02-04 18:16:48 +01:00
Saleh Mir
51e48396b0 store Order and CompletedTrade records into database in live mode 2021-02-04 17:51:51 +01:00
Saleh Mir
8dcc0f17a1 Merge branch 'master' of github.com:jesse-ai/jesse 2021-02-03 11:20:10 +01:00
Saleh Mir
bc86d82bb8 improve exception message 2021-02-03 11:19:02 +01:00
Saleh Mir
f1b46a603d create DB table for Order model 2021-02-03 11:18:53 +01:00
Saleh Mir
42bf7baf8d use postgres_ext driver for db to have access to PostgreSQL specific features 2021-02-03 11:18:34 +01:00
Saleh Mir
04fdfb390c create DB table for CompletedTrade model 2021-02-03 09:54:08 +01:00
Saleh Mir
8efc66c13f add trade_id property to Order models 2021-02-03 09:53:20 +01:00
Saleh Mir
689da58d4b add unit test for CompletedTrade model 2021-02-03 09:52:55 +01:00
Saleh Mir
c926772354 add is_valid_uuid() helper function 2021-02-03 09:52:19 +01:00
Markus
7971bafbd3 Add CG Oscillator 2021-02-02 21:55:19 +01:00
Markus
963f6df397 Add Indicator: Chande Forcast Oscillator (CFO) 2021-02-02 21:21:12 +01:00
Saleh Mir
7a0b60fc44 refactor 2021-02-02 16:04:05 +01:00
Saleh Mir
650652ea66 add support for zero_fee to set_up test utility function 2021-02-02 16:03:48 +01:00
Saleh Mir
e00b5c0b1a rename test_trade => test_completed_trade 2021-02-02 15:58:09 +01:00
Saleh Mir
97e750c9d5 bring back frama and sinwma 2021-02-02 15:54:54 +01:00
Saleh Mir
bebdab0348 disable sinwma and frama which very causing tests to fail 2021-02-02 15:38:20 +01:00
Saleh Mir
ea73c4afe9 clean formatting 2021-02-02 15:15:05 +01:00
Saleh Mir
1712c6162e generte new UUID per each Trade object 2021-02-02 15:09:43 +01:00
Saleh Mir
8b4f426b36 Merge branch 'master' of github.com:jesse-ai/jesse 2021-02-02 15:08:32 +01:00
Saleh Mir
1a07afa365 remove empty comments 2021-02-02 15:08:26 +01:00
Saleh Mir
5d6f0d170d remove empty comments 2021-02-01 15:26:18 +01:00
Markus
d270217719 Rever ma_type to matype. 2021-02-01 11:20:28 +01:00
Markus
2e078016b2 Add typing. 2021-02-01 11:18:09 +01:00
Markus
eb53f182ba Refactor for consistency. 2021-02-01 11:03:17 +01:00
Markus
1f4424c6d9 Refactor for consistency. 2021-02-01 10:53:35 +01:00
Markus
109680eaf2 Add Sine Weighted Moving Average (SINWMA) 2021-01-31 21:33:06 +01:00
Markus
0f96bcc507 Fibonacci's Weighted Moving Average (FWMA) 2021-01-31 14:18:39 +01:00
Markus
336875b147 Add missing types. 2021-01-31 13:53:18 +01:00
Markus
3ad0a2bee5 Merge branch 'master' of https://github.com/jesse-ai/jesse 2021-01-31 13:47:43 +01:00
pyup.io bot
07db5c7bd5 Pin numpy to latest version 1.20.0 (#120) 2021-01-31 13:45:46 +01:00
Saleh Mir
ceb0331907 return nan instead None for closed posotion's total_cost value 2021-01-29 14:29:12 +01:00
Saleh Mir
e39bccbe7d rename statistics to metrics 2021-01-29 14:28:45 +01:00
Saleh Mir
6ae50534a3 improve InsufficientMargin's message 2021-01-29 11:32:57 +01:00
Markus
95849bd482 Add missing types. 2021-01-28 21:25:50 +01:00
Saleh Mir
13a7fea49d bump version 2021-01-28 18:50:03 +01:00
Saleh Mir
8bde520ffc add test_statistics_for_trades_without_fee unit test 2021-01-28 16:35:13 +01:00
Saleh Mir
d8dc65282f rename statistics module to metrics 2021-01-28 15:35:28 +01:00
Saleh Mir
4681221bac Merge branch 'leverage' 2021-01-28 15:17:50 +01:00
Saleh Mir
22732f1db8 cleanup 2021-01-28 15:13:40 +01:00
Saleh Mir
df82b85a9b implement wallet_balance and available_margin for the Exchange model and add proper properties for them in the Strategy class 2021-01-28 14:57:48 +01:00
Saleh Mir
52640815ed add types to exchange property of Position class 2021-01-28 14:35:43 +01:00
Saleh Mir
00b3fccb9e Merge pull request #118 from jesse-ai/pyup-update-pytest-6.2.1-to-6.2.2
Update pytest to 6.2.2
2021-01-28 11:55:25 +03:30
Saleh Mir
5d74df9eed add self.leverage property to Strategy 2021-01-27 19:52:27 +03:30
Saleh Mir
f9917d6a86 implement InsufficientMargin validation for futures market 2021-01-27 19:32:32 +03:30
Saleh Mir
60f893d39d use a reduce_only order for closing positons at the end of backtests 2021-01-27 19:31:49 +03:30
Saleh Mir
284d54e823 refactor test_order unit tests 2021-01-27 17:58:01 +03:30
Saleh Mir
a654af4899 add roi, total_cost, and pnl% with support for leverage to CompletedTrade class 2021-01-27 17:38:47 +03:30
Saleh Mir
97b5d5275d improve statistics and reports modules 2021-01-27 17:35:27 +03:30
Saleh Mir
2a0b67d3cf refactor CompletedTrade model 2021-01-27 14:02:46 +03:30
Saleh Mir
e5cc608b55 rename to PNL to pnl and R to r in CompletedTrade objects 2021-01-27 13:30:55 +03:30
Saleh Mir
e15fee22ee add roi and total_cost properties to Position class 2021-01-27 12:51:53 +03:30
Saleh Mir
3219e11c76 refactor + add unit test for position with leverage 2021-01-27 12:51:34 +03:30
Saleh Mir
bd4d50daca don't cash config values when running unit tests 2021-01-27 12:35:19 +03:30
Saleh Mir
bc2efcbab3 improve support for leverage in set_up() helper 2021-01-27 12:34:48 +03:30
Markus
27ce01ce2c Fix rsx indicator test. 2021-01-26 19:42:01 +01:00
Markus
76a1b9bf8b Add rsx indicator. 2021-01-26 19:41:29 +01:00
pyup-bot
76cc671d13 Update pytest from 6.2.1 to 6.2.2 2021-01-26 13:49:53 +01:00
Markus
4826d3f96a Add Market Change metric. 2021-01-25 21:34:54 +01:00
Saleh Mir
dd68a02bc7 implement Position mode + add entry_margin alias 2021-01-25 20:01:36 +03:30
Saleh Mir
b006ef5f4e load leverage config into Exchange model 2021-01-25 19:31:14 +03:30
Saleh Mir
3fbffae598 prepare config file's structure for leverage values 2021-01-25 19:30:40 +03:30
Saleh Mir
d89e46d136 change default fee to taker for Binance Futures 2021-01-25 18:25:50 +03:30
Saleh Mir
981ccf1c71 Merge branch 'leverage' of github.com:jesse-ai/jesse into leverage 2021-01-25 17:03:37 +03:30
Saleh Mir
746a220ef6 remove unnessary code 2021-01-25 16:45:31 +03:30
Saleh Mir
d93c4e2916 remove empty comments 2021-01-25 16:45:24 +03:30
Saleh Mir
edc2817b3d add roi() and total_cost() properties to Position class 2021-01-25 16:45:16 +03:30
Markus
a12033c7a0 Add utils.signal_line 2021-01-23 17:22:18 +01:00
Markus
fd955a432c Make np.diff optional in utils.streaks. 2021-01-23 17:17:29 +01:00
Markus
077201ddcc Fix for max losing and winning streak. 2021-01-23 17:15:12 +01:00
Markus
5034b6b9fa Add current_streak to stats. Use current_streak for max losing and winning streak. 2021-01-23 16:22:49 +01:00
Markus
340ca60ebe Add kelly_criterion to utils. 2021-01-23 14:47:50 +01:00
Markus
c5e8d5d6e6 Move round() to report for better accuracy in self.statistics 2021-01-23 14:43:05 +01:00
Markus
08d0b99a4d Move round() to report for better accuracy in self.statistics 2021-01-23 14:42:36 +01:00
Markus
df11855fe4 Move round() to report for better accuracy in self.statistics 2021-01-23 14:35:11 +01:00
Markus
7afe5eb3c4 Add self.statistics to strategy. 2021-01-23 14:21:35 +01:00
Saleh Mir
cf4b681bb3 raise InvalidConfig exception for wrong exchange type 2021-01-22 14:45:24 +01:00
Saleh Mir
83008ab01e rename ConfigException to InvalidConfig 2021-01-22 14:43:18 +01:00
Saleh Mir
dcce864e6d change default exchange values to "futures" 2021-01-22 14:38:54 +01:00
Saleh Mir
422d813dd8 Refactor Exchange types into Spot and Futures classes 2021-01-22 14:35:08 +01:00
Saleh Mir
fd2812b1b5 Merge pull request #117 from jesse-ai/pyup-update-pandas-1.2.0-to-1.2.1
Update pandas to 1.2.1
2021-01-21 23:25:48 +03:30
pyup-bot
d6b1eb174b Update pandas from 1.2.0 to 1.2.1 2021-01-21 15:44:43 +01:00
Saleh Mir
1af5e7c1b1 Merge pull request #116 from nicolay-zlobin/helperTrades
Ability to get completed trades inside strategy through self.trades
2021-01-21 14:52:08 +01:00
Nicolay Zlobin
b801cc09e1 Ability to get completed trades inside strategy through self.trades 2021-01-21 13:55:31 +03:00
Saleh Mir
9f7b89f770 Merge pull request #115 from julesGoullee/export-json-timeframes
export json add considering_timeframes
2021-01-20 20:41:48 +01:00
Saleh Mir
d60e00a396 Merge pull request #114 from julesGoullee/docker-deployement
split dockerfile for deployement and dockerfileTest
2021-01-20 20:40:05 +01:00
Saleh Mir
6304fdab05 Merge pull request #110 from julesGoullee/db-config-use-env-variable
db config use env variable
2021-01-20 20:39:02 +01:00
Saleh Mir
dc2bc2d917 Merge branch 'master' into leverage 2021-01-20 10:30:56 +01:00
julesGoullee
83f537e099 export json add considering_timeframes 2021-01-19 15:39:39 +04:00
julesGoullee
4019565091 docker build optimization 2021-01-19 15:36:48 +04:00
Saleh Mir
c095b2bb68 Merge branch 'master' of github.com:jesse-ai/jesse 2021-01-18 10:38:27 +01:00
Saleh Mir
9a38ffa7e5 Improve round_qty_for_live_mode to work with minimum order qty on Binance 2021-01-18 10:35:22 +01:00
Saleh Mir
6a229cc3cb implement dd() utility function
very useful for debugging
2021-01-18 10:18:43 +01:00
Markus K
1fd01b7390 Add streaks() utils. 2021-01-16 18:07:38 +01:00
Markus K
f6b34fd3f5 Add missing return types. 2021-01-16 11:24:20 +01:00
Markus K
7466f80730 Add strictly_increasing and strictly_decreasing utils. 2021-01-16 11:22:46 +01:00
julesGoullee
edeaf96e0b end of line 2021-01-15 13:28:30 +04:00
julesGoullee
49e23a036c revert change 2021-01-15 13:26:51 +04:00
julesGoullee
81f51107b9 split dockerfile for deployement and dockerfileTest 2021-01-15 13:24:57 +04:00
julesGoullee
cdb1912e24 helper get_config use env variables 2021-01-15 13:18:19 +04:00
julesGoullee
699a68045b Merge branch 'master' into db-config-use-env-variable 2021-01-14 14:38:05 +04:00
Saleh Mir
18fee9f5d0 Merge pull request #109 from julesGoullee/docker-for-ci
Docker for ci test
2021-01-14 09:54:28 +01:00
Markus K
b8473ae6cb Optimize json csv (#113)
* Added CSV and JSON as option. Return training and testing log seperated.

* Snapshot with optional CSV JSON.

* Optimize JSON add encoding.

* Fix json export.

* Output actual parameters via csv and json.
2021-01-13 16:34:07 +01:00
Gabri
2e91359a81 adding '--skip_confirmation' flag to import-canldes command to avoid … (#111)
* adding '--skip_confirmation' flag to import-canldes command to avoid confirmation on candle duplicates

* changing parameter with dash as name separator

Co-authored-by: Gabriele Guidi <gabriele.guidi@injenia.it>
2021-01-13 16:01:18 +01:00
Markus K
e6e375961a Optimize json csv (#112)
* Added CSV and JSON as option. Return training and testing log seperated.

* Snapshot with optional CSV JSON.

* Optimize JSON add encoding.

* Fix json export.
2021-01-13 15:58:57 +01:00
Saleh Mir
96406afb8d save in-progress code 2021-01-13 15:12:02 +01:00
Saleh Mir
63015ef81e remove empty comments 2021-01-13 15:11:29 +01:00
julesGoullee
89e9e0e097 db config use env variable 2021-01-10 19:45:31 +04:00
julesGoullee
8777ed36f6 docker for ci test 2021-01-10 19:28:27 +04:00
Saleh Mir
49a1ac4671 Add unit test: test_negative_balance_validation_for_spot_market 2021-01-08 14:07:14 +01:00
Saleh Mir
220c545b41 Improve single_route_backtest() to work with both spot and margin 2021-01-08 14:06:59 +01:00
Saleh Mir
1241c04e8e Refactor refactor unit tests to use a separate utils module 2021-01-08 10:39:29 +01:00
Markus K
7798ba1db2 Remove notify again, as its handled in jesse_logger. 2021-01-06 10:48:39 +01:00
Markus K
5b65ea3506 Actually notify. 2021-01-05 11:48:05 +01:00
Saleh Mir
17083487e7 update test_dashless_symbol() 2021-01-02 17:54:02 +01:00
Saleh Mir
d6c2a96270 Merge branch 'master' of github.com:jesse-ai/jesse 2021-01-02 17:51:43 +01:00
Saleh Mir
ef31851558 rename unit test 2021-01-02 17:51:38 +01:00
Saleh Mir
e919a3a68b add dashy_symbo() helper 2021-01-02 17:43:40 +01:00
251 changed files with 5180 additions and 2638 deletions

25
.dockerignore Normal file
View File

@@ -0,0 +1,25 @@
__pycache__
*.pyc
*.pyo
*.pyd
.Python
env
pip-log.txt
pip-delete-this-directory.txt
.tox
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
*.log
.git
*.md
!README*.md
README-secret.md
.travis.yml
Dockerfile
docker-compose.yml
.idea
venv

155
.gitignore vendored
View File

@@ -1,20 +1,149 @@
/_pycache_
/.pytest_cache
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# IDE
/.vscode
/jesse.egg-info
/.idea
.DS_Store
/storage/*.key
/storage/*.sqlite
/storage/*.gz
.DS_Store
/.vagrant
*.pyc
/__pycache__
/venv
__pycache__
.vscode
/dist
/build
/*.egg-info
/*.egg
testing-*.py

28
Dockerfile Normal file
View File

@@ -0,0 +1,28 @@
FROM python:3.9.0
ENV PYTHONUNBUFFERED 1
RUN apt-get update \
&& apt-get -y install build-essential libssl-dev \
&& apt-get clean \
&& pip install --upgrade pip
RUN pip3 install Cython numpy
# Prepare environment
RUN mkdir /jesse-docker
WORKDIR /jesse-docker
# Install TA-lib
COPY docker_build_helpers/* /tmp/
RUN cd /tmp && /tmp/install_ta-lib.sh && rm -r /tmp/*ta-lib*
ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
# Install dependencies
COPY requirements.txt /jesse-docker
RUN pip3 install -r requirements.txt
# Build
COPY . /jesse-docker
RUN pip3 install -e .
WORKDIR /home

28
DockerfileTest Normal file
View File

@@ -0,0 +1,28 @@
FROM python:3.9.0
ENV PYTHONUNBUFFERED 1
RUN apt-get update \
&& apt-get -y install build-essential libssl-dev \
&& apt-get clean \
&& pip install --upgrade pip
RUN pip3 install Cython numpy codecov pytest-cov
# Prepare environment
RUN mkdir /jesse-docker
WORKDIR /jesse-docker
# Install TA-lib
COPY docker_build_helpers/* /tmp/
RUN cd /tmp && /tmp/install_ta-lib.sh && rm -r /tmp/*ta-lib*
ENV LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
# Install dependencies
COPY requirements.txt /jesse-docker
RUN pip3 install -r requirements.txt
# Build
COPY . /jesse-docker
RUN pip3 install -e .
ENTRYPOINT pytest --cov=./ # && codecov

View File

@@ -0,0 +1,17 @@
if [ -z "$1" ]; then
INSTALL_LOC=/usr/local
else
INSTALL_LOC=${1}
fi
echo "Installing to ${INSTALL_LOC}"
if [ ! -f "${INSTALL_LOC}/lib/libta_lib.a" ]; then
tar zxvf ta-lib-0.4.0-src.tar.gz
cd ta-lib \
&& sed -i.bak "s|0.00000001|0.000000000000000001 |g" src/ta_func/ta_utility.h \
&& ./configure --prefix=${INSTALL_LOC}/ \
&& make \
&& which sudo && sudo make install || make install \
&& echo "export LD_LIBRARY_PATH=/usr/local/lib" >> /root/.bashrc
else
echo "TA-lib already installed, skipping installation"
fi

Binary file not shown.

View File

@@ -1,5 +1,7 @@
import os
import sys
# Hide the "FutureWarning: pandas.util.testing is deprecated." caused by empyrical
import warnings
from pydoc import locate
import click
@@ -7,9 +9,6 @@ import pkg_resources
import jesse.helpers as jh
# Hide the "FutureWarning: pandas.util.testing is deprecated." caused by empyrical
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
# Python version validation.
@@ -28,7 +27,7 @@ ls = os.listdir('.')
is_jesse_project = 'strategies' in ls and 'config.py' in ls and 'storage' in ls and 'routes.py' in ls
def validate_cwd():
def validate_cwd() -> None:
"""
make sure we're in a Jesse project
"""
@@ -42,7 +41,7 @@ def validate_cwd():
os._exit(1)
def inject_local_config():
def inject_local_config() -> None:
"""
injects config from local config file
"""
@@ -51,7 +50,7 @@ def inject_local_config():
set_config(local_config)
def inject_local_routes():
def inject_local_routes() -> None:
"""
injects routes from local routes folder
"""
@@ -68,7 +67,7 @@ if is_jesse_project:
inject_local_routes()
def register_custom_exception_handler():
def register_custom_exception_handler() -> None:
"""
:return:
@@ -85,9 +84,11 @@ def register_custom_exception_handler():
os.makedirs('storage/logs', exist_ok=True)
if jh.is_livetrading():
logging.basicConfig(filename='storage/logs/live-trade.txt', level=logging.INFO, filemode='w', format=log_format)
logging.basicConfig(filename='storage/logs/live-trade.txt', level=logging.INFO,
filemode='w', format=log_format)
elif jh.is_paper_trading():
logging.basicConfig(filename='storage/logs/paper-trade.txt', level=logging.INFO, filemode='w',
logging.basicConfig(filename='storage/logs/paper-trade.txt', level=logging.INFO,
filemode='w',
format=log_format)
elif jh.is_collecting_data():
logging.basicConfig(filename='storage/logs/collect.txt', level=logging.INFO, filemode='w',
@@ -99,21 +100,14 @@ def register_custom_exception_handler():
logging.basicConfig(level=logging.INFO)
# main thread
def handle_exception(exc_type, exc_value, exc_traceback):
"""
:param exc_type:
:param exc_value:
:param exc_traceback:
:return:
"""
def handle_exception(exc_type, exc_value, exc_traceback) -> None:
if issubclass(exc_type, KeyboardInterrupt):
sys.excepthook(exc_type, exc_value, exc_traceback)
return
# handle Breaking exceptions
if exc_type in [
exceptions.ConfigException, exceptions.RouteNotFound, exceptions.InvalidRoutes,
exceptions.InvalidConfig, exceptions.RouteNotFound, exceptions.InvalidRoutes,
exceptions.CandleNotFoundInDatabase
]:
click.clear()
@@ -177,18 +171,13 @@ def register_custom_exception_handler():
# other threads
if jh.python_version() >= 3.8:
def handle_thread_exception(args):
"""
:param args:
:return:
"""
def handle_thread_exception(args) -> None:
if args.exc_type == SystemExit:
return
# handle Breaking exceptions
if args.exc_type in [
exceptions.ConfigException, exceptions.RouteNotFound, exceptions.InvalidRoutes,
exceptions.InvalidConfig, exceptions.RouteNotFound, exceptions.InvalidRoutes,
exceptions.CandleNotFoundInDatabase
]:
click.clear()
@@ -209,7 +198,8 @@ def register_custom_exception_handler():
)
if jh.is_live() or jh.is_collecting_data():
logging.error("Uncaught Exception:", exc_info=(args.exc_type, args.exc_value, args.exc_traceback))
logging.error("Uncaught Exception:",
exc_info=(args.exc_type, args.exc_value, args.exc_traceback))
else:
print('=' * 30 + ' EXCEPTION TRACEBACK:')
traceback.print_tb(args.exc_traceback, file=sys.stdout)
@@ -254,7 +244,7 @@ def register_custom_exception_handler():
# create a Click group
@click.group()
@click.version_option(pkg_resources.get_distribution("jesse").version)
def cli():
def cli() -> None:
pass
@@ -262,7 +252,9 @@ def cli():
@click.argument('exchange', required=True, type=str)
@click.argument('symbol', required=True, type=str)
@click.argument('start_date', required=True, type=str)
def import_candles(exchange, symbol, start_date):
@click.option('--skip-confirmation', is_flag=True,
help="Will prevent confirmation for skipping duplicates")
def import_candles(exchange: str, symbol: str, start_date: str, skip_confirmation: bool) -> None:
"""
imports historical candles from exchange
"""
@@ -276,7 +268,7 @@ def import_candles(exchange, symbol, start_date):
from jesse.modes import import_candles_mode
import_candles_mode.run(exchange, symbol, start_date)
import_candles_mode.run(exchange, symbol, start_date, skip_confirmation)
db.close_connection()
@@ -290,12 +282,14 @@ def import_candles(exchange, symbol, start_date):
help='Outputs a CSV file of all executed trades on completion.')
@click.option('--json/--no-json', default=False,
help='Outputs a JSON file of all executed trades on completion.')
@click.option('--fee/--no-fee', default=True, help='You can use "--no-fee" as a quick way to set trading fee to zero.')
@click.option('--fee/--no-fee', default=True,
help='You can use "--no-fee" as a quick way to set trading fee to zero.')
@click.option('--chart/--no-chart', default=False,
help='Generates charts of daily portfolio balance and assets price change. Useful for a visual comparision of your portfolio against the market.')
@click.option('--tradingview/--no-tradingview', default=False,
help="Generates an output that can be copy-and-pasted into tradingview.com's pine-editor too see the trades in their charts.")
def backtest(start_date, finish_date, debug, csv, json, fee, chart, tradingview):
def backtest(start_date: str, finish_date: str, debug: bool, csv: bool, json: bool, fee: bool, chart: bool,
tradingview: bool) -> None:
"""
backtest mode. Enter in "YYYY-MM-DD" "YYYY-MM-DD"
"""
@@ -319,7 +313,8 @@ def backtest(start_date, finish_date, debug, csv, json, fee, chart, tradingview)
config['env']['exchanges'][e]['fee'] = 0
get_exchange(e).fee = 0
backtest_mode.run(start_date, finish_date, chart=chart, tradingview=tradingview, csv=csv, json=json)
backtest_mode.run(start_date, finish_date, chart=chart, tradingview=tradingview, csv=csv,
json=json)
db.close_connection()
@@ -335,7 +330,10 @@ def backtest(start_date, finish_date, debug, csv, json, fee, chart, tradingview)
'--debug/--no-debug', default=False,
help='Displays detailed logs about the genetics algorithm. Use it if you are interested int he genetics algorithm.'
)
def optimize(start_date, finish_date, optimal_total, cpu, debug):
@click.option('--csv/--no-csv', default=False, help='Outputs a CSV file of all DNAs on completion.')
@click.option('--json/--no-json', default=False, help='Outputs a JSON file of all DNAs on completion.')
def optimize(start_date: str, finish_date: str, optimal_total: int, cpu: int, debug: bool, csv: bool,
json: bool) -> None:
"""
tunes the hyper-parameters of your strategy
"""
@@ -350,12 +348,12 @@ def optimize(start_date, finish_date, optimal_total, cpu, debug):
from jesse.modes.optimize_mode import optimize_mode
optimize_mode(start_date, finish_date, optimal_total, cpu)
optimize_mode(start_date, finish_date, optimal_total, cpu, csv, json)
@cli.command()
@click.argument('name', required=True, type=str)
def make_strategy(name):
def make_strategy(name: str) -> None:
"""
generates a new strategy folder from jesse/strategies/ExampleStrategy
"""
@@ -373,7 +371,7 @@ def make_strategy(name):
@cli.command()
@click.argument('name', required=True, type=str)
def make_project(name):
def make_project(name: str) -> None:
"""
generates a new strategy folder from jesse/strategies/ExampleStrategy
"""
@@ -389,8 +387,9 @@ def make_project(name):
@cli.command()
@click.option('--dna/--no-dna', default=False, help='Translates DNA into parameters. Used in optimize mode only')
def routes(dna):
@click.option('--dna/--no-dna', default=False,
help='Translates DNA into parameters. Used in optimize mode only')
def routes(dna: bool) -> None:
"""
lists all routes
"""
@@ -408,7 +407,7 @@ def routes(dna):
if 'plugins' in ls:
@cli.command()
def collect():
def collect() -> None:
"""
fetches streamed market data such as tickers, trades, and orderbook from
the WS connection and stores them into the database for later research.
@@ -431,7 +430,7 @@ if 'plugins' in ls:
@click.option('--debug/--no-debug', default=False)
@click.option('--dev/--no-dev', default=False)
@click.option('--fee/--no-fee', default=True)
def live(testdrive, debug, dev, fee):
def live(testdrive: bool, debug: bool, dev: bool, fee: bool) -> None:
"""
trades in real-time on exchange with REAL money
"""
@@ -468,7 +467,7 @@ if 'plugins' in ls:
@click.option('--debug/--no-debug', default=False)
@click.option('--dev/--no-dev', default=False)
@click.option('--fee/--no-fee', default=True)
def paper(debug, dev, fee):
def paper(debug: bool, dev: bool, fee: bool) -> None:
"""
trades in real-time on exchange with PAPER money
"""

View File

@@ -30,69 +30,139 @@ config = {
'Sandbox': {
'fee': 0,
'type': 'spot',
# used only in margin trading
# used only in futures trading
'settlement_currency': 'USDT',
# accepted values are: 'cross' and 'isolated'
'futures_leverage_mode': 'cross',
# 1x, 2x, 10x, 50x, etc. Enter as integers
'futures_leverage': 1,
'assets': [
{'asset': 'USDT', 'balance': 10000},
{'asset': 'USDT', 'balance': 10_000},
{'asset': 'BTC', 'balance': 0},
],
},
# https://www.bitfinex.com
'Bitfinex': {
'type': 'margin',
# used only in margin trading
'settlement_currency': 'USD',
'fee': 0.002,
# backtest mode only: accepted are 'spot' and 'futures'
'type': 'futures',
# futures mode only
'settlement_currency': 'USD',
# accepted values are: 'cross' and 'isolated'
'futures_leverage_mode': 'cross',
# 1x, 2x, 10x, 50x, etc. Enter as integers
'futures_leverage': 1,
# used for spot exchange only
'assets': [
{'asset': 'USDT', 'balance': 10000},
{'asset': 'USD', 'balance': 10000},
{'asset': 'USDT', 'balance': 10_000},
{'asset': 'USD', 'balance': 10_000},
{'asset': 'BTC', 'balance': 0},
],
},
# https://www.binance.com
'Binance': {
'type': 'spot',
# used only in margin trading
'settlement_currency': 'USDT',
'fee': 0.001,
# backtest mode only: accepted are 'spot' and 'futures'
'type': 'futures',
# futures mode only
'settlement_currency': 'USDT',
# accepted values are: 'cross' and 'isolated'
'futures_leverage_mode': 'cross',
# 1x, 2x, 10x, 50x, etc. Enter as integers
'futures_leverage': 1,
# used for spot exchange only
'assets': [
{'asset': 'USDT', 'balance': 10000},
{'asset': 'USDT', 'balance': 10_000},
{'asset': 'BTC', 'balance': 0},
],
},
# https://www.binance.com
# https://www.binance.com/en/futures/BTC_USDT
'Binance Futures': {
'type': 'margin',
# used only in margin trading
'fee': 0.0004,
# backtest mode only: accepted are 'spot' and 'futures'
'type': 'futures',
# futures mode only
'settlement_currency': 'USDT',
'fee': 0.0002,
# accepted values are: 'cross' and 'isolated'
'futures_leverage_mode': 'cross',
# 1x, 2x, 10x, 50x, etc. Enter as integers
'futures_leverage': 1,
# used for spot exchange only
'assets': [
{'asset': 'USDT', 'balance': 10000},
{'asset': 'USDT', 'balance': 10_000},
],
},
# https://www.binance.com/en/delivery/btcusd_perpetual
'Binance Inverse Futures': {
'fee': 0.0004,
# backtest mode only: accepted are 'spot' and 'futures'
'type': 'inverse futures',
# accepted values are: 'cross' and 'isolated'
'futures_leverage_mode': 'cross',
# 1x, 2x, 10x, 50x, etc. Enter as integers
'futures_leverage': 1,
# Price per contract, also called the contract-multiplier
'contract_size': 100,
'assets': [
{'asset': 'BTC', 'balance': 1},
{'asset': 'ETH', 'balance': 10},
],
},
# https://testnet.binancefuture.com
'Testnet Binance Futures': {
'type': 'margin',
# used only in margin trading
'fee': 0.0004,
# backtest mode only: accepted are 'spot' and 'futures'
'type': 'futures',
# futures mode only
'settlement_currency': 'USDT',
'fee': 0.0002,
# accepted values are: 'cross' and 'isolated'
'futures_leverage_mode': 'cross',
# 1x, 2x, 10x, 50x, etc. Enter as integers
'futures_leverage': 1,
# used for spot mode
'assets': [
{'asset': 'USDT', 'balance': 10000},
{'asset': 'USDT', 'balance': 10_000},
],
},
# https://pro.coinbase.com
'Coinbase': {
'type': 'spot',
# used only in margin trading
'settlement_currency': 'USDT',
'fee': 0.005,
# backtest mode only: accepted are 'spot' and 'futures'
'type': 'futures',
# futures mode only
'settlement_currency': 'USD',
# accepted values are: 'cross' and 'isolated'
'futures_leverage_mode': 'cross',
# 1x, 2x, 10x, 50x, etc. Enter as integers
'futures_leverage': 1,
# used for spot exchange only
'assets': [
{'asset': 'USDT', 'balance': 10000},
{'asset': 'USDT', 'balance': 10_000},
{'asset': 'USD', 'balance': 10_000},
{'asset': 'BTC', 'balance': 0},
],
},
@@ -131,7 +201,7 @@ config = {
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
'data': {
# The minimum number of warmup candles that is loaded before each session.
'warmup_candles_num': 210,
'warmup_candles_num': 240,
}
},
@@ -171,22 +241,24 @@ config = {
backup_config = config.copy()
def set_config(c):
def set_config(c) -> None:
global config
config['env'] = c
# add sandbox because it isn't in the local config file
config['env']['exchanges']['Sandbox'] = {
'type': 'spot',
# used only in margin trading
# used only in futures trading
'settlement_currency': 'USDT',
'fee': 0,
'futures_leverage_mode': 'cross',
'futures_leverage': 1,
'assets': [
{'asset': 'USDT', 'balance': 10000},
{'asset': 'USDT', 'balance': 10_000},
{'asset': 'BTC', 'balance': 0},
],
}
def reset_config():
def reset_config() -> None:
global config
config = backup_config.copy()

View File

@@ -21,13 +21,17 @@ class timeframes:
MINUTE_5 = '5m'
MINUTE_15 = '15m'
MINUTE_30 = '30m'
MINUTE_45 = '45m'
HOUR_1 = '1h'
HOUR_2 = '2h'
HOUR_3 = '3h'
HOUR_4 = '4h'
HOUR_6 = '6h'
HOUR_8 = '8h'
HOUR_12 = '12h'
DAY_1 = '1D'
DAY_3 = '3D'
WEEK_1 = '1W'
class colors:

View File

@@ -58,12 +58,17 @@ class InvalidShape(Exception):
pass
class ConfigException(Exception):
class InvalidConfig(Exception):
pass
class InvalidTimeframe(Exception):
pass
class NegativeBalance(Exception):
pass
class InsufficientMargin(Exception):
pass

View File

@@ -8,64 +8,24 @@ class Exchange(ABC):
@abstractmethod
def market_order(self, symbol, qty, current_price, side, role, flags):
"""
:param symbol:
:param qty:
:param current_price:
:param side:
:param role:
:param flags:
"""
pass
@abstractmethod
def limit_order(self, symbol, qty, price, side, role, flags):
"""
:param symbol:
:param qty:
:param price:
:param side:
:param role:
:param flags:
"""
pass
@abstractmethod
def stop_order(self, symbol, qty, price, side, role, flags):
"""
:param symbol:
:param qty:
:param price:
:param side:
:param role:
:param flags:
"""
pass
@abstractmethod
def cancel_all_orders(self, symbol):
"""
:param symbol:
"""
pass
@abstractmethod
def cancel_order(self, symbol, order_id):
"""
:param symbol:
:param order_id:
"""
pass
@abstractmethod
def get_exec_inst(self, flags):
"""
:param flags:
"""
pass

View File

@@ -5,6 +5,7 @@ import random
import string
import sys
import uuid
from typing import List, Tuple, Union, Any
import arrow
import click
@@ -13,21 +14,24 @@ import numpy as np
CACHED_CONFIG = dict()
def app_currency():
def app_currency() -> str:
from jesse.routes import router
return quote_asset(router.routes[0].symbol)
underlying = quote_asset(router.routes[0].symbol)
if underlying.upper() == 'PERP':
underlying = base_asset(router.routes[0].symbol)
return underlying
def app_mode():
def app_mode() -> str:
from jesse.config import config
return config['app']['trading_mode']
def arrow_to_timestamp(arrow_time):
def arrow_to_timestamp(arrow_time: arrow.arrow.Arrow) -> int:
return arrow_time.int_timestamp * 1000
def base_asset(symbol: str):
def base_asset(symbol: str) -> str:
return symbol.split('-')[0]
@@ -49,11 +53,16 @@ def binary_search(arr: list, item) -> int:
return -1
def clean_orderbook_list(arr):
def class_iter(Class):
return (value for variable, value in vars(Class).items() if
not callable(getattr(Class, variable)) and not variable.startswith("__"))
def clean_orderbook_list(arr) -> List[List[float]]:
return [[float(i[0]), float(i[1])] for i in arr]
def color(msg_text: str, msg_color: str):
def color(msg_text: str, msg_color: str) -> str:
if not msg_text:
return ''
@@ -77,7 +86,7 @@ def color(msg_text: str, msg_color: str):
raise ValueError('unsupported color')
def convert_number(old_max, old_min, new_max, new_min, old_value):
def convert_number(old_max: float, old_min: float, new_max: float, new_min: float, old_value: float) -> float:
"""
convert a number from one range (ex 40-119) to another
range (ex 0-30) while keeping the ratio.
@@ -92,15 +101,22 @@ def convert_number(old_max, old_min, new_max, new_min, old_value):
return new_value
def dashless_symbol(symbol):
def dashless_symbol(symbol: str) -> str:
return symbol.replace("-", "")
def date_diff_in_days(date1, date2):
def dashy_symbol(symbol: str) -> str:
return symbol[0:3] + '-' + symbol[3:]
def date_diff_in_days(date1: arrow.arrow.Arrow, date2: arrow.arrow.Arrow) -> int:
if type(date1) is not arrow.arrow.Arrow or type(
date2) is not arrow.arrow.Arrow:
raise TypeError('dates must be Arrow instances')
dif = date2 - date1
return abs(dif.days)
@@ -130,11 +146,10 @@ def dna_to_hp(strategy_hp, dna: str):
raise TypeError('Only int and float types are implemented')
hp[h['name']] = decoded_gene
return hp
def dump_exception():
def dump_exception() -> None:
"""
a useful debugging helper
"""
@@ -143,7 +158,8 @@ def dump_exception():
terminate_app()
def estimate_average_price(order_qty, order_price, current_qty, current_entry_price):
def estimate_average_price(order_qty: float, order_price: float, current_qty: float,
current_entry_price: float) -> float:
"""Estimates the new entry price for the position.
This is used after having a new order and updating the currently holding position.
@@ -160,7 +176,7 @@ def estimate_average_price(order_qty, order_price, current_qty, current_entry_pr
current_entry_price) / (abs(order_qty) + abs(current_qty))
def estimate_PNL(qty, entry_price, exit_price, trade_type, trading_fee=0):
def estimate_PNL(qty: float, entry_price: float, exit_price: float, trade_type: str, trading_fee: float = 0) -> float:
qty = abs(qty)
profit = qty * (exit_price - entry_price)
@@ -172,7 +188,7 @@ def estimate_PNL(qty, entry_price, exit_price, trade_type, trading_fee=0):
return profit - fee
def estimate_PNL_percentage(qty, entry_price, exit_price, trade_type):
def estimate_PNL_percentage(qty: float, entry_price: float, exit_price: float, trade_type: str) -> float:
qty = abs(qty)
profit = qty * (exit_price - entry_price)
@@ -186,20 +202,24 @@ def file_exists(path: str) -> bool:
return os.path.isfile(path)
def floor_with_precision(num, precision=0):
def floor_with_precision(num: float, precision: int = 0) -> float:
temp = 10 ** precision
return math.floor(num * temp) / temp
def format_currency(num):
def format_currency(num: float) -> str:
return f'{num:,}'
def generate_unique_id():
def generate_unique_id() -> str:
return str(uuid.uuid4())
def get_candle_source(candles: np.ndarray, source_type="close") -> np.ndarray:
def get_arrow(timestamp: int) -> arrow.arrow.Arrow:
return timestamp_to_arrow(timestamp)
def get_candle_source(candles: np.ndarray, source_type: str = "close") -> np.ndarray:
"""
Returns the candles corresponding the selected type.
@@ -228,7 +248,7 @@ def get_candle_source(candles: np.ndarray, source_type="close") -> np.ndarray:
raise ValueError('type string not recognised')
def get_config(keys: str, default=None):
def get_config(keys: str, default: Any = None) -> Any:
"""
Gets keys as a single string separated with "." and returns value.
Also accepts a default value so that the app would work even if
@@ -242,16 +262,19 @@ def get_config(keys: str, default=None):
if not str:
raise ValueError('keys string cannot be empty')
if not keys in CACHED_CONFIG:
from functools import reduce
from jesse.config import config
CACHED_CONFIG[keys] = reduce(lambda d, k: d.get(k, default) if isinstance(d, dict) else default,
keys.split("."), config)
if is_unit_testing() or not keys in CACHED_CONFIG:
if os.environ.get(keys.upper().replace(".", "_")) is not None:
CACHED_CONFIG[keys] = os.environ.get(keys.upper().replace(".", "_"))
else:
from functools import reduce
from jesse.config import config
CACHED_CONFIG[keys] = reduce(lambda d, k: d.get(k, default) if isinstance(d, dict) else default,
keys.split("."), config)
return CACHED_CONFIG[keys]
def get_strategy_class(strategy_name):
def get_strategy_class(strategy_name: str):
from pydoc import locate
if is_unit_testing():
@@ -264,7 +287,7 @@ def insecure_hash(msg: str) -> str:
return hashlib.md5(msg.encode()).hexdigest()
def insert_list(index: int, item, arr: list):
def insert_list(index: int, item, arr: list) -> list:
"""
helper to insert an item in a Python List without removing the item
"""
@@ -274,71 +297,85 @@ def insert_list(index: int, item, arr: list):
return arr[:index] + [item] + arr[index:]
def is_backtesting():
def is_backtesting() -> bool:
from jesse.config import config
return config['app']['trading_mode'] == 'backtest'
def is_collecting_data():
def is_collecting_data() -> bool:
from jesse.config import config
return config['app']['trading_mode'] == 'collect'
def is_debuggable(debug_item):
def is_debuggable(debug_item) -> bool:
from jesse.config import config
return is_debugging() and config['env']['logging'][debug_item]
def is_debugging():
def is_debugging() -> bool:
from jesse.config import config
return config['app']['debug_mode']
def is_importing_candles():
def is_importing_candles() -> bool:
from jesse.config import config
return config['app']['trading_mode'] == 'import-candles'
def is_live():
def is_live() -> bool:
return is_livetrading() or is_paper_trading()
def is_livetrading():
def is_livetrading() -> bool:
from jesse.config import config
return config['app']['trading_mode'] == 'livetrade'
def is_optimizing():
def is_optimizing() -> bool:
from jesse.config import config
return config['app']['trading_mode'] == 'optimize'
def is_paper_trading():
def is_paper_trading() -> bool:
from jesse.config import config
return config['app']['trading_mode'] == 'papertrade'
def is_test_driving():
def is_test_driving() -> bool:
from jesse.config import config
return config['app']['is_test_driving']
def is_unit_testing():
def is_unit_testing() -> bool:
return "pytest" in sys.modules
def key(exchange, symbol, timeframe=None):
def is_valid_uuid(uuid_to_test, version: int = 4) -> bool:
try:
uuid_obj = uuid.UUID(uuid_to_test, version=version)
except ValueError:
return False
return str(uuid_obj) == uuid_to_test
def key(exchange: str, symbol: str, timeframe: str = None):
if timeframe is None:
return '{}-{}'.format(exchange, symbol)
return '{}-{}-{}'.format(exchange, symbol, timeframe)
def max_timeframe(timeframes_list):
def max_timeframe(timeframes_list: list) -> str:
from jesse.enums import timeframes
if timeframes.WEEK_1 in timeframes_list:
return timeframes.WEEK_1
if timeframes.DAY_3 in timeframes_list:
return timeframes.DAY_3
if timeframes.DAY_1 in timeframes_list:
return timeframes.DAY_1
if timeframes.HOUR_12 in timeframes_list:
return timeframes.HOUR_12
if timeframes.HOUR_8 in timeframes_list:
return timeframes.HOUR_8
if timeframes.HOUR_6 in timeframes_list:
@@ -351,6 +388,8 @@ def max_timeframe(timeframes_list):
return timeframes.HOUR_2
if timeframes.HOUR_1 in timeframes_list:
return timeframes.HOUR_1
if timeframes.MINUTE_45 in timeframes_list:
return timeframes.MINUTE_45
if timeframes.MINUTE_30 in timeframes_list:
return timeframes.MINUTE_30
if timeframes.MINUTE_15 in timeframes_list:
@@ -363,7 +402,7 @@ def max_timeframe(timeframes_list):
return timeframes.MINUTE_1
def normalize(x, x_min, x_max):
def normalize(x: float, x_min: float, x_max: float) -> float:
"""
Rescaling data to have values between 0 and 1
"""
@@ -371,7 +410,11 @@ def normalize(x, x_min, x_max):
return x_new
def now_to_timestamp():
def now() -> int:
return now_to_timestamp()
def now_to_timestamp() -> int:
if not (is_live() or is_collecting_data() or is_importing_candles()):
from jesse.store import store
return store.app.time
@@ -379,11 +422,18 @@ def now_to_timestamp():
return arrow.utcnow().int_timestamp * 1000
def now():
return now_to_timestamp()
def np_ffill(arr: np.ndarray, axis: int = 0) -> np.ndarray:
idx_shape = tuple([slice(None)] + [np.newaxis] * (len(arr.shape) - axis - 1))
idx = np.where(~np.isnan(arr), np.arange(arr.shape[axis])[idx_shape], 0)
np.maximum.accumulate(idx, axis=axis, out=idx)
slc = [np.arange(k)[tuple([slice(None) if dim == i else np.newaxis
for dim in range(len(arr.shape))])]
for i, k in enumerate(arr.shape)]
slc[axis] = idx
return arr[tuple(slc)]
def np_shift(arr: np.ndarray, num: int, fill_value=0):
def np_shift(arr: np.ndarray, num: int, fill_value=0) -> np.ndarray:
result = np.empty_like(arr)
if num > 0:
@@ -398,7 +448,7 @@ def np_shift(arr: np.ndarray, num: int, fill_value=0):
return result
def opposite_side(s):
def opposite_side(s: str) -> str:
from jesse.enums import sides
if s == sides.BUY:
@@ -408,7 +458,7 @@ def opposite_side(s):
raise ValueError('unsupported side')
def opposite_type(t):
def opposite_type(t: str) -> str:
from jesse.enums import trade_types
if t == trade_types.LONG:
@@ -418,7 +468,7 @@ def opposite_type(t):
raise ValueError('unsupported type')
def orderbook_insertion_index_search(arr, target, ascending=True):
def orderbook_insertion_index_search(arr, target: int, ascending: bool = True) -> Tuple[bool, int]:
target = target[0]
lower = 0
upper = len(arr)
@@ -453,14 +503,7 @@ def orderbook_insertion_index_search(arr, target, ascending=True):
upper = x
def orderbook_trim_price(p: float, ascending: bool, unit: float):
"""
:param p:
:param ascending:
:param unit:
:return:
"""
def orderbook_trim_price(p: float, ascending: bool, unit: float) -> float:
if ascending:
trimmed = np.ceil(p / unit) * unit
if math.log10(unit) < 0:
@@ -473,7 +516,7 @@ def orderbook_trim_price(p: float, ascending: bool, unit: float):
return p if trimmed == p - unit else trimmed
def prepare_qty(qty, side):
def prepare_qty(qty: float, side: str) -> float:
if side.lower() in ('sell', 'short'):
return -abs(qty)
@@ -487,7 +530,7 @@ def python_version() -> float:
return float('{}.{}'.format(sys.version_info[0], sys.version_info[1]))
def quote_asset(symbol: str):
def quote_asset(symbol: str) -> str:
try:
return symbol.split('-')[1]
except IndexError:
@@ -495,11 +538,11 @@ def quote_asset(symbol: str):
raise InvalidRoutes("The symbol format is incorrect. Correct example: 'BTC-USDT'. Yours is '{}'".format(symbol))
def random_str(num_characters=8):
def random_str(num_characters: int = 8) -> str:
return ''.join(random.choice(string.ascii_letters) for i in range(num_characters))
def readable_duration(seconds, granularity=2):
def readable_duration(seconds: int, granularity: int = 2) -> str:
intervals = (
('weeks', 604800), # 60 * 60 * 24 * 7
('days', 86400), # 60 * 60 * 24
@@ -509,6 +552,7 @@ def readable_duration(seconds, granularity=2):
)
result = []
seconds = int(seconds)
for name, count in intervals:
value = seconds // count
@@ -524,12 +568,12 @@ def relative_to_absolute(path: str) -> str:
return os.path.abspath(path)
def round_price_for_live_mode(price, roundable_price):
def round_price_for_live_mode(price: float, roundable_price: float) -> Union[float, np.array]:
"""
Rounds price(s) based on exchange requirements
:param price: float
:param roundable_price: float | nd.array
:param roundable_price: float
:return: float | nd.array
"""
n = int(math.log10(price))
@@ -544,7 +588,7 @@ def round_price_for_live_mode(price, roundable_price):
return np.round(roundable_price, price_round_precision)
def round_qty_for_live_mode(price, roundable_qty):
def round_qty_for_live_mode(price: float, roundable_qty: float) -> Union[float, np.array]:
"""
Rounds qty(s) based on exchange requirements
@@ -560,8 +604,13 @@ def round_qty_for_live_mode(price, roundable_qty):
qty_round_precision = n + 1
if qty_round_precision > 3:
qty_round_precision = 3
rounded = np.round(roundable_qty, qty_round_precision)
return np.round(roundable_qty, qty_round_precision)
for index, q in enumerate(rounded):
if q == 0.0:
rounded[index] = 0.001
return rounded
def secure_hash(msg: str) -> str:
@@ -572,7 +621,7 @@ def should_execute_silently() -> bool:
return is_optimizing() or is_unit_testing()
def side_to_type(s):
def side_to_type(s: str) -> str:
from jesse.enums import trade_types, sides
if s == sides.BUY:
@@ -582,14 +631,14 @@ def side_to_type(s):
raise ValueError
def string_after_character(string: str, character: str):
def string_after_character(string: str, character: str) -> str:
try:
return string.split(character, 1)[1]
except IndexError:
return None
def style(msg_text: str, msg_style: str):
def style(msg_text: str, msg_style: str) -> str:
if msg_style is None:
return msg_text
@@ -602,7 +651,7 @@ def style(msg_text: str, msg_style: str):
raise ValueError('unsupported style')
def terminate_app():
def terminate_app() -> None:
# close the database
from jesse.services.db import close_connection
close_connection()
@@ -610,9 +659,10 @@ def terminate_app():
os._exit(1)
def timeframe_to_one_minutes(timeframe):
def timeframe_to_one_minutes(timeframe: str) -> int:
from jesse.enums import timeframes
from jesse.exceptions import InvalidTimeframe
all_timeframes = [timeframe for timeframe in class_iter(timeframes)]
dic = {
timeframes.MINUTE_1: 1,
@@ -620,36 +670,36 @@ def timeframe_to_one_minutes(timeframe):
timeframes.MINUTE_5: 5,
timeframes.MINUTE_15: 15,
timeframes.MINUTE_30: 30,
timeframes.MINUTE_45: 45,
timeframes.HOUR_1: 60,
timeframes.HOUR_2: 60 * 2,
timeframes.HOUR_3: 60 * 3,
timeframes.HOUR_4: 60 * 4,
timeframes.HOUR_6: 60 * 6,
timeframes.HOUR_8: 60 * 8,
timeframes.HOUR_12: 60 * 12,
timeframes.DAY_1: 60 * 24,
timeframes.DAY_3: 60 * 24 * 3,
timeframes.WEEK_1: 60 * 24 * 7,
}
try:
return dic[timeframe]
except KeyError:
raise InvalidTimeframe(
'Timeframe "{}" is invalid. Supported timeframes are 1m, 3m, 5m, 15m, 30m, 1h, 2h, 3h, 4h, 6h, 8h, 1D'.format(
timeframe))
'Timeframe "{}" is invalid. Supported timeframes are {}.'.format(
timeframe, ', '.join(all_timeframes)))
def timestamp_to_arrow(timestamp):
def timestamp_to_arrow(timestamp: int) -> arrow.arrow.Arrow:
return arrow.get(timestamp / 1000)
def get_arrow(timestamp):
return timestamp_to_arrow(timestamp)
def timestamp_to_date(timestamp: int) -> str:
return str(arrow.get(timestamp / 1000))[:10]
def timestamp_to_time(timestamp):
def timestamp_to_time(timestamp: int) -> str:
return str(arrow.get(timestamp / 1000))
@@ -662,7 +712,7 @@ def today_to_timestamp() -> int:
return arrow.utcnow().floor('day').int_timestamp * 1000
def type_to_side(t):
def type_to_side(t: str) -> str:
from jesse.enums import trade_types, sides
if t == trade_types.LONG:

View File

@@ -16,32 +16,38 @@ from .bollinger_bands_width import bollinger_bands_width
from .bop import bop
from .cc import cc
from .cci import cci
from .cfo import cfo
from .cg import cg
from .chande import chande
from .cmo import cmo
from .correlation_cycle import correlation_cycle
from .correl import correl
from .correlation_cycle import correlation_cycle
from .cvi import cvi
from .damiani_volatmeter import damiani_volatmeter
from .dec_osc import dec_osc
from .decycler import decycler
from .dema import dema
from .devstop import devstop
from .di import di
from .dm import dm
from .dx import dx
from .donchian import donchian
from .dpo import dpo
from .dti import dti
from .dx import dx
from .efi import efi
from .ema import ema
from .emd import emd
from .emv import emv
from .er import er
from .fisher import fisher
from .fosc import fosc
from .frama import frama
from .fwma import fwma
from .gatorosc import gatorosc
from .gauss import gauss
from .hma import hma
from .high_pass import high_pass
from .high_pass_2_pole import high_pass_2_pole
from .hma import hma
from .ht_dcperiod import ht_dcperiod
from .ht_dcphase import ht_dcphase
from .ht_phasor import ht_phasor
@@ -52,6 +58,7 @@ from .ichimoku_cloud import ichimoku_cloud
from .ichimoku_cloud_seq import ichimoku_cloud_seq
from .itrend import itrend
from .kama import kama
from .kaufmanstop import kaufmanstop
from .keltner import keltner
from .kst import kst
from .kvo import kvo
@@ -63,9 +70,9 @@ from .lrsi import lrsi
from .macd import macd
from .macdext import macdext
from .mama import mama
from .marketfi import marketfi
from .mass import mass
from .mcginley_dynamic import mcginley_dynamic
from .marketfi import marketfi
from .medprice import medprice
from .mfi import mfi
from .midpoint import midpoint
@@ -83,20 +90,27 @@ from .pvi import pvi
from .qstick import qstick
from .reflex import reflex
from .roc import roc
from .roofing import roofing
from .rocp import rocp
from .rocr import rocr
from .rocr100 import rocr100
from .roofing import roofing
from .rsi import rsi
from .rsmk import rsmk
from .rsx import rsx
from .rvi import rvi
from .safezonestop import safezonestop
from .sar import sar
from .sarext import sarext
from .sinwma import sinwma
from .sma import sma
from .smma import smma
from .srsi import srsi
from .stc import stc
from .stddev import stddev
from .stochastic import stoch
from .stochf import stochf
from .supersmoother import supersmoother
from .supersmoother_3_pole import supersmoother_3_pole
from .supertrend import supertrend
from .t3 import t3
from .tema import tema
@@ -111,12 +125,13 @@ from .ultosc import ultosc
from .var import var
from .vi import vi
from .vidya import vidya
from .vosc import vosc
from .voss import voss
from .vpci import vpci
from .vpt import vpt
from .vwma import vwma
from .vwmacd import vwmacd
from .vosc import vosc
from .voss import voss
from .vwap import vwap
from .wad import wad
from .wclprice import wclprice
from .wilders import wilders

View File

@@ -3,10 +3,12 @@ from collections import namedtuple
import numpy as np
import talib
from jesse.helpers import get_config
AC = namedtuple('AC', ['osc', 'change'])
def acosc(candles: np.ndarray, sequential=False) -> AC:
def acosc(candles: np.ndarray, sequential: bool = False) -> AC:
"""
Acceleration / Deceleration Oscillator (AC)
@@ -15,8 +17,9 @@ def acosc(candles: np.ndarray, sequential=False) -> AC:
:return: AC(osc, change)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
med = talib.MEDPRICE(candles[:, 3], candles[:, 4])
ao = talib.SMA(med, 5) - talib.SMA(med, 34)

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def ad(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
def ad(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
"""
AD - Chaikin A/D Line
@@ -13,8 +15,9 @@ def ad(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.AD(candles[:, 3], candles[:, 4], candles[:, 2], candles[:, 5])

View File

@@ -3,23 +3,27 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def adosc(candles: np.ndarray, fastperiod=3, slowperiod=10, sequential=False) -> Union[float, np.ndarray]:
def adosc(candles: np.ndarray, fast_period: int = 3, slow_period: int = 10, sequential: bool = False) -> Union[
float, np.ndarray]:
"""
ADOSC - Chaikin A/D Oscillator
:param candles: np.ndarray
:param fastperiod: int - default: 3
:param slowperiod: int - default: 10
:param fast_period: int - default: 3
:param slow_period: int - default: 10
:param sequential: bool - default=False
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.ADOSC(candles[:, 3], candles[:, 4], candles[:, 2], candles[:, 5], fastperiod=fastperiod,
slowperiod=slowperiod)
res = talib.ADOSC(candles[:, 3], candles[:, 4], candles[:, 2], candles[:, 5], fastperiod=fast_period,
slowperiod=slow_period)
if sequential:
return res

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def adx(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
def adx(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
"""
ADX - Average Directional Movement Index
@@ -14,8 +16,9 @@ def adx(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nda
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.ADX(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def adxr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
def adxr(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
"""
ADXR - Average Directional Movement Index Rating
@@ -14,8 +16,9 @@ def adxr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nd
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.ADXR(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)

View File

@@ -3,11 +3,12 @@ from collections import namedtuple
import numpy as np
from jesse.helpers import get_candle_source, np_shift
from jesse.helpers import get_config
AG = namedtuple('AG', ['jaw', 'teeth', 'lips'])
def alligator(candles: np.ndarray, source_type="close", sequential=False) -> AG:
def alligator(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> AG:
"""
Alligator
@@ -17,8 +18,9 @@ def alligator(candles: np.ndarray, source_type="close", sequential=False) -> AG:
:return: AG(jaw, teeth, lips)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
@@ -32,7 +34,7 @@ def alligator(candles: np.ndarray, source_type="close", sequential=False) -> AG:
return AG(jaw[-1], teeth[-1], lips[-1])
def numpy_ewma(data, window):
def numpy_ewma(data: np.ndarray, window: int):
"""
:param data:

View File

@@ -3,10 +3,12 @@ from collections import namedtuple
import numpy as np
import talib
from jesse.helpers import get_config
AO = namedtuple('AO', ['osc', 'change'])
def ao(candles: np.ndarray, sequential=False) -> AO:
def ao(candles: np.ndarray, sequential: bool = False) -> AO:
"""
Awesome Oscillator
@@ -15,8 +17,9 @@ def ao(candles: np.ndarray, sequential=False) -> AO:
:return: AO(osc, change)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
med = talib.MEDPRICE(candles[:, 3], candles[:, 4])
res = talib.SMA(med, 5) - talib.SMA(med, 34)

View File

@@ -4,27 +4,29 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def apo(candles: np.ndarray, fastperiod=12, slowperiod=26, matype=0, source_type="close", sequential=False) -> Union[
float, np.ndarray]:
def apo(candles: np.ndarray, fast_period: int = 12, slow_period: int = 26, matype: int = 0, source_type: str = "close",
sequential: bool = False) -> Union[float, np.ndarray]:
"""
APO - Absolute Price Oscillator
:param candles: np.ndarray
:param fastperiod: int - default: 12
:param slowperiod: int - default: 26
:param fast_period: int - default: 12
:param slow_period: int - default: 26
:param matype: int - default: 0
:param source_type: str - default: "close"
:param sequential: bool - default=False
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.APO(source, fastperiod=fastperiod, slowperiod=slowperiod, matype=matype)
res = talib.APO(source, fastperiod=fast_period, slowperiod=slow_period, matype=matype)
return res if sequential else res[-1]

View File

@@ -3,10 +3,12 @@ from collections import namedtuple
import numpy as np
import talib
from jesse.helpers import get_config
AROON = namedtuple('AROON', ['down', 'up'])
def aroon(candles: np.ndarray, period=14, sequential=False) -> AROON:
def aroon(candles: np.ndarray, period: int = 14, sequential: bool = False) -> AROON:
"""
AROON - Aroon
@@ -16,8 +18,9 @@ def aroon(candles: np.ndarray, period=14, sequential=False) -> AROON:
:return: AROON(down, up)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
aroondown, aroonup = talib.AROON(candles[:, 3], candles[:, 4], timeperiod=period)

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def aroonosc(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
def aroonosc(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
"""
AROONOSC - Aroon Oscillator
@@ -14,8 +16,9 @@ def aroonosc(candles: np.ndarray, period=14, sequential=False) -> Union[float, n
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.AROONOSC(candles[:, 3], candles[:, 4], timeperiod=period)

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def atr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
def atr(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
"""
ATR - Average True Range
@@ -14,8 +16,9 @@ def atr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nda
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.ATR(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def avgprice(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
def avgprice(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
"""
AVGPRICE - Average Price
@@ -13,8 +15,9 @@ def avgprice(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.AVGPRICE(candles[:, 1], candles[:, 3], candles[:, 4], candles[:, 2])

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def beta(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.ndarray]:
def beta(candles: np.ndarray, period: int = 5, sequential: bool = False) -> Union[float, np.ndarray]:
"""
BETA - Beta
@@ -14,8 +16,9 @@ def beta(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.nda
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.BETA(candles[:, 3], candles[:, 4], timeperiod=period)

View File

@@ -4,12 +4,14 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
BollingerBands = namedtuple('BollingerBands', ['upperband', 'middleband', 'lowerband'])
def bollinger_bands(candles: np.ndarray, period=20, devup=2, devdn=2, matype=0, source_type="close",
sequential=False) -> BollingerBands:
def bollinger_bands(candles: np.ndarray, period: int = 20, devup: float = 2, devdn: float = 2, matype: int = 0,
source_type: str = "close",
sequential: bool = False) -> BollingerBands:
"""
BBANDS - Bollinger Bands
@@ -23,8 +25,9 @@ def bollinger_bands(candles: np.ndarray, period=20, devup=2, devdn=2, matype=0,
:return: BollingerBands(upperband, middleband, lowerband)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
upperbands, middlebands, lowerbands = talib.BBANDS(source, timeperiod=period, nbdevup=devup, nbdevdn=devdn,

View File

@@ -4,10 +4,12 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def bollinger_bands_width(candles: np.ndarray, period=20, devup=2, devdn=2, matype=0, source_type="close",
sequential=False) -> Union[float, np.ndarray]:
def bollinger_bands_width(candles: np.ndarray, period: int = 20, devup: float = 2, devdn: float = 2, matype: int = 0,
source_type: str = "close",
sequential: bool = False) -> Union[float, np.ndarray]:
"""
BBW - Bollinger Bands Width - Bollinger Bands Bandwidth
@@ -21,8 +23,9 @@ def bollinger_bands_width(candles: np.ndarray, period=20, devup=2, devdn=2, maty
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
upperbands, middlebands, lowerbands = talib.BBANDS(source, timeperiod=period, nbdevup=devup, nbdevdn=devdn,

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def bop(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
def bop(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
"""
BOP - Balance Of Power
@@ -13,8 +15,9 @@ def bop(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.BOP(candles[:, 1], candles[:, 3], candles[:, 4], candles[:, 2])

View File

@@ -4,9 +4,12 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def cc(candles: np.ndarray, wma_period=10, roc_short_period=11, roc_long_period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def cc(candles: np.ndarray, wma_period: int = 10, roc_short_period: int = 11, roc_long_period: int = 14,
source_type: str = "close",
sequential: bool = False) -> Union[float, np.ndarray]:
"""
CC - Coppock Curve
@@ -19,10 +22,12 @@ def cc(candles: np.ndarray, wma_period=10, roc_short_period=11, roc_long_period=
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.WMA(talib.ROC(source, timeperiod=roc_long_period) + talib.ROC(source, timeperiod=roc_short_period), timeperiod=wma_period)
res = talib.WMA(talib.ROC(source, timeperiod=roc_long_period) + talib.ROC(source, timeperiod=roc_short_period),
timeperiod=wma_period)
return res if sequential else res[-1]

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def cci(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
def cci(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
"""
CCI - Commodity Channel Index
@@ -14,8 +16,9 @@ def cci(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nda
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.CCI(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)

35
jesse/indicators/cfo.py Normal file
View File

@@ -0,0 +1,35 @@
from typing import Union
import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def cfo(candles: np.ndarray, period: int = 14, scalar: float = 100, source_type: str = "close",
sequential: bool = False) -> Union[
float, np.ndarray]:
"""
CFO - Chande Forcast Oscillator
:param candles: np.ndarray
:param period: int - default=14
:param source_type: str - default: "close"
:param sequential: bool - default=False
:return: float | np.ndarray
"""
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
cfo = scalar * (source - talib.LINEARREG(source, timeperiod=period))
cfo /= source
if sequential:
return cfo
else:
return None if np.isnan(cfo[-1]) else cfo[-1]

46
jesse/indicators/cg.py Normal file
View File

@@ -0,0 +1,46 @@
from typing import Union
import numpy as np
from numba import njit
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def cg(candles: np.ndarray, period: int = 10, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
Center of Gravity (CG)
:param candles: np.ndarray
:param period: int - default: 10
:param source_type: str - default: "close"
:param sequential: bool - default=False
:return: float | np.ndarray
"""
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = go_fast(source, period)
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]
@njit
def go_fast(source, period): # Function is compiled to machine code when called the first time
res = np.full_like(source, fill_value=np.nan)
for i in range(0, len(source)):
if i > period:
num = 0
denom = 0
for count in range(0, period - 1):
close = source[i - count]
if not np.isnan(close):
num = num + (1 + count) * close
denom = denom + close
result = -num / denom if denom != 0 else 0
res[i] = result
return res

View File

@@ -4,8 +4,11 @@ import numpy as np
import talib
from scipy.ndimage.filters import maximum_filter1d, minimum_filter1d
from jesse.helpers import get_config
def chande(candles: np.ndarray, period=22, mult=3.0, direction="long", sequential=False) -> Union[float, np.ndarray]:
def chande(candles: np.ndarray, period: int = 22, mult: float = 3.0, direction: str = "long",
sequential: bool = False) -> Union[float, np.ndarray]:
"""
Chandelier Exits
@@ -17,8 +20,9 @@ def chande(candles: np.ndarray, period=22, mult=3.0, direction="long", sequentia
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
candles_close = candles[:, 2]
candles_high = candles[:, 3]
@@ -38,7 +42,7 @@ def chande(candles: np.ndarray, period=22, mult=3.0, direction="long", sequentia
return result if sequential else result[-1]
def filter1d_same(a, W, type, fillna=np.nan):
def filter1d_same(a: np.ndarray, W: int, type: str, fillna=np.nan):
out_dtype = np.full(0, fillna).dtype
hW = (W - 1) // 2 # Half window size
if type == 'max':

View File

@@ -4,9 +4,11 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def cmo(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def cmo(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
CMO - Chande Momentum Oscillator
@@ -17,8 +19,9 @@ def cmo(candles: np.ndarray, period=14, source_type="close", sequential=False) -
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.CMO(source, timeperiod=period)

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def correl(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.ndarray]:
def correl(candles: np.ndarray, period: int = 5, sequential: bool = False) -> Union[float, np.ndarray]:
"""
CORREL - Pearson's Correlation Coefficient (r)
@@ -14,8 +16,9 @@ def correl(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.n
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.CORREL(candles[:, 3], candles[:, 4], timeperiod=period)

View File

@@ -1,14 +1,16 @@
import math
from collections import namedtuple
import numpy as np
from numba import njit
from jesse.helpers import get_candle_source, np_shift
from jesse.helpers import get_config
CC = namedtuple('CC', ['real', 'imag', 'angle', 'state'])
def correlation_cycle(candles: np.ndarray, period=20, threshold=9, source_type="close", sequential=False) -> CC:
def correlation_cycle(candles: np.ndarray, period: int = 20, threshold: int = 9, source_type: str = "close",
sequential: bool = False) -> CC:
"""
"Correlation Cycle, Correlation Angle, Market State - John Ehlers
@@ -20,13 +22,30 @@ def correlation_cycle(candles: np.ndarray, period=20, threshold=9, source_type="
:return: CC(real, imag)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
realPart, imagPart, angle = go_fast(source, period, threshold)
priorAngle = np_shift(angle, 1, fill_value=np.nan)
angle = np.where(np.logical_and(priorAngle > angle, priorAngle - angle < 270.0), priorAngle, angle)
# Market State Function
state = np.where(np.abs(angle - priorAngle) < threshold, np.where(angle >= 0.0, 1, np.where(angle < 0.0, -1, 0)), 0)
if sequential:
return CC(realPart, imagPart, angle, state)
else:
return CC(realPart[-1], imagPart[-1], angle[-1], state[-1])
@njit
def go_fast(source, period, threshold): # Function is compiled to machine code when called the first time
# Correlation Cycle Function
PIx2 = 4.0 * math.asin(1.0)
PIx2 = 4.0 * np.arcsin(1.0)
period = max(2, period)
realPart = np.full_like(source, np.nan)
@@ -75,16 +94,9 @@ def correlation_cycle(candles: np.ndarray, period=20, threshold=9, source_type="
imagPart[i] = (period * Ixy - Ix * Iy) / np.sqrt(temp_1 * temp_2)
# Correlation Angle Phasor
HALF_OF_PI = math.asin(1.0)
HALF_OF_PI = np.arcsin(1.0)
angle = np.where(imagPart == 0, 0.0, np.degrees(np.arctan(realPart / imagPart) + HALF_OF_PI))
angle = np.where(imagPart > 0.0, angle - 180.0, angle)
priorAngle = np_shift(angle, 1, fill_value=np.nan)
angle = np.where(np.logical_and(priorAngle > angle, priorAngle - angle < 270.0), priorAngle, angle)
# Market State Function
state = np.where(np.abs(angle - priorAngle) < threshold, np.where(angle >= 0.0, 1, np.where(angle < 0.0, -1, 0)), 0)
if sequential:
return CC(realPart, imagPart, angle, state)
else:
return CC(realPart[-1], imagPart[-1], angle[-1], state[-1])
return realPart, imagPart, angle

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import tulipy as ti
from jesse.helpers import get_config
def cvi(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.ndarray]:
def cvi(candles: np.ndarray, period: int = 5, sequential: bool = False) -> Union[float, np.ndarray]:
"""
CVI - Chaikins Volatility
@@ -14,8 +16,9 @@ def cvi(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.ndar
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = ti.cvi(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]), period=period)

View File

@@ -1,14 +1,18 @@
import talib
from collections import namedtuple
import numpy as np
import talib
from numba import njit
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
DamianiVolatmeter = namedtuple('DamianiVolatmeter', ['vol', 'anti' ])
DamianiVolatmeter = namedtuple('DamianiVolatmeter', ['vol', 'anti'])
def damiani_volatmeter(candles: np.ndarray, vis_atr=13, vis_std=20, sed_atr=40, sed_std=100, threshold=1.4, source_type="close", sequential=False) -> DamianiVolatmeter:
def damiani_volatmeter(candles: np.ndarray, vis_atr: int = 13, vis_std: int = 20, sed_atr: int = 40, sed_std: int = 100,
threshold: float = 1.4, source_type: str = "close",
sequential: bool = False) -> DamianiVolatmeter:
"""
Damiani Volatmeter
@@ -24,26 +28,33 @@ def damiani_volatmeter(candles: np.ndarray, vis_atr=13, vis_std=20, sed_atr=40,
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
lag_s = 0.5
vol = np.full_like(source, 0)
t = np.full_like(source, 0)
atrvis = talib.ATR(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=vis_atr)
atrsed = talib.ATR(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=sed_atr)
for i in range(source.shape[0]):
if not (i < sed_std):
vol[i]=atrvis[i] / atrsed[i] + lag_s * (vol[i - 1] - vol[i - 3])
anti_thres = np.std(source[i - vis_std:i]) / np.std(source[i - sed_std:i])
t[i] = threshold - anti_thres
vol, t = damiani_volatmeter_fast(source, sed_std, atrvis, atrsed, vis_std, threshold)
if sequential:
return DamianiVolatmeter(vol, t)
else:
return DamianiVolatmeter(vol[-1], t[-1])
@njit
def damiani_volatmeter_fast(source, sed_std, atrvis, atrsed, vis_std,
threshold): # Function is compiled to machine code when called the first time
lag_s = 0.5
vol = np.full_like(source, 0)
t = np.full_like(source, 0)
for i in range(source.shape[0]):
if not (i < sed_std):
vol[i] = atrvis[i] / atrsed[i] + lag_s * (vol[i - 1] - vol[i - 3])
anti_thres = np.std(source[i - vis_std:i]) / np.std(source[i - sed_std:i])
t[i] = threshold - anti_thres
return vol, t

View File

@@ -3,9 +3,12 @@ from typing import Union
import numpy as np
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
from .high_pass_2_pole import high_pass_2_pole_fast
def dec_osc(candles: np.ndarray, hp_period=125, k=1, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def dec_osc(candles: np.ndarray, hp_period: int = 125, k: float = 1, source_type: str = "close",
sequential: bool = False) -> Union[float, np.ndarray]:
"""
Ehlers Decycler Oscillator
@@ -16,30 +19,17 @@ def dec_osc(candles: np.ndarray, hp_period=125, k=1, source_type="close", sequen
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
alphaArg1 = 2 * np.pi * 0.707 / hp_period
alpha1 = (np.cos(alphaArg1) + np.sin(alphaArg1) - 1) / np.cos(alphaArg1)
coeff1 = np.array([(1 - alpha1 / 2) ** 2, 2 * (1 - alpha1), -(1 - alpha1) ** 2])
hp1 = np.copy(source)
alphaArg2 = 2 * np.pi * 0.707 / (0.5 * hp_period)
alpha2 = (np.cos(alphaArg2) + np.sin(alphaArg2) - 1) / np.cos(alphaArg2)
coeff2 = np.array([(1 - alpha2 / 2) ** 2, 2 * (1 - alpha2), -(1 - alpha2) ** 2])
hp2 = np.copy(source)
hp = high_pass_2_pole_fast(source, hp_period)
dec = source - hp
decosc = high_pass_2_pole_fast(dec, 0.5 * hp_period)
for i in range(source.shape[0]):
val1 = np.array([source[i] - 2 * source[i - 1] + source[i - 2], hp1[i - 1], hp1[i - 2]])
hp1[i] = np.matmul(coeff1, val1)
val2 = np.array(
[(source[i] - hp1[i]) - 2 * (source[i - 1] - hp1[i - 1]) + (source[i - 2] - hp1[i - 2]), hp2[i - 1],
hp2[i - 2]])
hp2[i] = np.matmul(coeff2, val2)
res = 100 * k * hp2 / source
res = 100 * k * decosc / source
if sequential:
return res

View File

@@ -3,9 +3,12 @@ from typing import Union
import numpy as np
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
from .high_pass_2_pole import high_pass_2_pole_fast
def decycler(candles: np.ndarray, hp_period=125, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def decycler(candles: np.ndarray, hp_period: int = 125, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
Ehlers Simple Decycler
@@ -15,20 +18,13 @@ def decycler(candles: np.ndarray, hp_period=125, source_type="close", sequential
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
alphaArg1 = 2 * np.pi * 0.707 / hp_period
alpha1 = (np.cos(alphaArg1) + np.sin(alphaArg1) - 1) / np.cos(alphaArg1)
coeff1 = np.array([(1 - alpha1 / 2) ** 2, 2 * (1 - alpha1), -(1 - alpha1) ** 2])
hp1 = np.copy(source)
for i in range(source.shape[0]):
val1 = np.array([source[i] - 2 * source[i - 1] + source[i - 2], hp1[i - 1], hp1[i - 2]])
hp1[i] = np.matmul(coeff1, val1)
res = source - hp1
hp = high_pass_2_pole_fast(source, hp_period)
res = source - hp
if sequential:
return res

View File

@@ -4,9 +4,11 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def dema(candles: np.ndarray, period=30, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def dema(candles: np.ndarray, period: int = 30, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
DEMA - Double Exponential Moving Average
@@ -17,8 +19,9 @@ def dema(candles: np.ndarray, period=30, source_type="close", sequential=False)
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.DEMA(source, timeperiod=period)

View File

@@ -0,0 +1,39 @@
from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def devstop(candles: np.ndarray, period:int=20, mult: float = 0, direction: str = "long", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
Kase Dev Stops
:param candles: np.ndarray
:param period: int - default=20
:param mult: float - default=0
:param direction: str - default=long
:param sequential: bool - default=False
:return: float | np.ndarray
"""
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
high = candles[:, 3]
low = candles[:, 4]
AVTR = talib.SMA(talib.MAX(high, 2) - talib.MIN(low, 2), period)
SD = talib.STDDEV(talib.MAX(high, 2) - talib.MIN(low, 2), period)
if direction == "long":
res = talib.MAX(high - AVTR - mult * SD, period)
else:
res = talib.MIN(low + AVTR + mult * SD, period)
if sequential:
return res
else:
return None if np.isnan(res[-1]) else res[-1]

View File

@@ -3,10 +3,12 @@ from collections import namedtuple
import numpy as np
import talib
from jesse.helpers import get_config
DI = namedtuple('DI', ['plus', 'minus'])
def di(candles: np.ndarray, period=14, sequential=False) -> DI:
def di(candles: np.ndarray, period: int = 14, sequential: bool = False) -> DI:
"""
DI - Directional Indicator
@@ -16,8 +18,9 @@ def di(candles: np.ndarray, period=14, sequential=False) -> DI:
:return: DI(plus, minus)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
MINUS_DI = talib.MINUS_DI(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)
PLUS_DI = talib.PLUS_DI(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)

View File

@@ -3,10 +3,12 @@ from collections import namedtuple
import numpy as np
import talib
from jesse.helpers import get_config
DM = namedtuple('DM', ['plus', 'minus'])
def dm(candles: np.ndarray, period=14, sequential=False) -> DM:
def dm(candles: np.ndarray, period: int = 14, sequential: bool = False) -> DM:
"""
DM - Directional Movement
@@ -16,11 +18,12 @@ def dm(candles: np.ndarray, period=14, sequential=False) -> DM:
:return: DM(plus, minus)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
MINUS_DI = talib.MINUS_DM(candles[:, 3], candles[:, 4], timeperiod=period)
PLUS_DI = talib.PLUS_DM(candles[:, 3], candles[:, 4], timeperiod=period)
MINUS_DI = talib.MINUS_DM(candles[:, 3], candles[:, 4], timeperiod=period)
PLUS_DI = talib.PLUS_DM(candles[:, 3], candles[:, 4], timeperiod=period)
if sequential:
return DM(PLUS_DI, MINUS_DI)

View File

@@ -3,10 +3,12 @@ from collections import namedtuple
import numpy as np
import talib
from jesse.helpers import get_config
DonchianChannel = namedtuple('DonchianChannel', ['upperband', 'middleband', 'lowerband'])
def donchian(candles: np.ndarray, period=20, sequential=False) -> DonchianChannel:
def donchian(candles: np.ndarray, period: int = 20, sequential: bool = False) -> DonchianChannel:
"""
Donchian Channels
@@ -16,8 +18,9 @@ def donchian(candles: np.ndarray, period=20, sequential=False) -> DonchianChanne
:return: DonchianChannel(upperband, middleband, lowerband)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
UC = talib.MAX(candles[:, 3], timeperiod=period)
LC = talib.MIN(candles[:, 4], timeperiod=period)

View File

@@ -4,9 +4,11 @@ import numpy as np
import tulipy as ti
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def dpo(candles: np.ndarray, period=5, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def dpo(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
DPO - Detrended Price Oscillator
@@ -17,8 +19,9 @@ def dpo(candles: np.ndarray, period=5, source_type="close", sequential=False) ->
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = ti.dpo(np.ascontiguousarray(source), period=period)

View File

@@ -4,9 +4,11 @@ import numpy as np
import talib
import jesse.helpers as jh
from jesse.helpers import get_config
def dti(candles: np.ndarray, r=14, s=10, u=5, sequential=False) -> Union[float, np.ndarray]:
def dti(candles: np.ndarray, r: int = 14, s: int = 10, u: int = 5, sequential: bool = False) -> Union[
float, np.ndarray]:
"""
DTI by William Blau
@@ -18,8 +20,9 @@ def dti(candles: np.ndarray, r=14, s=10, u=5, sequential=False) -> Union[float,
:return: float
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
high = candles[:, 3]
low = candles[:, 4]

View File

@@ -3,9 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def dx(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
def dx(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
"""
DX - Directional Movement Index
@@ -15,8 +16,9 @@ def dx(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndar
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.DX(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)

View File

@@ -2,11 +2,14 @@ from typing import Union
import numpy as np
import talib
from numba import njit
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def efi(candles: np.ndarray, period=13, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def efi(candles: np.ndarray, period: int = 13, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
EFI - Elders Force Index
@@ -17,16 +20,23 @@ def efi(candles: np.ndarray, period=13, source_type="close", sequential=False) -
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
dif = np.zeros(len(source) - 1)
for i in range(1, len(source)):
dif[i - 1] = (source[i] - source[i - 1]) * candles[:, 5][i]
dif = efi_fast(source, candles[:, 5])
res = talib.EMA(dif, timeperiod=period)
res_with_nan = np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res))
return res_with_nan if sequential else res_with_nan[-1]
@njit
def efi_fast(source, volume):
dif = np.zeros(len(source) - 1)
for i in range(1, len(source)):
dif[i - 1] = (source[i] - source[i - 1]) * volume[i]
return dif

View File

@@ -4,9 +4,11 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def ema(candles: np.ndarray, period=5, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def ema(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
EMA - Exponential Moving Average
@@ -17,8 +19,9 @@ def ema(candles: np.ndarray, period=5, source_type="close", sequential=False) ->
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.EMA(source, timeperiod=period)

View File

@@ -1,13 +1,15 @@
import math
from collections import namedtuple
import numpy as np
import talib
from numba import njit
from jesse.helpers import get_config
EMD = namedtuple('EMD', ['upperband', 'middleband', 'lowerband'])
def emd(candles: np.ndarray, period=20, delta=0.5, fraction=0.1, sequential=False) -> EMD:
def emd(candles: np.ndarray, period: int = 20, delta=0.5, fraction=0.1, sequential: bool = False) -> EMD:
"""
Empirical Mode Decomposition by John F. Ehlers and Ric Way
@@ -19,14 +21,32 @@ def emd(candles: np.ndarray, period=20, delta=0.5, fraction=0.1, sequential=Fals
:return: EMD(upperband, middleband, lowerband)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
price = (candles[:, 3] + candles[:, 4]) / 2
bp = bp_fast(price, period, delta)
mean = talib.SMA(bp, timeperiod=2 * period)
peak, valley = peak_valley_fast(bp, price)
avg_peak = fraction * talib.SMA(peak, timeperiod=50)
avg_valley = fraction * talib.SMA(valley, timeperiod=50)
if sequential:
return EMD(avg_peak, mean, avg_valley)
else:
return EMD(avg_peak[-1], mean[-1], avg_valley[-1])
@njit
def bp_fast(price, period, delta):
# bandpass filter
beta = math.cos(2 * math.pi / period)
gamma = 1 / math.cos(4 * math.pi * delta / period)
alpha = gamma - math.sqrt(gamma * gamma - 1)
beta = np.cos(2 * np.pi / period)
gamma = 1 / np.cos(4 * np.pi * delta / period)
alpha = gamma - np.sqrt(gamma * gamma - 1)
bp = np.zeros_like(price)
for i in range(price.shape[0]):
@@ -34,9 +54,11 @@ def emd(candles: np.ndarray, period=20, delta=0.5, fraction=0.1, sequential=Fals
bp[i] = 0.5 * (1 - alpha) * (price[i] - price[i - 2]) + beta * (1 + alpha) * bp[i - 1] - alpha * bp[i - 2]
else:
bp[i] = 0.5 * (1 - alpha) * (price[i] - price[i - 2])
return bp
mean = talib.SMA(bp, timeperiod=2 * period)
@njit
def peak_valley_fast(bp, price):
peak = np.copy(bp)
valley = np.copy(bp)
@@ -49,10 +71,4 @@ def emd(candles: np.ndarray, period=20, delta=0.5, fraction=0.1, sequential=Fals
if bp[i - 1] < bp[i] and bp[i - 1] < bp[i - 2]:
valley[i] = bp[i - 1]
avg_peak = fraction * talib.SMA(peak, timeperiod=50)
avg_valley = fraction * talib.SMA(valley, timeperiod=50)
if sequential:
return EMD(avg_peak, mean, avg_valley)
else:
return EMD(avg_peak[-1], mean[-1], avg_valley[-1])
return peak, valley

View File

@@ -3,9 +3,10 @@ from typing import Union
import numpy as np
import tulipy as ti
from jesse.helpers import get_config
def emv(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
def emv(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
"""
EMV - Ease of Movement
@@ -14,9 +15,11 @@ def emv(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = ti.emv(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]), np.ascontiguousarray(candles[:, 5]))
res = ti.emv(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]),
np.ascontiguousarray(candles[:, 5]))
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]

35
jesse/indicators/er.py Normal file
View File

@@ -0,0 +1,35 @@
from typing import Union
import numpy as np
from numpy.lib.stride_tricks import sliding_window_view
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def er(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
ER - The Kaufman Efficiency indicator
:param candles: np.ndarray
:param period: int - default: 5
:param source_type: str - default: "close"
:param sequential: bool - default=False
:return: float | np.ndarray
"""
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
change = np.abs(np.diff(source, period))
abs_dif = np.abs(np.diff(source))
swv = sliding_window_view(abs_dif, window_shape=period)
volatility = swv.sum()
res = change / volatility
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]

View File

@@ -3,10 +3,12 @@ from collections import namedtuple
import numpy as np
import tulipy as ti
from jesse.helpers import get_config
FisherTransform = namedtuple('FisherTransform', ['fisher', 'signal'])
def fisher(candles: np.ndarray, period=9, sequential=False) -> FisherTransform:
def fisher(candles: np.ndarray, period: int = 9, sequential: bool = False) -> FisherTransform:
"""
The Fisher Transform helps identify price reversals.
@@ -16,8 +18,9 @@ def fisher(candles: np.ndarray, period=9, sequential=False) -> FisherTransform:
:return: FisherTransform(fisher, signal)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
fisher, fisher_signal = ti.fisher(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]),
period=period)

View File

@@ -4,9 +4,11 @@ import numpy as np
import tulipy as ti
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def fosc(candles: np.ndarray, period=5, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def fosc(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
FOSC - Forecast Oscillator
@@ -17,8 +19,9 @@ def fosc(candles: np.ndarray, period=5, source_type="close", sequential=False) -
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = ti.fosc(np.ascontiguousarray(source), period=period)

View File

@@ -1,10 +1,13 @@
import math
from typing import Union
import numpy as np
from numba import njit
from jesse.helpers import get_config
def frama(candles: np.ndarray, window=10, FC=1, SC=300, sequential=False) -> Union[float, np.ndarray]:
def frama(candles: np.ndarray, window: int = 10, FC: int = 1, SC: int = 300, sequential: bool = False) -> Union[
float, np.ndarray]:
"""
Fractal Adaptive Moving Average (FRAMA)
@@ -16,9 +19,9 @@ def frama(candles: np.ndarray, window=10, FC=1, SC=300, sequential=False) -> Uni
:return: float | np.ndarray
"""
if len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
n = window
@@ -27,7 +30,17 @@ def frama(candles: np.ndarray, window=10, FC=1, SC=300, sequential=False) -> Uni
print("FRAMA n must be even. Adding one")
n += 1
w = math.log(2.0 / (SC + 1))
frama = frame_fast(candles, n, SC, FC)
if sequential:
return frama
else:
return frama[-1]
@njit
def frame_fast(candles, n, SC, FC):
w = np.log(2.0 / (SC + 1))
D = np.zeros(len(candles))
D[:n] = np.NaN
@@ -38,24 +51,22 @@ def frama(candles: np.ndarray, window=10, FC=1, SC=300, sequential=False) -> Uni
for i in range(n, len(candles)):
per = candles[i - n:i]
# take 2 batches of the input
split = np.split(per, 2)
v1 = split[0]
v2 = split[1]
v1 = per[len(per)//2:]
v2 = per[:len(per)//2]
N1 = (np.max(v1[:, 3]) - np.min(v1[:, 4])) / (n / 2)
N2 = (np.max(v2[:, 3]) - np.min(v2[:, 4])) / (n / 2)
N3 = (np.max(per[:, 3]) - np.min(per[:, 4])) / n
N1 = (max(v1[:, 3]) - min(v1[:, 4])) / (n / 2)
N2 = (max(v2[:, 3]) - min(v2[:, 4])) / (n / 2)
N3 = (max(per[:, 3]) - min(per[:, 4])) / n
if N1 > 0 and N2 > 0 and N3 > 0:
D[i] = (math.log(N1 + N2) - math.log(N3)) / math.log(2)
D[i] = (np.log(N1 + N2) - np.log(N3)) / np.log(2)
else:
D[i] = D[i - 1]
oldalpha = math.exp(w * (D[i] - 1))
oldalpha = np.exp(w * (D[i] - 1))
# keep btwn 1 & 0.01
oldalpha = np.max([oldalpha, 0.1])
oldalpha = np.min([oldalpha, 1])
oldalpha = max([oldalpha, 0.1])
oldalpha = min([oldalpha, 1])
oldN = (2 - oldalpha) / oldalpha
N = ((SC - FC) * ((oldN - 1) / (SC - 1))) + FC
@@ -73,8 +84,4 @@ def frama(candles: np.ndarray, window=10, FC=1, SC=300, sequential=False) -> Uni
for i in range(n, len(frama)):
frama[i] = (alphas[i] * candles[:, 2][i]) + (1 - alphas[i]) * frama[i - 1]
if sequential:
return frama
else:
return frama[-1]
return frama

52
jesse/indicators/fwma.py Normal file
View File

@@ -0,0 +1,52 @@
from math import fabs
from typing import Union
import numpy as np
from numpy.lib.stride_tricks import sliding_window_view
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def fwma(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
Fibonacci's Weighted Moving Average (FWMA)
:param candles: np.ndarray
:param period: int - default: 5
:param source_type: str - default: "close"
:param sequential: bool - default=False
:return: float | np.ndarray
"""
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
fibs = fibonacci(n=period)
swv = sliding_window_view(source, window_shape=period)
res = np.average(swv, weights=fibs, axis=-1)
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]
def fibonacci(n: int = 2) -> np.ndarray:
"""Fibonacci Sequence as a numpy array"""
n = int(fabs(n)) if n >= 0 else 2
n -= 1
a, b = 1, 1
result = np.array([a])
for i in range(0, n):
a, b = b, a + b
result = np.append(result, a)
fib_sum = np.sum(result)
if fib_sum > 0:
return result / fib_sum
else:
return result

View File

@@ -4,11 +4,12 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source, np_shift
from jesse.helpers import get_config
GATOR = namedtuple('GATOR', ['upper', 'lower', 'upper_change', 'lower_change'])
def gatorosc(candles: np.ndarray, source_type="close", sequential=False) -> GATOR:
def gatorosc(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> GATOR:
"""
Gator Oscillator by Bill M. Williams
@@ -19,8 +20,9 @@ def gatorosc(candles: np.ndarray, source_type="close", sequential=False) -> GATO
:return: GATOR(upper, lower, upper_change, lower_change)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
@@ -39,6 +41,7 @@ def gatorosc(candles: np.ndarray, source_type="close", sequential=False) -> GATO
else:
return GATOR(upper[-1], lower[-1], upper_change[-1], lower_change[-1])
def numpy_ewma(data, window):
"""

View File

@@ -1,12 +1,14 @@
import math
from typing import Union
import numpy as np
from numba import njit
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def gauss(candles: np.ndarray, period=14, poles=4, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def gauss(candles: np.ndarray, period: int = 14, poles: int = 4, source_type: str = "close",
sequential: bool = False) -> Union[float, np.ndarray]:
"""
Gaussian Filter
@@ -19,16 +21,32 @@ def gauss(candles: np.ndarray, period=14, poles=4, source_type="close", sequenti
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
fil, to_fill = gauss_fast(source, period, poles)
if to_fill != 0:
res = np.insert(fil[poles:], 0, np.repeat(np.nan, to_fill))
else:
res = fil[poles:]
if sequential:
return res
else:
return None if np.isnan(res[-1]) else res[-1]
@njit
def gauss_fast(source, period, poles):
N = len(source)
source = source[~np.isnan(source)]
to_fill = N - len(source)
PI = math.pi
beta = (1 - math.cos(2 * PI / period)) / (math.pow(2, 1 / poles) - 1)
alpha = -beta + math.sqrt(math.pow(beta, 2) + 2 * beta)
PI = np.pi
beta = (1 - np.cos(2 * PI / period)) / (np.power(2, 1 / poles) - 1)
alpha = -beta + np.sqrt(np.power(beta, 2) + 2 * beta)
fil = np.zeros(poles + len(source))
if poles == 1:
@@ -52,12 +70,4 @@ def gauss(candles: np.ndarray, period=14, poles=4, source_type="close", sequenti
fil[poles + i] = np.dot(coeff, val)
if to_fill != 0:
res = np.insert(fil[poles:], 0, np.repeat(np.nan, to_fill))
else:
res = fil[poles:]
if sequential:
return res
else:
return None if np.isnan(res[-1]) else res[-1]
return fil, to_fill

View File

@@ -1,14 +1,16 @@
import math
from typing import Union
import numpy as np
from numba import njit
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def high_pass(candles: np.ndarray, period=48, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def high_pass(candles: np.ndarray, period: int = 48, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
High Pass Filter indicator by John F. Ehlers
(1 pole) high-pass filter indicator by John F. Ehlers
:param candles: np.ndarray
:param period: int - default=48
@@ -18,20 +20,26 @@ def high_pass(candles: np.ndarray, period=48, source_type="close", sequential=Fa
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
hpf = np.full_like(source, 0)
for i in range(source.shape[0]):
if not (i < 2):
alpha_arg = 2 * math.pi / (period * 1.414)
alpha1 = (math.cos(alpha_arg) + math.sin(alpha_arg) - 1) / math.cos(alpha_arg)
hpf[i] = math.pow(1.0 - alpha1 / 2.0, 2) * (source[i] - 2 * source[i - 1] + source[i - 2]) + 2 * (1 - alpha1) * hpf[i - 1] - math.pow(1 - alpha1, 2) * hpf[i - 2]
hpf = high_pass_fast(source, period)
if sequential:
return hpf
else:
return None if np.isnan(hpf[-1]) else hpf[-1]
@njit
def high_pass_fast(source, period): # Function is compiled to machine code when called the first time
k = 1
alpha = 1 + (np.sin(2 * np.pi * k / period) - 1) / np.cos(2 * np.pi * k / period)
newseries = np.copy(source)
for i in range(1, source.shape[0]):
newseries[i] = (1 - alpha / 2) * source[i] - (1 - alpha / 2) * source[i - 1] \
+ (1 - alpha) * newseries[i - 1]
return newseries

View File

@@ -0,0 +1,46 @@
from typing import Union
import numpy as np
from numba import njit
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def high_pass_2_pole(candles: np.ndarray, period: int = 48, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
(2 pole) high-pass filter indicator by John F. Ehlers
:param candles: np.ndarray
:param period: int - default=48
:param source_type: str - default: "close"
:param sequential: bool - default=False
:return: float | np.ndarray
"""
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
hpf = high_pass_2_pole_fast(source, period)
if sequential:
return hpf
else:
return None if np.isnan(hpf[-1]) else hpf[-1]
@njit
def high_pass_2_pole_fast(source, period, K=0.707): # Function is compiled to machine code when called the first time
alpha = 1 + (np.sin(2 * np.pi * K / period) - 1) / np.cos(2 * np.pi * K / period)
newseries = np.copy(source)
for i in range(2, source.shape[0]):
newseries[i] = (1 - alpha / 2) ** 2 * source[i] \
- 2 * (1 - alpha / 2) ** 2 * source[i - 1] \
+ (1 - alpha / 2) ** 2 * source[i - 2] \
+ 2 * (1 - alpha) * newseries[i - 1] - (1 - alpha) ** 2 * newseries[i - 2]
return newseries

View File

@@ -4,9 +4,11 @@ import numpy as np
import tulipy as ti
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def hma(candles: np.ndarray, period=5, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def hma(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
Hull Moving Average
@@ -17,8 +19,9 @@ def hma(candles: np.ndarray, period=5, source_type="close", sequential=False) ->
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = ti.hma(np.ascontiguousarray(source), period=period)

View File

@@ -4,9 +4,10 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def ht_dcperiod(candles: np.ndarray, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def ht_dcperiod(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]:
"""
HT_DCPERIOD - Hilbert Transform - Dominant Cycle Period
@@ -16,8 +17,9 @@ def ht_dcperiod(candles: np.ndarray, source_type="close", sequential=False) -> U
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.HT_DCPERIOD(source)

View File

@@ -4,9 +4,10 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def ht_dcphase(candles: np.ndarray, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def ht_dcphase(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]:
"""
HT_DCPHASE - Hilbert Transform - Dominant Cycle Phase
@@ -16,10 +17,11 @@ def ht_dcphase(candles: np.ndarray, source_type="close", sequential=False) -> Un
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.HT_DCPHASE (source)
res = talib.HT_DCPHASE(source)
return res if sequential else res[-1]

View File

@@ -4,10 +4,12 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
IQ = namedtuple('IQ', ['inphase', 'quadrature'])
def ht_phasor(candles: np.ndarray, source_type="close", sequential=False) -> IQ:
def ht_phasor(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> IQ:
"""
HT_PHASOR - Hilbert Transform - Phasor Components
@@ -17,8 +19,9 @@ def ht_phasor(candles: np.ndarray, source_type="close", sequential=False) -> IQ:
:return: IQ(inphase, quadrature)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
inphase, quadrature = talib.HT_PHASOR(source)

View File

@@ -4,10 +4,12 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
SINEWAVE = namedtuple('SINEWAVE', ['sine', 'lead'])
def ht_sine(candles: np.ndarray, source_type="close", sequential=False) -> SINEWAVE:
def ht_sine(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> SINEWAVE:
"""
HT_SINE - Hilbert Transform - SineWave
@@ -17,8 +19,9 @@ def ht_sine(candles: np.ndarray, source_type="close", sequential=False) -> SINEW
:return: SINEWAVE(sine, lead)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
sine, leadsine = talib.HT_SINE(source)

View File

@@ -4,9 +4,10 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def ht_trendline(candles: np.ndarray, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def ht_trendline(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]:
"""
HT_TRENDLINE - Hilbert Transform - Instantaneous Trendline
@@ -16,8 +17,9 @@ def ht_trendline(candles: np.ndarray, source_type="close", sequential=False) ->
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.HT_TRENDLINE(source)

View File

@@ -4,9 +4,10 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def ht_trendmode(candles: np.ndarray, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def ht_trendmode(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]:
"""
HT_TRENDMODE - Hilbert Transform - Trend vs Cycle Mode
@@ -16,8 +17,9 @@ def ht_trendmode(candles: np.ndarray, source_type="close", sequential=False) ->
:return: int | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.HT_TRENDMODE(source)

View File

@@ -5,8 +5,8 @@ import numpy as np
IchimokuCloud = namedtuple('IchimokuCloud', ['conversion_line', 'base_line', 'span_a', 'span_b'])
def ichimoku_cloud(candles: np.ndarray, conversion_line_period=9, base_line_period=26, lagging_line_period=52,
displacement=26) -> IchimokuCloud:
def ichimoku_cloud(candles: np.ndarray, conversion_line_period: int = 9, base_line_period: int = 26,
lagging_line_period: int = 52, displacement: int = 26) -> IchimokuCloud:
"""
Ichimoku Cloud

View File

@@ -3,6 +3,7 @@ from collections import namedtuple
import numpy as np
import talib
from jesse.helpers import get_config
from jesse.helpers import np_shift
IchimokuCloud = namedtuple('IchimokuCloud',
@@ -10,8 +11,9 @@ IchimokuCloud = namedtuple('IchimokuCloud',
'future_span_b'])
def ichimoku_cloud_seq(candles: np.ndarray, conversion_line_period=9, base_line_period=26, lagging_line_period=52,
displacement=26, sequential=False) -> IchimokuCloud:
def ichimoku_cloud_seq(candles: np.ndarray, conversion_line_period: int = 9, base_line_period: int = 26,
lagging_line_period: int = 52, displacement: int = 26,
sequential: bool = False) -> IchimokuCloud:
"""
Ichimoku Cloud
@@ -28,8 +30,9 @@ def ichimoku_cloud_seq(candles: np.ndarray, conversion_line_period=9, base_line_
if len(candles) < lagging_line_period + displacement:
raise ValueError("Too few candles available for lagging_line_period + displacement.")
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
small_ph = talib.MAX(candles[:, 3], conversion_line_period)
small_pl = talib.MIN(candles[:, 4], conversion_line_period)

View File

@@ -1,13 +1,15 @@
from collections import namedtuple
import numpy as np
from numba import njit
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
ITREND = namedtuple('ITREND', ['signal', 'it', 'trigger'])
def itrend(candles: np.ndarray, alpha=0.07, source_type="hl2", sequential=False) -> ITREND:
def itrend(candles: np.ndarray, alpha: float = 0.07, source_type: str = "hl2", sequential: bool = False) -> ITREND:
"""
Instantaneous Trendline
@@ -18,28 +20,34 @@ def itrend(candles: np.ndarray, alpha=0.07, source_type="hl2", sequential=False)
:return: ITREND(signal, it, trigger)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
coeff = np.array(
[(alpha - alpha ** 2 / 4), alpha ** 2 / 2, - (alpha - alpha ** 2 * 3 / 4), 2 * (1 - alpha), - (1 - alpha) ** 2])
signal, it, trigger = itrend_fast(source, alpha)
if sequential:
return ITREND(signal, it, trigger)
else:
return ITREND(signal[-1], it[-1], trigger[-1])
@njit
def itrend_fast(source, alpha):
it = np.copy(source)
for i in range(2, 7):
it[i] = (source[i] + 2 * source[i - 1] + source[i - 2]) / 4
for i in range(7, source.shape[0]):
val = np.array([source[i], source[i - 1], source[i - 2], it[i - 1], it[i - 2]])
it[i] = np.matmul(coeff, val)
it[i] = (alpha - alpha ** 2 / 4) * source[i] \
+ alpha ** 2 / 2 * source[i - 1] \
- (alpha - alpha ** 2 * 3 / 4) * source[i - 2] \
+ 2 * (1 - alpha) * it[i - 1] - (1 - alpha) ** 2 * it[i - 2]
# compute lead 2 trigger & signal
lag2 = np.roll(it, 20)
lag2[:20] = it[:20]
trigger = 2 * it - lag2
signal = (trigger > it) * 1 - (trigger < it) * 1
if sequential:
return ITREND(signal, it, trigger)
else:
return ITREND(signal[-1], it[-1], trigger[-1])
return signal, it, trigger

View File

@@ -4,9 +4,11 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def kama(candles: np.ndarray, period=30, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def kama(candles: np.ndarray, period: int = 30, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
KAMA - Kaufman Adaptive Moving Average
@@ -17,8 +19,9 @@ def kama(candles: np.ndarray, period=30, source_type="close", sequential=False)
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.KAMA(source, timeperiod=period)

View File

@@ -0,0 +1,39 @@
from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def kaufmanstop(candles: np.ndarray, period: int = 22, mult: float = 2, direction: str = "long", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
Perry Kaufman's Stops
:param candles: np.ndarray
:param period: int - default=22
:param mult: float - default=2
:param direction: str - default=long
:param sequential: bool - default=False
:return: float | np.ndarray
"""
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
high = candles[:, 3]
low = candles[:, 4]
hl_diff = talib.SMA(high - low, period)
if direction == "long":
res = hl_diff * mult - low
else:
res = hl_diff * mult + high
if sequential:
return res
else:
return None if np.isnan(res[-1]) else res[-1]

View File

@@ -4,11 +4,13 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
KeltnerChannel = namedtuple('KeltnerChannel', ['upperband', 'middleband', 'lowerband'])
def keltner(candles: np.ndarray, period=20, multiplier=2, matype=1, source_type="close", sequential=False) -> KeltnerChannel:
def keltner(candles: np.ndarray, period: int = 20, multiplier: float = 2, matype: int = 1, source_type: str = "close",
sequential: bool = False) -> KeltnerChannel:
"""
Keltner Channels
@@ -22,8 +24,9 @@ def keltner(candles: np.ndarray, period=20, multiplier=2, matype=1, source_type=
:return: KeltnerChannel(upperband, middleband, lowerband)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
e = talib.MA(source, timeperiod=period, matype=matype)

View File

@@ -4,11 +4,14 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
KST = namedtuple('KST', ['line', 'signal'])
def kst(candles: np.ndarray, sma_period1=10, sma_period2=10, sma_period3=10, sma_period4=15, roc_period1=10, roc_period2=15, roc_period3=20, roc_period4=30, signal_period=9, source_type="close", sequential=False) -> KST:
def kst(candles: np.ndarray, sma_period1: int = 10, sma_period2: int = 10, sma_period3: int = 10, sma_period4: int = 15,
roc_period1: int = 10, roc_period2: int = 15, roc_period3: int = 20, roc_period4: int = 30,
signal_period: int = 9, source_type: str = "close", sequential: bool = False) -> KST:
"""
Know Sure Thing (KST)
@@ -27,13 +30,12 @@ def kst(candles: np.ndarray, sma_period1=10, sma_period2=10, sma_period3=10, sma
:return: KST(line, signal)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
aroc1 = talib.SMA(talib.ROC(source, timeperiod=roc_period1), sma_period1)
aroc2 = talib.SMA(talib.ROC(source, timeperiod=roc_period2), sma_period2)
aroc3 = talib.SMA(talib.ROC(source, timeperiod=roc_period3), sma_period3)

View File

@@ -3,8 +3,11 @@ from typing import Union
import numpy as np
import tulipy as ti
from jesse.helpers import get_config
def kvo(candles: np.ndarray, short_period=2, long_period=5, sequential=False) -> Union[float, np.ndarray]:
def kvo(candles: np.ndarray, short_period: int = 2, long_period: int = 5, sequential: bool = False) -> Union[
float, np.ndarray]:
"""
KVO - Klinger Volume Oscillator
@@ -15,9 +18,12 @@ def kvo(candles: np.ndarray, short_period=2, long_period=5, sequential=False) -
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = ti.kvo(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]), np.ascontiguousarray(candles[:, 2]), np.ascontiguousarray(candles[:, 5]), short_period=short_period, long_period=long_period)
res = ti.kvo(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]),
np.ascontiguousarray(candles[:, 2]), np.ascontiguousarray(candles[:, 5]), short_period=short_period,
long_period=long_period)
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]

View File

@@ -4,9 +4,11 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def linearreg(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def linearreg(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
LINEARREG - Linear Regression
@@ -17,8 +19,9 @@ def linearreg(candles: np.ndarray, period=14, source_type="close", sequential=Fa
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.LINEARREG(source, timeperiod=period)

View File

@@ -4,9 +4,11 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def linearreg_angle(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def linearreg_angle(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> \
Union[float, np.ndarray]:
"""
LINEARREG_ANGLE - Linear Regression Angle
@@ -17,8 +19,9 @@ def linearreg_angle(candles: np.ndarray, period=14, source_type="close", sequent
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.LINEARREG_ANGLE(source, timeperiod=period)

View File

@@ -4,9 +4,11 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def linearreg_intercept(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def linearreg_intercept(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> \
Union[float, np.ndarray]:
"""
LINEARREG_INTERCEPT - Linear Regression Intercept
@@ -17,8 +19,9 @@ def linearreg_intercept(candles: np.ndarray, period=14, source_type="close", seq
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.LINEARREG_INTERCEPT(source, timeperiod=period)

View File

@@ -4,9 +4,11 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def linearreg_slope(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def linearreg_slope(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> \
Union[float, np.ndarray]:
"""
LINEARREG_SLOPE - Linear Regression Slope
@@ -17,8 +19,9 @@ def linearreg_slope(candles: np.ndarray, period=14, source_type="close", sequent
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.LINEARREG_SLOPE(source, timeperiod=period)

View File

@@ -1,9 +1,12 @@
from typing import Union
import numpy as np
from numba import njit
from jesse.helpers import get_config
def lrsi(candles: np.ndarray, alpha=0.2, sequential=False) -> Union[float, np.ndarray]:
def lrsi(candles: np.ndarray, alpha: float = 0.2, sequential: bool = False) -> Union[float, np.ndarray]:
"""
RSI Laguerre Filter
@@ -13,11 +16,21 @@ def lrsi(candles: np.ndarray, alpha=0.2, sequential=False) -> Union[float, np.nd
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
rsi = lrsi_fast(alpha, candles)
if sequential:
return rsi
else:
return None if np.isnan(rsi[-1]) else rsi[-1]
@njit
def lrsi_fast(alpha, candles):
price = (candles[:, 3] + candles[:, 4]) / 2
l0 = np.copy(price)
l1 = np.copy(price)
l2 = np.copy(price)
@@ -55,7 +68,4 @@ def lrsi(candles: np.ndarray, alpha=0.2, sequential=False) -> Union[float, np.nd
else:
rsi[i] = cu / (cu + cd)
if sequential:
return rsi
else:
return None if np.isnan(rsi[-1]) else rsi[-1]
return rsi

View File

@@ -4,17 +4,19 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
MACD = namedtuple('MACD', ['macd', 'signal', 'hist'])
def macd(candles: np.ndarray, fastperiod=12, slowperiod=26, signalperiod=9, source_type="close",
sequential=False) -> MACD:
def macd(candles: np.ndarray, fast_period: int = 12, slow_period: int = 26, signal_period: int = 9,
source_type: str = "close",
sequential: bool = False) -> MACD:
"""
MACD - Moving Average Convergence/Divergence
:param candles: np.ndarray
:param fastperiod: int - default: 12
:param fast_period: int - default: 12
:param slow_period: int - default: 26
:param signal_period: int - default: 9
:param source_type: str - default: "close"
@@ -22,12 +24,13 @@ def macd(candles: np.ndarray, fastperiod=12, slowperiod=26, signalperiod=9, sour
:return: MACD(macd, signal, hist)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
macd, macdsignal, macdhist = talib.MACD(source, fastperiod=fastperiod, slowperiod=slowperiod,
signalperiod=signalperiod)
macd, macdsignal, macdhist = talib.MACD(source, fastperiod=fast_period, slowperiod=slow_period,
signalperiod=signal_period)
if sequential:
return MACD(macd, macdsignal, macdhist)

View File

@@ -4,17 +4,19 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
MACDEXT = namedtuple('MACDEXT', ['macd', 'signal', 'hist'])
def macdext(candles: np.ndarray, fastperiod=12, fastmatype=0, slowperiod=26, slowmatype=0, signalperiod=9,
signalmatype=0, source_type="close", sequential=False) -> MACDEXT:
def macdext(candles: np.ndarray, fast_period: int = 12, fast_matype: int = 0, slow_period: int = 26,
slow_matype: int = 0, signal_period: int = 9, signal_matype: int = 0, source_type: str = "close",
sequential: bool = False) -> MACDEXT:
"""
MACDEXT - MACD with controllable MA type
:param candles: np.ndarray
:param fastperiod: int - default: 12
:param fast_period: int - default: 12
:param fastmatype: int - default: 0
:param slow_period: int - default: 26
:param slowmatype: int - default: 0
@@ -25,13 +27,14 @@ def macdext(candles: np.ndarray, fastperiod=12, fastmatype=0, slowperiod=26, slo
:return: MACDEXT(macd, signal, hist)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
macd, macdsignal, macdhist = talib.MACDEXT(source, fastperiod=fastperiod, fastmatype=fastmatype,
slowperiod=slowperiod, slowmatype=slowmatype,
signalperiod=signalperiod, signalmatype=signalmatype)
macd, macdsignal, macdhist = talib.MACDEXT(source, fastperiod=fast_period, fastmatype=fast_matype,
slowperiod=slow_period, slowmatype=slow_matype,
signalperiod=signal_period, signalmatype=signal_matype)
if sequential:
return MACDEXT(macd, macdsignal, macdhist)

View File

@@ -4,11 +4,13 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
MAMA = namedtuple('MAMA', ['mama', 'fama'])
def mama(candles: np.ndarray, fastlimit=0.5, slowlimit=0.05, source_type="close", sequential=False) -> MAMA:
def mama(candles: np.ndarray, fastlimit: float = 0.5, slowlimit: float = 0.05, source_type: str = "close",
sequential: bool = False) -> MAMA:
"""
MAMA - MESA Adaptive Moving Average
@@ -20,8 +22,9 @@ def mama(candles: np.ndarray, fastlimit=0.5, slowlimit=0.05, source_type="close"
:return: MAMA(mama, fama)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
mama, fama = talib.MAMA(source, fastlimit=fastlimit, slowlimit=slowlimit)

View File

@@ -3,9 +3,10 @@ from typing import Union
import numpy as np
import tulipy as ti
from jesse.helpers import get_config
def marketfi(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
def marketfi(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
"""
MARKETFI - Market Facilitation Index
@@ -14,9 +15,11 @@ def marketfi(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = ti.marketfi(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]), np.ascontiguousarray(candles[:, 5]))
res = ti.marketfi(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]),
np.ascontiguousarray(candles[:, 5]))
return np.concatenate((np.full((candles.shape[0] - res.shape[0]), np.nan), res), axis=0) if sequential else res[-1]

View File

@@ -3,9 +3,10 @@ from typing import Union
import numpy as np
import tulipy as ti
from jesse.helpers import get_config
def mass(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.ndarray]:
def mass(candles: np.ndarray, period: int = 5, sequential: bool = False) -> Union[float, np.ndarray]:
"""
MASS - Mass Index
@@ -15,8 +16,9 @@ def mass(candles: np.ndarray, period=5, sequential=False) -> Union[float, np.nda
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = ti.mass(np.ascontiguousarray(candles[:, 3]), np.ascontiguousarray(candles[:, 4]), period=period)

View File

@@ -1,11 +1,14 @@
from typing import Union
import numpy as np
from numba import njit
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def mcginley_dynamic(candles: np.ndarray, period=10, k=0.6, source_type="close", sequential=False) -> Union[
def mcginley_dynamic(candles: np.ndarray, period: int = 10, k: float = 0.6, source_type: str = "close",
sequential: bool = False) -> Union[
float, np.ndarray]:
"""
McGinley Dynamic
@@ -17,19 +20,27 @@ def mcginley_dynamic(candles: np.ndarray, period=10, k=0.6, source_type="close",
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
mg = np.full_like(source, np.nan)
for i in range(len(source)):
if i == 0:
mg[i] = source[i]
else:
mg[i] = mg[i - 1] + ((source[i] - mg[i - 1]) / np.max([(k * period * ((source[i] / mg[i - 1]) ** 4)), 1]))
mg = md_fast(source, k, period)
if sequential:
return mg
else:
return None if np.isnan(mg[-1]) else mg[-1]
@njit
def md_fast(source, k, period):
mg = np.full_like(source, np.nan)
for i in range(len(source)):
if i == 0:
mg[i] = source[i]
else:
mg[i] = mg[i - 1] + ((source[i] - mg[i - 1]) / max([(k * period * ((source[i] / mg[i - 1]) ** 4)), 1]))
return mg

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def medprice(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
def medprice(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
"""
MEDPRICE - Median Price
@@ -13,8 +15,9 @@ def medprice(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.MEDPRICE(candles[:, 3], candles[:, 4])

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def mfi(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
def mfi(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
"""
MFI - Money Flow Index
@@ -14,8 +16,9 @@ def mfi(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nda
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.MFI(candles[:, 3], candles[:, 4], candles[:, 2], candles[:, 5], timeperiod=period)

View File

@@ -4,9 +4,11 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def midpoint(candles: np.ndarray, period=14, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def midpoint(candles: np.ndarray, period: int = 14, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
MIDPOINT - MidPoint over period
@@ -17,8 +19,9 @@ def midpoint(candles: np.ndarray, period=14, source_type="close", sequential=Fal
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.MIDPOINT(source, timeperiod=period)

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def midprice(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
def midprice(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
"""
MIDPRICE - Midpoint Price over period
@@ -14,8 +16,9 @@ def midprice(candles: np.ndarray, period=14, sequential=False) -> Union[float, n
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.MIDPRICE(candles[:, 3], candles[:, 4], timeperiod=period)

View File

@@ -3,10 +3,12 @@ from collections import namedtuple
import numpy as np
from scipy.signal import argrelextrema
from jesse.helpers import get_config, np_ffill
EXTREMA = namedtuple('EXTREMA', ['min', 'max', 'last_min', 'last_max'])
def minmax(candles: np.ndarray, order=3, sequential=False) -> EXTREMA:
def minmax(candles: np.ndarray, order: int = 3, sequential: bool = False) -> EXTREMA:
"""
minmax - Get extrema
@@ -16,8 +18,9 @@ def minmax(candles: np.ndarray, order=3, sequential=False) -> EXTREMA:
:return: EXTREMA(min, max, last_min, last_max)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
low = candles[:, 4]
high = candles[:, 3]
@@ -41,19 +44,3 @@ def minmax(candles: np.ndarray, order=3, sequential=False) -> EXTREMA:
else:
return EXTREMA(min[-1], max[-1], last_min[-1], last_max[-1])
def np_ffill(arr, axis=0):
"""
:param arr:
:param axis:
:return:
"""
idx_shape = tuple([slice(None)] + [np.newaxis] * (len(arr.shape) - axis - 1))
idx = np.where(~np.isnan(arr), np.arange(arr.shape[axis])[idx_shape], 0)
np.maximum.accumulate(idx, axis=axis, out=idx)
slc = [np.arange(k)[tuple([slice(None) if dim == i else np.newaxis
for dim in range(len(arr.shape))])]
for i, k in enumerate(arr.shape)]
slc[axis] = idx
return arr[tuple(slc)]

View File

@@ -4,9 +4,11 @@ import numpy as np
import talib
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def mom(candles: np.ndarray, period=10, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def mom(candles: np.ndarray, period: int = 10, source_type: str = "close", sequential: bool = False) -> Union[
float, np.ndarray]:
"""
MOM - Momentum
@@ -17,8 +19,9 @@ def mom(candles: np.ndarray, period=10, source_type="close", sequential=False) -
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = talib.MOM(source, timeperiod=period)

View File

@@ -4,10 +4,12 @@ import numpy as np
import tulipy as ti
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
MSW = namedtuple('MSW', ['sine', 'lead'])
def msw(candles: np.ndarray, period=5, source_type="close", sequential=False) -> MSW:
def msw(candles: np.ndarray, period: int = 5, source_type: str = "close", sequential: bool = False) -> MSW:
"""
MSW - Mesa Sine Wave
@@ -18,8 +20,9 @@ def msw(candles: np.ndarray, period=5, source_type="close", sequential=False) ->
:return: MSW(sine, lead)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
msw_sine, msw_lead = ti.msw(np.ascontiguousarray(source), period=period)
@@ -30,4 +33,4 @@ def msw(candles: np.ndarray, period=5, source_type="close", sequential=False) ->
if sequential:
return MSW(s, l)
else:
return MSW(s[-1], l[-1])
return MSW(s[-1], l[-1])

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def natr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.ndarray]:
def natr(candles: np.ndarray, period: int = 14, sequential: bool = False) -> Union[float, np.ndarray]:
"""
NATR - Normalized Average True Range
@@ -14,8 +16,9 @@ def natr(candles: np.ndarray, period=14, sequential=False) -> Union[float, np.nd
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.NATR(candles[:, 3], candles[:, 4], candles[:, 2], timeperiod=period)

View File

@@ -4,9 +4,10 @@ import numpy as np
import tulipy as ti
from jesse.helpers import get_candle_source
from jesse.helpers import get_config
def nvi(candles: np.ndarray, source_type="close", sequential=False) -> Union[float, np.ndarray]:
def nvi(candles: np.ndarray, source_type: str = "close", sequential: bool = False) -> Union[float, np.ndarray]:
"""
NVI - Negative Volume Index
@@ -16,8 +17,9 @@ def nvi(candles: np.ndarray, source_type="close", sequential=False) -> Union[flo
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
source = get_candle_source(candles, source_type=source_type)
res = ti.nvi(np.ascontiguousarray(source), np.ascontiguousarray(candles[:, 5]))

View File

@@ -3,8 +3,10 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def obv(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
def obv(candles: np.ndarray, sequential: bool = False) -> Union[float, np.ndarray]:
"""
OBV - On Balance Volume
@@ -13,8 +15,9 @@ def obv(candles: np.ndarray, sequential=False) -> Union[float, np.ndarray]:
:return: float | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
res = talib.OBV(candles[:, 2], candles[:, 5])

View File

@@ -3,8 +3,11 @@ from typing import Union
import numpy as np
import talib
from jesse.helpers import get_config
def pattern_recognition(candles: np.ndarray, pattern_type, penetration=0, sequential=False) -> Union[int, np.ndarray]:
def pattern_recognition(candles: np.ndarray, pattern_type: str, penetration: int = 0, sequential: bool = False) -> \
Union[int, np.ndarray]:
"""
Pattern Recognition
@@ -15,8 +18,9 @@ def pattern_recognition(candles: np.ndarray, pattern_type, penetration=0, sequen
:return: int | np.ndarray
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
if pattern_type == "CDL2CROWS":
res = talib.CDL2CROWS(candles[:, 1], candles[:, 3], candles[:, 4], candles[:, 2])

View File

@@ -2,10 +2,12 @@ from collections import namedtuple
import numpy as np
from jesse.helpers import get_config
PIVOT = namedtuple('PIVOT', ['r4', 'r3', 'r2', 'r1', 'pp', 's1', 's2', 's3', 's4'])
def pivot(candles: np.ndarray, mode=0, sequential=False) -> PIVOT:
def pivot(candles: np.ndarray, mode: int = 0, sequential: bool = False) -> PIVOT:
"""
Pivot Points
@@ -15,8 +17,9 @@ def pivot(candles: np.ndarray, mode=0, sequential=False) -> PIVOT:
:return: PIVOT(r4, r3, r2, r1, pp, s1, s2, s3, s4)
"""
if not sequential and len(candles) > 240:
candles = candles[-240:]
warmup_candles_num = get_config('env.data.warmup_candles_num', 240)
if not sequential and len(candles) > warmup_candles_num:
candles = candles[-warmup_candles_num:]
high = candles[:, 3]
low = candles[:, 4]

Some files were not shown because too many files have changed in this diff Show More