Compare commits

...

3305 Commits

Author SHA1 Message Date
Matthias 24e1de91eb Merge pull request #1438 from freqtrade/release_1804
Release last version for 2018
2018-12-27 12:47:12 +01:00
Matthias 23d0cea01f Version bump to 0.18.0 2018-12-27 11:45:21 +01:00
Matthias bb9dd86e77 Merge branch 'master' into release_1804 2018-12-27 11:44:50 +01:00
Misagh 0cbdf10ebe Merge pull request #1440 from freqtrade/fix/market_orders
Fix/market orders
2018-12-27 11:28:40 +01:00
Matthias 9af2fca718 Add handling for market orders
fixes #1427 and #1428
2018-12-27 11:19:26 +01:00
Matthias 20cdabbe9c Add test for market order 2018-12-27 09:31:21 +01:00
Matthias 9d906d013a Merge pull request #1439 from oonid/patch-1
Update installation reference for Raspbian
2018-12-27 08:57:13 +01:00
Oon Arfiandwi 8b38f44da6 Update installation reference for Raspbian
Add `libffi-dev` as an additional package to install before process installation with `pip`.
Add recommendation to use (mini)conda and remove package `scipy`, `pandas`, and `numpy` from `requrements.txt`.
2018-12-27 14:56:35 +08:00
Matthias 3f1248405f Merge pull request #1434 from freqtrade/strategy_explanatin
Enhance strategy explanation
2018-12-27 07:04:30 +01:00
Matthias 5b30815d7b Move "following section" part 2018-12-27 07:03:28 +01:00
Matthias 37cde77e18 Fix typo 2018-12-27 07:01:57 +01:00
Matthias 1fc0dcb9d8 Fix typo in link 2018-12-27 06:58:59 +01:00
Misagh 3411d3d5fa Merge pull request #1436 from freqtrade/remove_convert_dataframe
Remove convert_backtestdata - this is not usefull anymore
2018-12-26 15:53:25 +01:00
Misagh 93a9642abf Merge pull request #1435 from freqtrade/pyup_include_dev_file
Adjust pyup.yml to also "find" requirements-dev
2018-12-26 15:52:40 +01:00
Matthias b2bc5d9396 Remove convert_backtestdata - this is not usefull anymore 2018-12-26 14:02:17 +01:00
Matthias 32f43d3294 Adjust pyup.yml to also "find" requirements-dev 2018-12-26 13:52:56 +01:00
Matthias dc4e412e21 Merge pull request #1433 from freqtrade/pyup-scheduled-update-2018-12-26
Scheduled daily dependency update on wednesday
2018-12-26 13:44:47 +01:00
Matthias 5e3e7b6928 correct TOC for bot-optimization.md 2018-12-26 13:42:52 +01:00
Matthias f2beaf101c Add strategy documentation (fixes #818) 2018-12-26 13:42:46 +01:00
Matthias d951289862 Refactor strategy documentation 2018-12-26 13:42:37 +01:00
pyup-bot b28b2369da Update ccxt from 1.18.69 to 1.18.71 2018-12-26 13:33:06 +01:00
Misagh 2f1721a45b Merge pull request #1431 from freqtrade/pyup-scheduled-update-2018-12-25
Scheduled daily dependency update on tuesday
2018-12-25 14:04:53 +01:00
pyup-bot 0e6dbfab5e Update ccxt from 1.18.67 to 1.18.69 2018-12-25 13:33:06 +01:00
Misagh 8e5ea8620b Merge pull request #1430 from freqtrade/download_data_config
Download data config
2018-12-25 13:32:11 +01:00
Matthias 34b93eb952 Load config-file in download_backtest_data - 2018-12-25 13:15:41 +01:00
Matthias 8fbeb700d6 move key/secret in download_backtest_data to correct location 2018-12-25 13:00:48 +01:00
Matthias 22cd84de09 Merge pull request #1429 from freqtrade/pyup-scheduled-update-2018-12-24
Scheduled daily dependency update on monday
2018-12-24 13:46:23 +01:00
pyup-bot ae51458585 Update ccxt from 1.18.64 to 1.18.67 2018-12-24 13:33:08 +01:00
Matthias cef1fa8636 Merge pull request #1426 from mishaker/fix_int_remaining
Remaining amount in check_handle_timedout should be treated as float not int.
2018-12-24 13:32:45 +01:00
misagh a5137e4fa4 comparing float instead of int 2018-12-24 11:39:11 +01:00
Samuel Husso 390f13bbe4 Merge pull request #1423 from freqtrade/pyup-scheduled-update-2018-12-23
Scheduled daily dependency update on sunday
2018-12-24 11:18:02 +02:00
pyup-bot 741e336864 Update ccxt from 1.18.60 to 1.18.64 2018-12-23 13:33:07 +01:00
Matthias 30fe06aa55 Merge pull request #1417 from mishaker/last_candle_close
Adding "copy" as a parameter to klines. default to True
2018-12-22 19:14:18 +01:00
misagh 7243da3afe tests added for klines copy=True 2018-12-22 19:03:42 +01:00
Matthias f563dec0d6 Merge pull request #1422 from freqtrade/pyup-scheduled-update-2018-12-22
Scheduled daily dependency update on saturday
2018-12-22 14:07:49 +01:00
pyup-bot 7c69dbae30 Update ccxt from 1.18.58 to 1.18.60 2018-12-22 13:33:07 +01:00
Matthias 82a3806015 Merge pull request #1421 from freqtrade/pyup-scheduled-update-2018-12-21
Scheduled daily dependency update on friday
2018-12-21 13:53:40 +01:00
pyup-bot 41ef02a292 Update ccxt from 1.18.50 to 1.18.58 2018-12-21 13:32:06 +01:00
misagh 34e3af6ad4 do not copy DF if copy is false 2018-12-21 10:35:17 +01:00
misagh a13b30b2de removing test 2018-12-21 10:21:31 +01:00
misagh a45ec1ed1c adding copy as a parameter to klines 2018-12-21 10:20:01 +01:00
Matthias 7f4b5e43fc Merge pull request #1420 from freqtrade/pyup-scheduled-update-2018-12-20
Scheduled daily dependency update on thursday
2018-12-20 14:45:29 +01:00
pyup-bot 358b5d7e5d Update scikit-learn from 0.20.1 to 0.20.2 2018-12-20 13:32:09 +01:00
pyup-bot fc4384c96f Update ccxt from 1.18.46 to 1.18.50 2018-12-20 13:32:07 +01:00
Samuel Husso f54a21ae8f Merge pull request #1419 from freqtrade/pyup-scheduled-update-2018-12-19
Scheduled daily dependency update on wednesday
2018-12-19 16:39:52 +02:00
pyup-bot ad4952731a Update ccxt from 1.18.40 to 1.18.46 2018-12-19 13:32:09 +01:00
Samuel Husso ff0fc064c7 Merge pull request #1418 from freqtrade/pyup-scheduled-update-2018-12-18
Scheduled daily dependency update on tuesday
2018-12-18 14:50:38 +02:00
pyup-bot 2e06d52240 Update scipy from 1.1.0 to 1.2.0 2018-12-18 13:32:09 +01:00
pyup-bot fd4cfefda5 Update ccxt from 1.18.39 to 1.18.40 2018-12-18 13:32:07 +01:00
Samuel Husso d90a86ddef Merge pull request #1415 from pan-long/requirements-dev
Seperate requirements to run the bot and to develop.
2018-12-18 10:13:21 +02:00
Pan Long b1e9fa754a Base dev Docker image on freqtradeorg/freqtrade:develop. 2018-12-17 13:53:22 -08:00
misagh 215ded2e0a returning last candle close price for a pair 2018-12-17 21:30:58 +01:00
Pan Long 1483593e65 Fix instructions on building a dev Docker image. 2018-12-17 07:54:28 -08:00
Matthias a4aa87c21b Merge pull request #1416 from freqtrade/pyup-scheduled-update-2018-12-17
Scheduled daily dependency update on monday
2018-12-17 16:12:21 +01:00
pyup-bot ac9189ebc0 Update ccxt from 1.18.37 to 1.18.39 2018-12-17 13:32:07 +01:00
Misagh 1dbcab0b09 Merge pull request #1413 from freqtrade/feat/data_helpers
Feat/data helpers
2018-12-17 09:14:10 +01:00
Pan Long 1372095c66 Seperate requirements to run the bot and to develop.
- Add a requirements-dev.txt file which includes additional deps for development.
- Add a Dockerfile.develop which installs all deps for development and also enables dev commands.
- Change related documentations on how to run/dev the bot.
2018-12-16 22:15:45 -08:00
Matthias 5d253f352c Merge pull request #1358 from mishaker/time_in_force
Order Time In Force
2018-12-17 06:38:13 +01:00
Matthias b3bb98777b Merge branch 'develop' into time_in_force 2018-12-17 06:37:46 +01:00
Matthias 5493d1a7e0 Fix wonrly named module 2018-12-17 06:32:59 +01:00
Matthias c21bf7d6bb Merge pull request #1414 from mishaker/add_link_order_type
Adding order type explanation link to doc.
2018-12-17 06:09:48 +01:00
misagh 7357d6b089 adding order type explanation link to doc. 2018-12-16 22:13:50 +01:00
misagh c784b829e5 typo 2018-12-16 22:11:51 +01:00
misagh 213155e6d3 typo 2018-12-16 22:09:46 +01:00
misagh f756f1ad28 unnecessary explanation removed. 2018-12-16 22:08:50 +01:00
misagh 2e7028442d reformatting 2018-12-16 22:07:03 +01:00
misagh a967b8918a broken link corrected 2018-12-16 22:05:15 +01:00
misagh 9d8a3b4ec5 docs added 2018-12-16 22:02:29 +01:00
Matthias 806ab3729f Add / fix some comments 2018-12-16 14:14:17 +01:00
Misagh b6474e4a3c Merge pull request #1411 from freqtrade/xmatthias-readme_link
Add link to contributing for "wanna help"
2018-12-16 13:11:29 +01:00
Matthias eb7034c7a7 Rename download_backtest_testdata to download_pair_history 2018-12-16 10:33:08 +01:00
Matthias 50938d410a Remove tests for download_pairs 2018-12-16 10:30:13 +01:00
Matthias 8bd4d03e13 remove download_pairs 2018-12-16 10:29:53 +01:00
Matthias 8826a1df5f Add missing tests for trim_tickerlist 2018-12-16 10:19:49 +01:00
Matthias 043cefd60a allow reloading single pair 2018-12-16 10:17:11 +01:00
Matthias ebb80b6906 remove ujson / json fallback hack as it's now in requirements 2018-12-16 09:58:54 +01:00
Matthias f5b2430cda Fix docstrings and typo 2018-12-16 09:58:46 +01:00
Matthias 8a3c2a0c63 allow only loading 1 pair if necessary
* simplify tests nad remove unnecessary mocking
2018-12-15 20:32:55 +01:00
Matthias 429f846ad1 Switch load_data to kwargs 2018-12-15 20:31:05 +01:00
Matthias acd07d40a0 Cleanup some comments and code formatting 2018-12-15 19:52:52 +01:00
Matthias d421e4e8af update edge description 2018-12-15 19:15:38 +01:00
Matthias d0c9791ca6 Fix tests to support load_data with dataframe 2018-12-15 15:38:40 +01:00
Matthias 34ea214f7c Fix some tests to use dataframe 2018-12-15 14:42:21 +01:00
Matthias 1c5031b468 load_data to return dataframe 2018-12-15 14:28:37 +01:00
Matthias c1a32bc6c8 use json_load to load data
- otherwise unforseen problems could appear due to the default beeing ujson
2018-12-15 14:22:49 +01:00
Matthias b4f1a80dc1 Add edge oneliner to index 2018-12-15 14:21:14 +01:00
Matthias 6c02cc5993 Adjust test to pathlib 2018-12-15 14:14:38 +01:00
Matthias 21aba1620c Replace calls to load_data 2018-12-15 14:10:33 +01:00
Matthias f261911285 replace os.path with pathlib.Path 2018-12-15 13:54:35 +01:00
Matthias c82d165713 Merge pull request #1412 from freqtrade/pyup-scheduled-update-2018-12-15
Scheduled daily dependency update on saturday
2018-12-15 13:49:12 +01:00
Matthias a34c2cf64b Add missing test-module __init__.py 2018-12-15 13:40:02 +01:00
pyup-bot 4ad507f8dd Update ccxt from 1.18.36 to 1.18.37 2018-12-15 13:32:06 +01:00
Matthias 7e463b209c Add link to contributing for "wanna help" 2018-12-15 13:28:00 +01:00
Matthias df01e8b326 Merge pull request #1410 from freqtrade/pyup-scheduled-update-2018-12-14
Scheduled daily dependency update on friday
2018-12-14 13:54:07 +01:00
pyup-bot c42d5002a1 Update pytest from 4.0.1 to 4.0.2 2018-12-14 13:32:09 +01:00
pyup-bot 43039aa6ab Update ccxt from 1.18.32 to 1.18.36 2018-12-14 13:32:07 +01:00
Matthias 407139b0e0 remove unused imports 2018-12-14 06:32:49 +01:00
Matthias 17a820e5c0 Move tests from test_optimize to test_history 2018-12-14 06:32:49 +01:00
Matthias 92c800d925 Adjust tests to data.history 2018-12-14 06:32:49 +01:00
Matthias 4ca6aad99a Adjust imports in scripts 2018-12-14 06:32:49 +01:00
Matthias 432cc00283 Adjust imports to data.history 2018-12-14 06:32:49 +01:00
Matthias 0250a96feb Sort imports 2018-12-14 06:32:49 +01:00
Matthias 1a3fcd4771 extract data-handling methods from optimize 2018-12-14 06:32:49 +01:00
Matthias b38195e9b3 Rename to converter 2018-12-14 06:32:49 +01:00
Matthias 1f29802884 only export what's needed 2018-12-14 06:32:49 +01:00
Matthias 453f62cdfa Adjust imports 2018-12-14 06:32:49 +01:00
Matthias 030ecbfc17 move exchange_helpers to data module 2018-12-14 06:32:49 +01:00
Matthias 04c330f10b Merge pull request #1404 from freqtrade/feat/pass_df
keep DF instead of list
2018-12-13 20:14:32 +01:00
Matthias aca243086e Fix comment 2018-12-13 19:43:17 +01:00
Samuel Husso eb7fb2ff0f Merge pull request #1409 from freqtrade/pyup-scheduled-update-2018-12-13
Scheduled daily dependency update on thursday
2018-12-13 14:57:55 +02:00
pyup-bot 6c9c03b3d5 Update ccxt from 1.18.24 to 1.18.32 2018-12-13 13:32:07 +01:00
Misagh cdd0ef3094 Merge pull request #1407 from freqtrade/edge_docu
Add link to edge documentation in bot-usage
2018-12-12 20:38:44 +01:00
Matthias 6b4bab272f Add link to edge documentation in bot-usage 2018-12-12 20:20:07 +01:00
Matthias 960abeac0a Merge pull request #1406 from mishaker/fix_edge_broken_link
fix edge doc broken link
2018-12-12 20:17:56 +01:00
Misagh c3af7220f1 Merge pull request #1405 from freqtrade/edge_cli_comments
Fix edge-cli comments
2018-12-12 20:15:14 +01:00
misagh df5a280169 fix edge doc broken link 2018-12-12 20:11:27 +01:00
Matthias 7e3955b04c Fix edge-cli comments (refer to edge, not backtest 2018-12-12 20:04:14 +01:00
Matthias 5c3dcf3e2b Test for wrong inputs (empty / none-dataframes) in get_signal 2018-12-12 19:35:51 +01:00
Matthias d6ba4f0e81 Fix last 2 tests to use DF as data container 2018-12-12 19:17:09 +01:00
Matthias 7a533de1a8 Use list ticker history for backtesting 2018-12-12 19:17:09 +01:00
Matthias fe3990af3d Adjust some tests to dataframe passing 2018-12-12 19:17:09 +01:00
Matthias 627ab9f583 pass around dataframe instead of list 2018-12-12 19:17:09 +01:00
Misagh a377088421 Merge pull request #1403 from freqtrade/pyup-scheduled-update-2018-12-12
Scheduled daily dependency update on wednesday
2018-12-12 13:46:13 +01:00
misagh aa1262bea6 typo corrected 2018-12-12 13:33:03 +01:00
pyup-bot 79f5c4adfe Update sqlalchemy from 1.2.14 to 1.2.15 2018-12-12 13:32:09 +01:00
pyup-bot fd953bab8c Update ccxt from 1.18.18 to 1.18.24 2018-12-12 13:32:07 +01:00
misagh 8d8b53f4d1 added tests for IOC and FOK 2018-12-12 13:05:55 +01:00
Misagh 62f6dd5b17 Merge pull request #1402 from freqtrade/move_fiat
Move fiat to rpc module
2018-12-12 09:32:55 +01:00
Matthias 81b4940eef Adjust tests to new fiat-convert location 2018-12-11 20:27:54 +01:00
Matthias efc709501a move fiat-convert to rpc - adjust imports 2018-12-11 20:27:30 +01:00
Matthias 0f2c547805 Move fiat-convert to subfolder 2018-12-11 20:26:53 +01:00
Misagh 5a7451a823 Merge pull request #1400 from freqtrade/feat/exchange_styling
Feat/exchange styling
2018-12-11 19:32:49 +01:00
Matthias 0ab8ac1c1d Add test to verify downloading history does not modify
_pairs_last_refresh_time
2018-12-11 19:18:28 +01:00
Misagh 80efef87ab Merge pull request #1401 from freqtrade/pyup-scheduled-update-2018-12-11
Scheduled daily dependency update on tuesday
2018-12-11 13:59:59 +01:00
pyup-bot 70ad8a06c3 Update requests from 2.20.1 to 2.21.0 2018-12-11 13:33:08 +01:00
pyup-bot 97e7b0d9f6 Update ccxt from 1.18.17 to 1.18.18 2018-12-11 13:33:06 +01:00
Matthias 8c1901ad1e Extract caching logic from lowestlevel fetch_ohlcv function 2018-12-11 07:14:39 +01:00
Matthias 523dea4a04 remove hacky workaround not needed anymore 2018-12-10 20:22:41 +01:00
Matthias e2bff9d5cb Remove assigning klines from download method 2018-12-10 20:22:21 +01:00
Matthias 36de451809 Remove class-level variables 2018-12-10 19:55:21 +01:00
misagh adcaa8439e test_strategy_override_order_tif added 2018-12-10 19:17:56 +01:00
misagh e6fd7da43f adding test: create order should consider TIF 2018-12-10 19:09:20 +01:00
misagh 6018f2d252 order status handled in case of IOC and FOK 2018-12-10 18:52:24 +01:00
Misagh 3e479d045d Merge pull request #1399 from freqtrade/pyup-scheduled-update-2018-12-10
Scheduled daily dependency update on monday
2018-12-10 15:04:37 +01:00
pyup-bot d904667c87 Update ccxt from 1.18.13 to 1.18.17 2018-12-10 13:33:07 +01:00
misagh 866b7aee8e tests fixed 2018-12-09 16:22:21 +01:00
misagh 663e33d2ef if condition refactored 2018-12-09 16:06:00 +01:00
misagh 20d794e265 mistake in previous commit 2018-12-09 16:04:28 +01:00
misagh 2f5c8941eb removing unnecessary default value 2018-12-09 16:00:04 +01:00
misagh b35199a772 intermediary commit before extracting the logic 2018-12-09 15:59:05 +01:00
misagh 510f78079b conflict with develop resolved 2018-12-09 15:16:38 +01:00
Misagh c15231d1b9 Merge pull request #1398 from freqtrade/pyup-scheduled-update-2018-12-09
Scheduled daily dependency update on sunday
2018-12-09 15:09:02 +01:00
pyup-bot acb96eb501 Update ccxt from 1.18.11 to 1.18.13 2018-12-09 13:33:06 +01:00
Misagh 7e476e6144 Merge pull request #1397 from freqtrade/fix/db_migration
Drop indexes on renamed table during migration
2018-12-09 11:15:02 +01:00
Matthias 3b951c3817 Drop indexes on renamed table
avoid naming conflicts on recreate (indexes are not renamed, and keeping
them on backup tables does not really make sense).

fixes #1396
2018-12-09 09:03:17 +01:00
Matthias 2c27736dfe Merge pull request #1390 from freqtrade/feat/dynamic_provider
Dynamic Pairlist provider
2018-12-09 08:39:53 +01:00
Misagh 4e5fb6afd4 Merge pull request #1395 from freqtrade/pyup-scheduled-update-2018-12-08
Scheduled daily dependency update on saturday
2018-12-08 15:35:47 +01:00
pyup-bot b3b6eda2ba Update ccxt from 1.18.10 to 1.18.11 2018-12-08 13:33:06 +01:00
Misagh e5a51456ef Merge pull request #1394 from freqtrade/pyup-scheduled-update-2018-12-07
Scheduled daily dependency update on friday
2018-12-07 18:28:22 +01:00
pyup-bot ac9f19aee5 Update ccxt from 1.18.2 to 1.18.10 2018-12-07 13:34:07 +01:00
Matthias c38a1d0324 Merge pull request #1393 from freqtrade/pyup-scheduled-update-2018-12-06
Scheduled daily dependency update on thursday
2018-12-06 20:04:24 +01:00
Matthias aa579bafa4 Merge pull request #1365 from mishaker/edge_position
Fix edge position sizing.
2018-12-06 20:02:31 +01:00
Matthias 8f19c83f6b Refrase documentation 2018-12-06 19:39:25 +01:00
Matthias a63f123b6d Check if number_assets is defined, as it's required by VolumePairList 2018-12-06 19:36:33 +01:00
Matthias 40376c1e74 Merge pull request #1392 from freqtrade/fix/jsonschema
Specify JsonValidatorversion explicitly
2018-12-06 19:09:44 +01:00
misagh 0ea7dc9272 test added for total open trade stake amount from schalchemy 2018-12-06 13:51:06 +01:00
pyup-bot bf1841d2a8 Update ccxt from 1.17.583 to 1.18.2 2018-12-06 13:34:06 +01:00
Matthias 0c10719037 Specify JsonValidatorversion explicitly
without doing that, it exclusiveMaximum raises an exception
as jsonschema defaults to the latest version (Draft6)
which changes behaviour of this property.

fixes #1233
2018-12-06 06:57:07 +01:00
Matthias 2f0d7a1aea Add specific test 2018-12-05 20:45:11 +01:00
Matthias 3e2fa58029 load pairlists via resolver 2018-12-05 20:44:56 +01:00
Matthias 43031aa3bb Add missing path-error handler for hyperopt 2018-12-05 20:44:41 +01:00
Matthias 1a10e12861 Documentation and developer documentation 2018-12-05 19:48:59 +01:00
Matthias 21906e4892 Remove duplicate code 2018-12-05 19:48:50 +01:00
Matthias 616ca0237e Merge pull request #1385 from freqtrade/feat/improve_travis
Add commit and message to container
2018-12-05 19:16:37 +01:00
Samuel Husso ed22419b32 Merge pull request #1391 from freqtrade/pyup-scheduled-update-2018-12-05
Scheduled daily dependency update on wednesday
2018-12-05 15:22:56 +02:00
pyup-bot 37ebe05c6d Update ccxt from 1.17.581 to 1.17.583 2018-12-05 13:34:06 +01:00
misagh ee26b6bcff Merge branch 'develop' into time_in_force 2018-12-05 10:57:23 +01:00
misagh d12cc39a5e some visual happyness 2018-12-04 20:59:55 +01:00
misagh 910601ba1d in case exchange doesn’t return order info … 2018-12-04 20:50:35 +01:00
misagh e3876bcf0f removing AON as it is not supported in binance. will be added once TIF
is added for other exchanges
2018-12-04 20:36:44 +01:00
misagh b7aa77acdd conflict resolved 2018-12-04 20:28:07 +01:00
Matthias 369a609f61 Merge pull request #1389 from freqtrade/feat/sellreason
publish sellreason in rpc message
2018-12-04 20:27:13 +01:00
Matthias 1c3ce265f1 documentation for pairlists 2018-12-04 20:24:52 +01:00
Matthias 6ab907bef1 Rename config whitelist to pairlist 2018-12-04 20:24:45 +01:00
Matthias 4143e2c032 adapt tests to send sell-reason in sell-message 2018-12-04 19:58:43 +01:00
Matthias 33e9ed5a5e Print sellreason in sell-message 2018-12-04 19:58:26 +01:00
misagh 24f9ea29c6 tests fixed 2018-12-04 17:13:46 +01:00
misagh e7684b446b capital in trade extracted to a separated argument 2018-12-04 17:05:35 +01:00
Samuel Husso cc3b84a8de Merge pull request #1388 from freqtrade/pyup-scheduled-update-2018-12-04
Scheduled daily dependency update on tuesday
2018-12-04 14:55:23 +02:00
pyup-bot 32b6cd9dff Update ccxt from 1.17.574 to 1.17.581 2018-12-04 13:34:07 +01:00
Samuel Husso 4a6cec752d Merge pull request #1383 from freqtrade/remove_unnecessary_test
Remove unnecessary test-file
2018-12-04 12:06:47 +02:00
Matthias bf678164c7 remove default param - fix tests 2018-12-04 07:16:34 +01:00
Matthias ba3218a87d Support multiple sorting variants 2018-12-04 07:12:56 +01:00
Matthias ab60571ac7 Add sample config 2018-12-04 06:13:39 +01:00
Matthias 0929f59680 Refactor pairlist-tests 2018-12-03 20:48:51 +01:00
Matthias 18ad3388b4 Some more tests adapted to pairlists 2018-12-03 20:38:15 +01:00
Matthias ef1208b366 Fix rpc messages 2018-12-03 20:31:25 +01:00
Matthias 1b3ecb8343 Deprecate --dynamic-whitelist 2018-12-03 20:00:18 +01:00
misagh 108d9a1117 function name refactored 2018-12-03 19:55:37 +01:00
misagh 43bafc391f static method added 2018-12-03 19:46:22 +01:00
misagh b5192193fd total amount passed to edge should consider open trades too 2018-12-03 19:45:00 +01:00
Matthias 3360e777a1 Fix flake adn mypy 2018-12-03 19:29:35 +01:00
Matthias 49a6581dfe Merge pull request #1387 from freqtrade/pyup-scheduled-update-2018-12-03
Scheduled daily dependency update on monday
2018-12-03 14:41:20 +01:00
pyup-bot 11da297c25 Update ccxt from 1.17.572 to 1.17.574 2018-12-03 13:34:08 +01:00
Matthias f748a63df2 Merge pull request #1386 from pan-long/patch-2
Correct Edge links
2018-12-03 06:38:44 +01:00
Pan Long 99f7c3752a Correct Edge links
It was pointing to a fork instead of freqtrade/freqtrade
2018-12-03 07:21:01 +08:00
Matthias 3a086aac58 Add commit and message to container 2018-12-02 22:49:30 +01:00
Matthias 26187ef6c7 patch exchange_has 2018-12-02 22:18:14 +01:00
Matthias d09dbfe2e6 Add volumePairList - refactor tests to correct file 2018-12-02 22:07:09 +01:00
Matthias 58c7adae0a Test for blacklist 2018-12-02 22:07:09 +01:00
Matthias 8fd713f3ae validate_whitelist should return the list again 2018-12-02 22:07:09 +01:00
Matthias 1738633efc Fix refresh_whitelist tests 2018-12-02 22:07:09 +01:00
Matthias e8fbe77ebc Refactor static whitelist to module 2018-12-02 22:07:09 +01:00
Matthias bb828c308f Remove unnecessary test-file 2018-12-02 16:03:34 +01:00
Matthias dee6249977 Merge pull request #1381 from freqtrade/pyup-scheduled-update-2018-12-02
Scheduled daily dependency update on sunday
2018-12-02 15:40:35 +01:00
pyup-bot 35d678c505 Update ccxt from 1.17.566 to 1.17.572 2018-12-02 13:34:06 +01:00
Matthias 27c2e80cff Merge pull request #1357 from mishaker/fix_dry_run_stop_price
Fix dry run stop price in case of stoploss on exchange
2018-12-02 09:07:25 +01:00
Matthias 0f4a3365ad Merge pull request #1379 from freqtrade/pyup-scheduled-update-2018-12-01
Scheduled daily dependency update on saturday
2018-12-01 19:12:00 +01:00
pyup-bot b594bc7ccc Update ccxt from 1.17.563 to 1.17.566 2018-12-01 13:34:06 +01:00
misagh a5414b8437 flake8 2018-12-01 13:02:45 +01:00
misagh 2d17346b0e explaining arbitrary stake amount in comment 2018-12-01 13:01:51 +01:00
misagh 7ddbaa70ad USDT to ETH conversion. 1 USDT = 1 ETH 2018-12-01 12:06:48 +01:00
misagh 237dc8290f conflict resolved0 2018-12-01 12:00:03 +01:00
misagh bd673178ce constants removed 2018-12-01 11:56:53 +01:00
misagh 33f1cc13b3 fixing tests 2018-12-01 11:56:16 +01:00
misagh 1d41a91788 stake_amount in case it doesn’t exist 2018-12-01 11:48:41 +01:00
misagh ee62adf4f7 highlight 2018-12-01 11:41:30 +01:00
misagh 4431e3bdb6 position size explanation enriched 2018-12-01 11:40:13 +01:00
misagh 88d277ea55 adding required config for edge 2018-12-01 11:08:18 +01:00
misagh 9c0be99ff7 rounding float at the end 2018-12-01 11:00:33 +01:00
misagh c4f17f1c45 config json updated 2018-12-01 10:58:47 +01:00
misagh 86d9457ea1 removing unnecessary variable before returning the result 2018-12-01 10:58:05 +01:00
misagh 9c987fdedd variable name changed (_final_pairs) 2018-12-01 10:56:33 +01:00
misagh b1c81acfcb another futile one 2018-12-01 10:53:21 +01:00
misagh 042e631f87 rollback on futile change 2018-12-01 10:52:36 +01:00
misagh bf990ec599 test fixed and flake 2018-12-01 10:50:41 +01:00
misagh f100432fe8 conflict resolved0 2018-12-01 10:43:26 +01:00
Misagh 24f573f3b0 log "Found no sell signal for whitelisted ..." changed (#1378)
* sell log enriched and put modify on debug
2018-12-01 10:01:11 +01:00
Matthias e31963f6e1 Merge pull request #1341 from mishaker/stoploss_on_exchange
Stoploss on exchange
2018-12-01 09:46:37 +01:00
Matthias d4f83a7516 Fix missing mock in test_add_stoploss_on_exchange 2018-11-30 20:15:56 +01:00
Matthias f04655c012 Test exceptions in sell-stoploss 2018-11-30 20:13:50 +01:00
Matthias 3ac2106a16 Merge pull request #1290 from freqtrade/fix/backtest_toomanyopen
fix backtesting not respecting max_open_trades
2018-11-30 19:17:09 +01:00
Matthias 8effcc2de5 Merge pull request #1374 from freqtrade/refactor_startupmessges
refactor startup_messages to rpc_manger
2018-11-30 19:15:00 +01:00
misagh 4a2d60370c adding dots at the end of sentences 2018-11-30 18:28:18 +01:00
misagh 7e86ec31be tests added for wallet additional functions 2018-11-30 18:23:16 +01:00
misagh c61ede4182 documentation updated 2018-11-30 18:20:29 +01:00
misagh aadc9f052a conf schema 2018-11-30 18:10:22 +01:00
misagh 11101e6668 config full aded 2018-11-30 18:07:45 +01:00
misagh 12471e012e added tests for position sizing 2018-11-30 17:59:51 +01:00
misagh abd88767f8 Merge branch 'develop' into edge_position 2018-11-30 17:50:06 +01:00
misagh 7767470af8 return stake amount of strategy if edge doesn’t have any 2018-11-30 17:50:03 +01:00
misagh 9d005678c3 Merge branch 'develop' into stoploss_on_exchange 2018-11-30 15:13:43 +01:00
Matthias eedc790b53 Merge pull request #1375 from freqtrade/pyup-scheduled-update-2018-11-30
Scheduled daily dependency update on friday
2018-11-30 14:56:10 +01:00
Matthias 7570a0d0a4 Merge pull request #1376 from mishaker/info_to_debug
"checking sell" INFO log message pollutes logs unnecessarily.
2018-11-30 14:55:46 +01:00
misagh f554647efd “checking sell” message removed to debug 2018-11-30 14:14:31 +01:00
pyup-bot 42c8888fa1 Update ccxt from 1.17.556 to 1.17.563 2018-11-30 13:34:08 +01:00
misagh 8ff82e3dac Merge branch 'develop' into fix_dry_run_stop_price 2018-11-30 10:37:58 +01:00
Matthias efcec736b5 refactor startup_messages to rpc_manger
this cleans up freqtradebot slightly
2018-11-29 20:02:12 +01:00
Matthias 49e44d5481 Merge pull request #1373 from mishaker/fix_edge_stoploss
Should fallback to strategy stoploss if Edge cannot provide.
2018-11-29 19:09:25 +01:00
misagh 74ca34f2de flaking8 2018-11-29 18:45:37 +01:00
misagh 3d37c5d767 edge non existing stoploss fixed. solves #1370 2018-11-29 18:31:08 +01:00
Samuel Husso 6cf897a17a Merge pull request #1369 from freqtrade/add_binance_sampleconfig
Add binance config sample, improve invalid pair message
2018-11-29 18:17:16 +02:00
Matthias a6eb3328d2 Merge pull request #1372 from freqtrade/pyup-scheduled-update-2018-11-29
Scheduled daily dependency update on thursday
2018-11-29 16:17:23 +01:00
pyup-bot bc2f6d3b71 Update ccxt from 1.17.545 to 1.17.556 2018-11-29 13:34:07 +01:00
misagh 6bedcc5d79 log enriched for time in force 2018-11-29 13:22:41 +01:00
misagh a61daed8e9 logs enriched 2018-11-29 12:24:04 +01:00
Matthias cb9104fd8a Add BNB as blacklist to align to documentation 2018-11-29 07:36:37 +01:00
Matthias 38592c6fa6 Add binance config sample, improve invalid pair message 2018-11-29 07:07:47 +01:00
misagh e698590bb2 avoid generating logs on each iteration 2018-11-28 20:04:56 +01:00
misagh 1a5465fb50 logs enriched in case of stop loss on exchange, test fixed 2018-11-28 19:35:10 +01:00
Samuel Husso b090b7f4f0 Merge pull request #1368 from freqtrade/pyup-scheduled-update-2018-11-28
Scheduled daily dependency update on wednesday
2018-11-28 18:07:22 +02:00
misagh c913fef80c stop loss limit when hit, the close price is “average” 2018-11-28 15:45:11 +01:00
misagh e9305b6592 position size fixed 2018-11-28 15:36:32 +01:00
misagh fb755880fa logs added in case stop loss on exchange is hit 2018-11-28 14:16:50 +01:00
misagh da94e97c60 in case trade is not open, then handle_stoploss_on_exchange should not
be called
2018-11-28 13:58:53 +01:00
pyup-bot 50a384130f Update ccxt from 1.17.543 to 1.17.545 2018-11-28 13:34:07 +01:00
misagh 4ffc74d5fa if buy order is rejected or expired the bot should exit the buy loop 2018-11-27 19:05:59 +01:00
Matthias ff8987f517 Merge pull request #1367 from freqtrade/pyup-scheduled-update-2018-11-27
Scheduled daily dependency update on tuesday
2018-11-27 19:04:10 +01:00
misagh 29f680ec5d fix order type test 2018-11-27 17:26:06 +01:00
misagh 7dbf0fed68 stop loss limit order type corrected 2018-11-27 17:09:51 +01:00
misagh 159ac6e657 edge tests fixed for position sizing 2018-11-27 14:02:34 +01:00
pyup-bot 7832fe7074 Update ccxt from 1.17.539 to 1.17.543 2018-11-27 13:34:08 +01:00
Matthias 5fa3548dbe Merge pull request #1145 from freqtrade/feat/improve_travis
improve travis integration, add test for Docker
2018-11-27 07:04:38 +01:00
misagh f5a70750f0 edge real position sizing drafted 2018-11-26 21:06:32 +01:00
misagh 6351fe7a7f test added: stoploss_order_id should be null after migration 2018-11-26 20:24:13 +01:00
misagh 3131788639 Merge branch 'develop' into time_in_force 2018-11-26 19:20:01 +01:00
misagh 7f6fc7e90f Lost in git ! 2018-11-26 19:13:36 +01:00
misagh 86354ed258 conflict resolved 2018-11-26 19:08:58 +01:00
misagh 2135976cb8 Merge branch 'develop' of https://github.com/freqtrade/freqtrade into develop 2018-11-26 19:01:29 +01:00
misagh b63535083e flake8 2018-11-26 18:47:32 +01:00
misagh 1f1770ad5a migration script and and error handling on stop loss order 2018-11-26 18:46:59 +01:00
misagh 17004a5a72 documentation corrected 2018-11-26 18:29:41 +01:00
misagh b2634e8e08 typo corrected 2018-11-26 18:28:13 +01:00
Samuel Husso 823bc3abb6 Merge pull request #1361 from freqtrade/wallets/add_live_test
Test live mode of get_free
2018-11-26 15:15:42 +02:00
Samuel Husso a584327d2f Merge pull request #1363 from freqtrade/pyup-scheduled-update-2018-11-26
Scheduled daily dependency update on monday
2018-11-26 15:13:36 +02:00
pyup-bot d3712c6e40 Update ccxt from 1.17.536 to 1.17.539 2018-11-26 13:34:05 +01:00
Matthias 854af9c124 Merge pull request #1355 from freqtrade/fix/async_followup
async followup PR
2018-11-26 06:55:10 +01:00
Matthias ad8592f316 Test live mode of get_free 2018-11-26 06:40:20 +01:00
Matthias 797a0e8fd0 Merge pull request #1354 from freqtrade/fix/lambda_test
replace  lambda with Magicmock in test
2018-11-26 06:28:01 +01:00
Matthias c38f8b8ae2 Merge pull request #1360 from pan-long/patch-1
Use dot to access attribute in NamedTuple
2018-11-26 06:18:16 +01:00
Pan Long 16eec078d7 Use dot to access attribute in NamedTuple
This should fix the crash in #1359
2018-11-26 09:18:29 +08:00
misagh 9f26022ce5 copy/paste corrected 2018-11-25 22:08:42 +01:00
misagh 962b02b079 one last step before tests 2018-11-25 22:02:59 +01:00
misagh 29c23e3136 added time in force in buy and sell functions 2018-11-25 21:38:11 +01:00
misagh 181424e8ea time in force validator added 2018-11-25 21:09:35 +01:00
misagh ba20b1b5c7 TIF added to constants and json full 2018-11-25 21:05:25 +01:00
misagh 890cef88ab oops, lost in git :/ 2018-11-25 21:02:58 +01:00
misagh fb7b65c909 time in force drafted
time in force drafted
2018-11-25 20:44:40 +01:00
misagh 6c38bde24a some formatting fixed 2018-11-25 20:21:50 +01:00
misagh b579768618 dry run set explicitly to False for live stop loss 2018-11-25 20:20:11 +01:00
misagh 5c257730a8 test added for dry run stop loss sell 2018-11-25 20:16:53 +01:00
misagh 59fc67f85b Merge branch 'develop' of https://github.com/freqtrade/freqtrade into develop 2018-11-25 19:48:51 +01:00
misagh 1ad5ccdfb0 dry run condition when sell occurs 2018-11-25 19:48:46 +01:00
misagh a80c984323 flake8 2018-11-25 19:09:11 +01:00
misagh 92930b2343 test fixed 2018-11-25 19:03:28 +01:00
misagh 5e1fb11124 documentation added for stop loss on exchange 2018-11-25 17:30:06 +01:00
misagh 3e29fbb17a stoploss on exchange added as a parameter to order_types 2018-11-25 17:22:56 +01:00
Matthias ebaf58b0fe Only sort data if necessary 2018-11-25 15:00:50 +01:00
Matthias 8a43611992 Remove get_candle_history (it's now async)
convert sort-test to async
2018-11-25 14:48:15 +01:00
Matthias 745a517795 Fix comment pointing to wrong column 2018-11-25 14:40:21 +01:00
Matthias 317eba2139 Remove dual instanciation of pairinfo named tuple 2018-11-25 14:38:06 +01:00
Matthias fd7184718b replace lambda with Magicmock in test 2018-11-25 14:31:46 +01:00
Matthias 200484ab8b Merge pull request #1352 from freqtrade/combine_resolvers
Combine resolvers
2018-11-25 13:52:48 +01:00
Matthias 5a36dd5d5b Merge pull request #1353 from freqtrade/pyup-scheduled-update-2018-11-25
Scheduled daily dependency update on sunday
2018-11-25 13:51:59 +01:00
pyup-bot e89df448e8 Update ccxt from 1.17.535 to 1.17.536 2018-11-25 13:34:08 +01:00
Matthias 0aa74b8d72 Merge pull request #1348 from mishaker/walletizer
Getting available balance from wallet instead of API call.
2018-11-25 13:25:18 +01:00
misagh e4744c1ba4 stop loss on exchanged removed from doc 2018-11-25 11:31:30 +01:00
misagh dcae3a2644 test of check_consistency added 2018-11-25 11:29:04 +01:00
misagh 664b96173e removing NotImplementedError from stoploss_limit 2018-11-25 10:54:36 +01:00
Matthias 1d35428c8d Rename get_valid_objects to get_valid object
it only ever returns one object ...
2018-11-25 10:08:27 +01:00
Matthias a3477e07eb Remove constructor, it's not needed in the baseclass 2018-11-25 09:55:36 +01:00
misagh 266bd7b9b6 error message improved 2018-11-24 21:42:15 +01:00
Matthias 20de8c82e4 Convert to Pathlib 2018-11-24 20:39:16 +01:00
Matthias cc7b820978 Move hyperoptresolver to resolvers package 2018-11-24 20:14:08 +01:00
misagh 519b1f00e2 adding strategy config consistency function 2018-11-24 20:12:50 +01:00
Matthias 2c0d0946e6 Small stylistic improvements to strategyresolver 2018-11-24 20:02:29 +01:00
Matthias 21a093bcdb extract resolvers to IResolvers and it's own package 2018-11-24 20:00:02 +01:00
misagh c8a0956e1b fixed test handle_stoploss_on_exchange 2018-11-24 19:12:00 +01:00
Matthias e442390b1b Merge pull request #1350 from freqtrade/update_ordertype_docs
Add Note about order types support
2018-11-24 19:09:23 +01:00
misagh b5192880df [WIP] adding tests for handle_stoploss_on_exchange. 2018-11-24 19:00:59 +01:00
misagh fe8927136c typo 2018-11-24 18:36:07 +01:00
misagh b2c0b20a58 added real tests for stop on exchange in dry-run 2018-11-24 18:26:04 +01:00
misagh 000711b025 added stoploss_limit_order for dry-run 2018-11-24 18:08:11 +01:00
misagh 870631f324 1) comments added to handle_sl 2) dry-run force price removed 2018-11-24 17:32:25 +01:00
misagh 531d9ecd0c docstring added 2018-11-24 17:10:51 +01:00
misagh afd0a054b2 typo corrected 2018-11-24 17:08:12 +01:00
misagh a9ec5c6699 simplifying if conditions 2018-11-24 17:07:35 +01:00
misagh 1a8e9ebc0f stoploss_order_id added to migration script 2018-11-24 16:53:10 +01:00
misagh 63c2ea110a Not sure why those arguments were there ! 2018-11-24 16:41:17 +01:00
misagh 29347a6931 adding get_free to wallet 2018-11-24 16:37:28 +01:00
Matthias 2b0b7ffa5e Merge pull request #1351 from freqtrade/pyup-scheduled-update-2018-11-24
Scheduled daily dependency update on saturday
2018-11-24 14:09:15 +01:00
pyup-bot 29a4c99d1d Update pytest from 4.0.0 to 4.0.1 2018-11-24 13:34:07 +01:00
pyup-bot 412a627d9e Update ccxt from 1.17.533 to 1.17.535 2018-11-24 13:34:05 +01:00
Matthias 3e8de28b51 Add Note about order types support 2018-11-24 13:26:36 +01:00
Matthias 805f509498 Merge branch 'develop' into fix/backtest_toomanyopen 2018-11-24 10:39:16 +01:00
Matthias f88a113109 Merge pull request #1349 from freqtrade/pyup-scheduled-update-2018-11-23
Scheduled daily dependency update on friday
2018-11-24 09:39:12 +01:00
misagh dedf1ff703 refactoring 2018-11-23 20:51:23 +01:00
misagh 89eb3d9f36 blank line removed 2018-11-23 20:49:00 +01:00
misagh 1c2c19b12c the complex in the life of flake8 resolved 2018-11-23 20:47:17 +01:00
misagh 9144a8f79d tests fixed 2018-11-23 20:28:01 +01:00
misagh 5ee2faa182 adding stop loss on exchange after the buy order is fulfilled not
before.
2018-11-23 19:17:36 +01:00
misagh fea77824d0 handle stop loss on exchange added 2018-11-23 15:17:36 +01:00
pyup-bot 605211dbaf Update scikit-learn from 0.20.0 to 0.20.1 2018-11-23 13:34:09 +01:00
pyup-bot 270624c0c5 Update ccxt from 1.17.529 to 1.17.533 2018-11-23 13:34:08 +01:00
misagh a9f04609d3 tests fixed 2018-11-23 10:17:10 +01:00
misagh 27a6dcf3fc getting available balance from wallet instead of API call. 2018-11-22 21:23:35 +01:00
misagh 1dde56790c final broken test fixed 2018-11-22 21:12:49 +01:00
misagh 6f0025c6de documentation written 2018-11-22 21:07:33 +01:00
misagh 7faafea8a2 added test for cancelling stop loss before sell 2018-11-22 21:01:39 +01:00
misagh 07ac902451 test exchange added 2018-11-22 20:30:31 +01:00
misagh ecb2c4dca3 bloody flake8 2018-11-22 19:38:20 +01:00
misagh cc1422d448 flake8 2018-11-22 19:27:32 +01:00
misagh 3418592908 freqtradebot test added for orders on exchange 2018-11-22 19:25:26 +01:00
misagh 24df093a85 test: only implemented for binance 2018-11-22 17:41:01 +01:00
misagh 2461d86c8d dry run should consider stop loss is hit on limit price 2018-11-22 17:24:45 +01:00
misagh 3a1c378325 typing bugs 2018-11-22 17:14:22 +01:00
Matthias e4d9d72ff1 Merge pull request #1347 from freqtrade/pyup-scheduled-update-2018-11-22
Scheduled daily dependency update on thursday
2018-11-22 17:08:32 +01:00
misagh bbe8e4e494 flake8 2018-11-22 17:07:37 +01:00
misagh da5617624c cancelling stop loss order before selling 2018-11-22 17:02:02 +01:00
misagh fad7593935 doesn’t have to create another Trade for SL. can be cumulated into the
same.
2018-11-22 16:53:50 +01:00
misagh bb37b56dea adding stop loss order id to Trade 2018-11-22 16:47:52 +01:00
misagh 3b7e05e07b stop loss order added right after a buy order is executued 2018-11-22 16:26:24 +01:00
misagh bfbdddff26 stoploss limit order added to exchange 2018-11-22 16:24:40 +01:00
pyup-bot f73a18c56c Update ccxt from 1.17.522 to 1.17.529 2018-11-22 13:34:06 +01:00
misagh 238dd6413c Merge branch 'develop' into stoploss_on_exchange 2018-11-22 09:39:01 +01:00
Matthias 1810fc9efa Merge pull request #1346 from mishaker/fix_python_beginner_mistake
Refactoring a bit ...
2018-11-22 06:02:15 +01:00
Matthias 8e62fc1c03 Merge pull request #1337 from mishaker/wallet
Wallet data structure added. it is initialized on boot then updated right after any trade happens on the exchange.
2018-11-22 06:00:49 +01:00
misagh eb53281434 python beginner problem resolved 2018-11-22 00:04:20 +01:00
misagh 4b86b2b7e3 Happy flake8 ! 2018-11-21 23:36:48 +01:00
misagh 3a2134db24 removed Optional 2018-11-21 23:35:44 +01:00
misagh 4d75e9059c None ripped off for optional as wallet must have exchange and currency 2018-11-21 21:05:20 +01:00
misagh b129750f4d adding “optional” in str 2018-11-21 19:58:28 +01:00
misagh 88f61581d9 1) NamedTuple refactored 2) Missing data handled 2018-11-21 19:47:51 +01:00
misagh cb3cf960d7 tests added in case of missing data 2018-11-21 19:47:28 +01:00
Matthias 64028647a0 Merge pull request #571 from stephendade/userhyper
Separated out custom hyperopts
2018-11-21 19:14:30 +01:00
misagh aeb372c2f0 test wallet when api return changes 2018-11-21 17:54:14 +01:00
misagh 5b68940213 update wallet in casse order remaining is zero 2018-11-21 17:48:53 +01:00
misagh 68f81aa2af test wallets moved to tests folder 2018-11-21 17:27:45 +01:00
Matthias d2ae5e9201 Merge pull request #1343 from freqtrade/pyup-scheduled-update-2018-11-21
Scheduled daily dependency update on wednesday
2018-11-21 16:08:45 +01:00
Matthias 0f21c80335 Merge pull request #1344 from mishaker/fix_twice_refresh_ticker_call
Fix twice refresh ticker call
2018-11-21 16:08:24 +01:00
misagh c1673aaba3 Merge branch 'develop' into fix_twice_refresh_ticker_call 2018-11-21 14:01:08 +01:00
misagh 64129897f9 refresh_ticker should be called just once per iteration. 2018-11-21 14:00:15 +01:00
pyup-bot d745e577b4 Update ccxt from 1.17.518 to 1.17.522 2018-11-21 13:34:06 +01:00
Matthias a3b6004115 IHyperopt: all methods static, somef ixes for mypy 2018-11-20 19:41:07 +01:00
Matthias 7757c53b06 Small fixes 2018-11-20 17:43:49 +01:00
Matthias 5dd013c3b1 Rename hyperopt interface and resolver 2018-11-20 17:40:45 +01:00
Matthias 5a550ef2af Fix docs typo in hyperopt 2018-11-20 17:36:17 +01:00
Matthias 3d006b6cf9 Merge pull request #1342 from freqtrade/pyup-scheduled-update-2018-11-20
Scheduled daily dependency update on tuesday
2018-11-20 17:29:22 +01:00
pyup-bot ce092742da Update ccxt from 1.17.513 to 1.17.518 2018-11-20 13:34:07 +01:00
Matthias e69f943911 Add missing semicolon 2018-11-19 20:07:35 +01:00
misagh b50250139e Drafting stoploss on exchange 2018-11-19 20:02:26 +01:00
Matthias d72e605cb7 Merge pull request #1330 from freqtrade/feat/diff_order_types
Add support for different order types
2018-11-19 19:55:30 +01:00
Matthias 2ce13713fb Merge pull request #1340 from freqtrade/pyup-scheduled-update-2018-11-19
Scheduled daily dependency update on monday
2018-11-19 19:20:07 +01:00
pyup-bot cf2d68501c Update ccxt from 1.17.502 to 1.17.513 2018-11-19 13:34:07 +01:00
misagh 003480ad90 flake indentation 2018-11-19 13:01:17 +01:00
misagh b680681b34 updating wallet at handle timeout functions too 2018-11-19 11:16:07 +01:00
misagh c033378048 change dict type to Any 2018-11-18 14:57:03 +01:00
misagh 9c549f4513 removing unnecessary private function 2018-11-18 14:39:31 +01:00
misagh 608ce98e1a moving wallets to root 2018-11-18 14:38:31 +01:00
misagh a92619f18c Added empty lines related to last commit removed 2018-11-18 14:34:31 +01:00
misagh 7cb8b28f58 wallet sync added 2018-11-17 23:03:07 +01:00
misagh 606e41d574 wallet tests added 2018-11-17 22:58:27 +01:00
misagh f4bb203782 removing persistence update 2018-11-17 21:59:21 +01:00
misagh d5b47abe98 Wallet table removed 2018-11-17 21:31:06 +01:00
misagh a0658bb504 comments added 2018-11-17 21:27:42 +01:00
misagh 12f07ee126 space removed 2018-11-17 21:26:41 +01:00
misagh b815c8fe2d updating wallets whenever a trade happens 2018-11-17 21:22:54 +01:00
misagh afe52efc8a removing wallet from freq 2018-11-17 21:17:39 +01:00
misagh 82cb0e4d95 putting wallets into a class (doesn’t need to be in persistence) 2018-11-17 21:16:32 +01:00
Matthias b3e08831f7 Remove rate for market orders 2018-11-17 20:14:50 +01:00
Matthias c11984d943 Check if exchange supports all configured market types 2018-11-17 19:54:55 +01:00
Matthias 968184ef0d Swap default mode to all limit (defaults to how it was before) 2018-11-17 19:40:53 +01:00
misagh 69dd56b237 wallet sync drafted 2018-11-17 18:47:13 +01:00
Matthias 2799994098 Merge pull request #1336 from freqtrade/pyup-scheduled-update-2018-11-17
Scheduled daily dependency update on saturday
2018-11-17 13:48:38 +01:00
Matthias 492868a966 Seperate different tests within one test clearer 2018-11-17 13:34:23 +01:00
pyup-bot 681659f2d2 Update ccxt from 1.17.500 to 1.17.502 2018-11-17 13:34:06 +01:00
Matthias a9a157af0f Align tests and test if ordertype is passed to ccxt correctly 2018-11-17 13:29:42 +01:00
Matthias ef1e20bfe8 Don't add default value for ordertype
sort parameters to align with ccxt
2018-11-17 13:29:24 +01:00
Matthias 543873263a remove need for escaping quote 2018-11-17 13:13:16 +01:00
Matthias e485aff597 Test failed load on invalid ordertypes 2018-11-17 13:12:11 +01:00
Matthias 9ba281c141 add supported limit values 2018-11-17 13:05:35 +01:00
Matthias 54a86d72f2 Raise error if one of the required ordertypes is not present 2018-11-17 12:59:16 +01:00
Matthias 3ab0cf49af Add order_types to sample strategy 2018-11-17 10:26:15 +01:00
Matthias 6e78efd971 Document "order_types" setting 2018-11-17 10:24:42 +01:00
Matthias 24ed9a8b7d Add loading order_types from config file 2018-11-17 10:14:18 +01:00
Matthias 797de3e0c3 Merge pull request #1333 from freqtrade/pyup-scheduled-update-2018-11-16
Scheduled daily dependency update on friday
2018-11-17 09:37:48 +01:00
pyup-bot b7abf7dda9 Update ccxt from 1.17.498 to 1.17.500 2018-11-16 13:34:08 +01:00
Matthias de57da3249 Merge pull request #1328 from mishaker/edge_cli
Edge cli
2018-11-15 20:14:03 +01:00
Matthias cb1ab0aa49 Merge pull request #1332 from freqtrade/fix_missing_json_mock
Fix missing mock in backtesting
2018-11-15 20:13:47 +01:00
Matthias 98df3c8103 Fix missing mock in backtesting 2018-11-15 20:02:48 +01:00
misagh db8c8ea4a4 added a space in help 2018-11-15 20:02:07 +01:00
misagh d05c671a7e adding edge args to bot-usage 2018-11-15 19:54:17 +01:00
Matthias f1340142f0 Merge pull request #1331 from freqtrade/pyup-scheduled-update-2018-11-15
Scheduled daily dependency update on thursday
2018-11-15 19:51:31 +01:00
Matthias 44c682724d Merge pull request #1327 from mishaker/max_open_trades
Ignoring max_open_trades if it is -1 in config.
2018-11-15 19:40:05 +01:00
Matthias dcf9930858 improve hyperopt documentation (links) 2018-11-15 19:36:04 +01:00
Matthias e6baa9ccf2 Switch tests to kwarguments 2018-11-15 19:31:24 +01:00
pyup-bot 52f4d700ca Update pytest from 3.10.1 to 4.0.0 2018-11-15 13:34:08 +01:00
pyup-bot 23295514f6 Update ccxt from 1.17.494 to 1.17.498 2018-11-15 13:34:07 +01:00
misagh 69619030f3 removing unnecessary args from config 2018-11-15 10:50:40 +01:00
misagh 03e6caa501 adding notice about Edge ignoring ROI and TSL in doc 2018-11-15 10:46:36 +01:00
misagh 1cfd19aee3 removing unnecessary args for edge 2018-11-15 10:44:33 +01:00
misagh f666d1596b renaming edge to edge_cli for command line version 2018-11-15 10:31:56 +01:00
Matthias 6a71f80a9e Add support for different order types 2018-11-15 06:58:24 +01:00
Matthias 4f800bfbc8 Fix pickling-error 2018-11-14 20:25:43 +01:00
misagh bb9a1e5f9f edge cli tests added 2018-11-14 19:14:34 +01:00
Matthias 23958ba96a Merge pull request #1322 from freqtrade/feat/add_whitelist_rpc
Feat/add whitelist rpc
2018-11-14 19:13:00 +01:00
misagh 9698eee934 documentation added 2018-11-14 17:14:44 +01:00
misagh ca22a116ad timerange added to args 2018-11-14 17:14:37 +01:00
misagh 5d73b303fe unnecessary libraries removed + arg help enriched 2018-11-14 16:49:16 +01:00
misagh 0767718a17 clear help added to stop losses arg 2018-11-14 16:38:55 +01:00
misagh dd47d7adb4 cli blank line added to readability 2018-11-14 16:37:26 +01:00
misagh b0e4aa8eff stop loss range added to args 2018-11-14 16:31:23 +01:00
Matthias 454fba2328 Merge pull request #1329 from freqtrade/pyup-scheduled-update-2018-11-14
Scheduled daily dependency update on wednesday
2018-11-14 14:52:06 +01:00
misagh 36030176bb nb_trades and avg_trade_duration added to cli 2018-11-14 13:38:23 +01:00
misagh ac0c931492 adding number of trades + average trade duration to edge info 2018-11-14 13:38:04 +01:00
pyup-bot 7fb8ae3e1b Update py_find_1st from 1.1.2 to 1.1.3 2018-11-14 13:34:09 +01:00
pyup-bot 9baf228e8d Update ccxt from 1.17.492 to 1.17.494 2018-11-14 13:34:08 +01:00
misagh 5de3f1d9dd showing result in tabular 2018-11-14 13:25:44 +01:00
misagh 95cbbf1cb5 adding edge configuration to cli 2018-11-14 12:53:20 +01:00
misagh cf974168e9 Edge cli drafted 2018-11-14 12:37:15 +01:00
misagh 51dfd2bf47 If max_open_trade=-1 means it should be ignored. 2018-11-14 11:37:53 +01:00
Matthias 5c8544a425 Merge pull request #1229 from mishaker/money_mgt
Edge Positioning
2018-11-13 19:33:59 +01:00
Matthias 7fff389f34 Merge pull request #1325 from freqtrade/pyup-scheduled-update-2018-11-13
Scheduled daily dependency update on tuesday
2018-11-13 15:14:15 +01:00
pyup-bot 4e64bc3d29 Update ccxt from 1.17.491 to 1.17.492 2018-11-13 13:34:07 +01:00
Matthias bdba6186d8 Fix doc-typos 2018-11-12 19:43:20 +01:00
Matthias 79b255179d Merge pull request #1324 from freqtrade/pyup-scheduled-update-2018-11-12
Scheduled daily dependency update on monday
2018-11-12 19:20:26 +01:00
pyup-bot 028139fa3a Update pytest from 3.10.0 to 3.10.1 2018-11-12 13:34:08 +01:00
pyup-bot c29543dd6c Update ccxt from 1.17.489 to 1.17.491 2018-11-12 13:34:07 +01:00
Matthias 6e1bbb5c88 Merge pull request #1323 from freqtrade/pyup-scheduled-update-2018-11-11
Scheduled daily dependency update on sunday
2018-11-11 14:36:05 +01:00
pyup-bot 261cd7746b Update sqlalchemy from 1.2.13 to 1.2.14 2018-11-11 13:34:07 +01:00
pyup-bot ef2c31b543 Update ccxt from 1.17.488 to 1.17.489 2018-11-11 13:34:06 +01:00
Matthias 060a1b3fbc Add /whitelist to help message 2018-11-10 20:16:20 +01:00
Matthias 08ef2730a9 Add /whitelist call to telegram 2018-11-10 20:15:06 +01:00
Matthias 62402351b3 Clarify volume selection for dynamic whitelist 2018-11-10 20:14:46 +01:00
Matthias 02527eeea4 Add rpc_whitelist call 2018-11-10 20:07:09 +01:00
Matthias b3157fc499 Merge pull request #1321 from freqtrade/pyup-scheduled-update-2018-11-10
Scheduled daily dependency update on saturday
2018-11-10 19:37:09 +01:00
misagh 94f56af77d Merge branch 'develop' into money_mgt 2018-11-10 18:45:27 +01:00
misagh 9bbaeb4e6f mypy expression 2018-11-10 18:39:49 +01:00
misagh aacc1d5004 removing total capital in favour of stake amount 2018-11-10 18:28:05 +01:00
misagh d613553306 base position on stake amount instead of total capital 2018-11-10 18:22:34 +01:00
misagh 7dd74c374a flake happiness provided 2018-11-10 18:09:32 +01:00
misagh 97fd33d752 adding test for process 2018-11-10 18:03:46 +01:00
misagh 523a9a603c fix tests 2018-11-10 17:20:11 +01:00
pyup-bot 0f2ddbbef2 Update ccxt from 1.17.485 to 1.17.488 2018-11-10 13:34:06 +01:00
Matthias 2e4e5c86da Merge pull request #1320 from freqtrade/contribute_simplify
point out "good first issue" label
2018-11-10 12:45:30 +01:00
misagh 4dcd15da1d improving documentation for positioning 2018-11-09 20:59:28 +01:00
misagh 617a58402f putting edge adjust function in _process not in create_trade 2018-11-09 20:52:03 +01:00
misagh 12e735e831 1) extracting edge_conf to a fixture
2) test cased adjusted to Backtesting
3) Formatted backtesting_details a bit
2018-11-09 20:51:15 +01:00
Matthias b41633cfe3 point out "good first issue" label 2018-11-09 20:26:10 +01:00
Matthias 59cd4fe0ef Remove boilerplate comments 2018-11-09 19:34:46 +01:00
Matthias 292962d64d Fix tests 2018-11-09 19:34:18 +01:00
Matthias 610d5210ce Merge pull request #1318 from freqtrade/pyup-scheduled-update-2018-11-09
Scheduled daily dependency update on friday
2018-11-09 14:38:48 +01:00
pyup-bot 1840695a1c Update requests from 2.20.0 to 2.20.1 2018-11-09 13:34:08 +01:00
pyup-bot 1db9169cfc Update ccxt from 1.17.481 to 1.17.485 2018-11-09 13:34:07 +01:00
Matthias 5c5fe4c13a Fix test 2018-11-09 07:14:43 +01:00
Matthias 272ff51d51 correctly patch exchange 2018-11-09 07:13:20 +01:00
Matthias 56dcf080a9 Add explicit test for parallel trades 2018-11-09 07:13:20 +01:00
Matthias 93429a58b2 remove TODO 2018-11-09 07:13:20 +01:00
Matthias 9cd2ed5a16 fix hyperopt get_timeframe mock 2018-11-09 07:13:20 +01:00
Matthias fa4c199aa6 fix some mismatches after rebase 2018-11-09 07:13:20 +01:00
Matthias 2371d1e696 Fix backtest test (don't use 8m file if we use 1m tickers) 2018-11-09 07:13:20 +01:00
Matthias 66487f2a13 require start/end-date argument in backtest 2018-11-09 07:13:20 +01:00
Matthias 83a8d79115 Fix alternate buy/sell (this should respect the sell signal!) 2018-11-09 07:13:20 +01:00
Matthias db17ccef2b Adapt backtesting-tests to new backtest-logic 2018-11-09 07:13:20 +01:00
Matthias 03cda8e23e remove meaningless backtesting test 2018-11-09 07:12:41 +01:00
Matthias 6729dfa6d3 Add get_timeframe mock for hyperopt 2018-11-09 07:12:41 +01:00
Matthias 96efd12a31 add new options to hyperopt 2018-11-09 07:12:41 +01:00
Matthias e94da7ca41 inverse backtest logic to loop over time - not pairs (more realistic) 2018-11-09 07:12:41 +01:00
Matthias cc3d05488b Merge pull request #1206 from xmatthias/contributing
Improve Contributing documentation
2018-11-09 06:56:30 +01:00
Matthias d8c224c212 Merge pull request #1317 from freqtrade/pyup-scheduled-update-2018-11-08
Scheduled daily dependency update on thursday
2018-11-08 14:36:24 +01:00
misagh aefc20738a adding dot to the end of the phrase. 2018-11-08 14:18:07 +01:00
misagh a7dc8f5f4f adding edge configuration to configuration.md and removed whitespaces 2018-11-08 14:16:46 +01:00
misagh 5d850825f5 adding a notice about the incompatibility of Edge with Dynamic whitelist 2018-11-08 14:10:52 +01:00
pyup-bot cca371c573 Update ccxt from 1.17.480 to 1.17.481 2018-11-08 13:34:06 +01:00
misagh 6d80c03877 removing raise KeyError in test 2018-11-08 00:28:20 +01:00
misagh e5c6499706 assigning strategy to edge from FreqtradeBot 2018-11-08 00:22:46 +01:00
Matthias 7b62e71f23 Fix some tests and rebase issues 2018-11-07 20:45:52 +01:00
misagh 866da8aaa1 reinitializing Edge calculated data in case of inability to download
backtesting data
2018-11-07 19:24:53 +01:00
misagh 3330d327ed removing reserve keyword “filter”: replaced by “adjust” 2018-11-07 19:03:08 +01:00
misagh 7b80985533 comments on recursive function + indentation of function declaration 2018-11-07 19:00:18 +01:00
misagh 934dd97eb2 adding init for edge test folder 2018-11-07 18:54:21 +01:00
misagh 96a43327ca _pair_info moved to class header for reusibility 2018-11-07 18:52:15 +01:00
misagh b425cc3e3b adding explanation regarding max trade duration and interval 2018-11-07 18:33:35 +01:00
misagh f75606d295 formulas markdown style 2018-11-07 18:27:10 +01:00
misagh 5bd3bae5af unifying default value explanations 2018-11-07 18:24:13 +01:00
misagh bd1b05828e typos in documentation corrected 2018-11-07 18:19:58 +01:00
misagh 6d63de1932 removing unnecessary lib 2018-11-07 18:15:04 +01:00
misagh 553e5656ac forcestoploss refactored 2018-11-07 18:12:46 +01:00
misagh 6838ae0591 conflict resolved => new backtest low and high params 2018-11-07 17:56:21 +01:00
Matthias a96112f631 Merge pull request #1316 from freqtrade/pyup-scheduled-update-2018-11-07
Scheduled daily dependency update on wednesday
2018-11-07 15:20:25 +01:00
pyup-bot f4b626eda3 Update ccxt from 1.17.476 to 1.17.480 2018-11-07 13:34:07 +01:00
Matthias 8044846d37 Fix some refactoring problems 2018-11-07 07:05:40 +01:00
Stephen Dade 477515c4b5 Now using resolver for custom hyperopts 2018-11-07 06:58:20 +01:00
Stephen Dade e0f420983e Updated logger in custom_hyperopt 2018-11-07 06:55:28 +01:00
Stephen Dade 40368bd1b2 Added more hyperopt documentation 2018-11-07 06:55:27 +01:00
Stephen Dade 5816d1c1bd Updated documentation for new hyperopt 2018-11-07 06:52:25 +01:00
Stephen Dade 469db0d434 Decoupled custom hyperopts from hyperopt.py 2018-11-07 06:26:16 +01:00
misagh 23d3a7f31e capital after dots and default values corrected 2018-11-06 20:11:15 +01:00
misagh 1b457e902c config initializer refactored 2018-11-06 19:45:41 +01:00
misagh bcecaa69d4 removing global variable modification 2018-11-06 19:41:46 +01:00
Matthias e0489878d8 Merge pull request #1306 from xmatthias/feat/functional_tests
Funcional tests / backtest stoploss alignment
2018-11-06 19:25:31 +01:00
misagh 133ba5d6a1 moving stop loss range to init as it doesn’t need to be called on each
iteration
2018-11-06 19:16:20 +01:00
misagh 5c38b92a75 simplifying calculations to be more readable 2018-11-06 19:05:42 +01:00
Samuel Husso 8adaaf37e0 Merge pull request #1315 from freqtrade/pyup-scheduled-update-2018-11-06
Scheduled daily dependency update on tuesday
2018-11-06 14:56:58 +02:00
pyup-bot 7278cdc7d5 Update ccxt from 1.17.469 to 1.17.476 2018-11-06 13:34:06 +01:00
Matthias d95ae135a8 Merge pull request #1314 from freqtrade/pyup-scheduled-update-2018-11-05
Scheduled daily dependency update on monday
2018-11-05 20:07:56 +01:00
misagh 5754e51960 more typos 2018-11-05 17:32:12 +01:00
misagh 8a25490146 Typo corrected 2018-11-05 17:28:07 +01:00
misagh 4fbabd3b99 Doc for Edge WIP 3 2018-11-05 17:24:11 +01:00
misagh 49d30ad0e2 doc WIP 2 2018-11-05 15:55:35 +01:00
misagh cc41317670 documentation WIP 1 2018-11-05 15:49:10 +01:00
misagh 2c0fc3c735 Test latex images 2018-11-05 14:36:10 +01:00
pyup-bot f92d229f2e Update pytest from 3.9.3 to 3.10.0 2018-11-05 13:34:12 +01:00
pyup-bot 9f03c26c9a Update numpy from 1.15.3 to 1.15.4 2018-11-05 13:34:10 +01:00
pyup-bot 8a5e4c3f30 Update cachetools from 2.1.0 to 3.0.0 2018-11-05 13:34:09 +01:00
pyup-bot 3666e01396 Update ccxt from 1.17.464 to 1.17.469 2018-11-05 13:34:07 +01:00
misagh ed24d96a79 some formatting for flake8 2018-11-04 18:57:57 +01:00
misagh 8ea9b3746b passing pair to get_trade_stake_amount 2018-11-04 18:51:54 +01:00
misagh 714ac6dd08 Merge branch 'develop' into money_mgt 2018-11-04 18:49:21 +01:00
misagh 120655d262 fixing tests for namedtuple 2018-11-04 18:43:57 +01:00
misagh 14bfd4b7ee using named tuples for keeping pairs data 2018-11-04 18:11:58 +01:00
Matthias 67618e5db5 Merge pull request #1313 from freqtrade/pyup-scheduled-update-2018-11-04
Scheduled daily dependency update on sunday
2018-11-04 13:43:54 +01:00
pyup-bot 81f971f13e Update ccxt from 1.17.459 to 1.17.464 2018-11-04 13:34:07 +01:00
Matthias 7e5fd82f25 Merge pull request #1269 from freqtrade/feat/force_buy
add /forcebuy to telgram handler
2018-11-04 09:25:13 +01:00
Matthias 31a51bd96c Merge pull request #1312 from freqtrade/pyup-scheduled-update-2018-11-03
Scheduled daily dependency update on saturday
2018-11-04 09:17:24 +01:00
misagh d7821acbf0 refreshing pairs on each iteration 2018-11-03 14:33:17 +01:00
misagh b6d4e11e88 added minimum win rate to config 2018-11-03 14:31:34 +01:00
pyup-bot 4ab7a0fb5c Update urllib3 from 1.24 to 1.24.1 2018-11-03 13:34:07 +01:00
pyup-bot 7155e5cfeb Update ccxt from 1.17.455 to 1.17.459 2018-11-03 13:34:05 +01:00
misagh f6498bf5f7 beginning 2018-11-02 22:13:38 +01:00
misagh 9cb660776c money_mgt added 2018-11-02 20:52:46 +01:00
misagh f77fa6b592 misharizing temporarily for doc 2018-11-02 20:51:55 +01:00
misagh b57ae20af4 edge doc file added 2018-11-02 20:49:31 +01:00
misagh 83b3323c56 formating md 2018-11-02 20:48:35 +01:00
misagh 85768fcc51 beginning of doc 2018-11-02 20:35:46 +01:00
misagh 7e7af07c04 Merge branch 'develop' into money_mgt 2018-11-02 20:27:56 +01:00
misagh ece1c8a702 flake8 again and again and again and again
https://www.youtube.com/watch?v=MuSK3pDDYD4
2018-11-02 20:12:48 +01:00
misagh d1ba994e54 expectancy test completed 2018-11-02 20:07:45 +01:00
misagh 237233c300 renaming tests 2018-11-02 19:59:06 +01:00
misagh 2ef2754ffd flake8 happiness satisfied 2018-11-02 19:55:41 +01:00
misagh 3eeaa50fe5 stoploss and sell signal tests done 2018-11-02 19:54:32 +01:00
Matthias 62b546b180 Merge pull request #1311 from freqtrade/reinstate_plotdf
Reinstate df - which was removed in #1287
2018-11-02 19:21:05 +01:00
misagh bb791eac7e backtesting remove from import + whitespace removed 2018-11-02 19:19:28 +01:00
Matthias 7f3b4a97dd Reinstate df - which was removed in #1287 2018-11-02 19:14:50 +01:00
misagh 333d505b66 OHLC validation corrected 2018-11-02 19:01:37 +01:00
misagh 080ecae332 whitelist conflict resolved with develop branch 2018-11-02 18:59:31 +01:00
misagh 05b8010460 removing unnecessary test cases 2018-11-02 18:10:03 +01:00
misagh 2f6aafe66c Edge calculation refactored: removing redundant calculations 2018-11-02 18:07:38 +01:00
Matthias 9cadb188d7 Merge pull request #1276 from freqtrade/fix/1272
solve /balance crashes
2018-11-02 16:05:42 +01:00
Matthias efd59ed9ad Merge pull request #1302 from freqtrade/fix/whitelistprobs
Fix no tickerdata for pair for open trades
2018-11-02 16:03:09 +01:00
Matthias 509b1901b3 Merge pull request #1310 from freqtrade/pyup-scheduled-update-2018-11-02
Scheduled daily dependency update on friday
2018-11-02 14:34:53 +01:00
pyup-bot 17895282a1 Update ccxt from 1.17.448 to 1.17.455 2018-11-02 13:34:06 +01:00
Matthias b3e144f317 Merge pull request #1308 from freqtrade/pyup-scheduled-update-2018-11-01
Scheduled daily dependency update on thursday
2018-11-01 13:56:23 +01:00
pyup-bot afc1329126 Update sqlalchemy from 1.2.12 to 1.2.13 2018-11-01 13:34:08 +01:00
pyup-bot 92f9c828e6 Update ccxt from 1.17.439 to 1.17.448 2018-11-01 13:34:07 +01:00
Matthias 8316acfa78 Add column description to test-cases 2018-11-01 13:16:10 +01:00
Matthias 95d271ca5d Fix ROI close-rate calculation to work with fees - adjust tests 2018-11-01 13:14:59 +01:00
Matthias c21b45647d Fix smoe comments in persistence 2018-11-01 13:05:57 +01:00
Samuel Husso dedb91645c Merge pull request #1307 from freqtrade/pyup-scheduled-update-2018-10-31
Scheduled daily dependency update on wednesday
2018-10-31 15:56:39 +02:00
pyup-bot eab15e09f5 Update ccxt from 1.17.436 to 1.17.439 2018-10-31 13:34:07 +01:00
Matthias a321d0a820 Short descriptors 2018-10-30 20:49:12 +01:00
Matthias daa9863d0b Try adding headers 2018-10-30 20:45:32 +01:00
Matthias 79d1d63e6f Align data (by halfing all data) 2018-10-30 20:42:34 +01:00
Matthias 8c93760a6d simplify some code 2018-10-30 20:23:31 +01:00
Matthias fe2c158e59 Adjust sell-rate to new backtesting (respects roi/stoploss) 2018-10-30 20:13:56 +01:00
Matthias f96f0cdea7 Add additional comment 2018-10-30 20:02:31 +01:00
Matthias 9e921d4410 refactor General bt-utils out of detailed backtest file 2018-10-30 20:02:01 +01:00
Matthias e442e22a20 refactorign 2018-10-30 19:58:06 +01:00
Matthias 9798e881cb refactor sell_r to sell_reason 2018-10-30 19:44:31 +01:00
Matthias 3679b0948a cleanup interface 2018-10-30 19:37:45 +01:00
Matthias fc3f8b436d some more cleanup 2018-10-30 19:36:19 +01:00
Matthias b383113d6c Test open / close time - small refactorings 2018-10-30 19:33:32 +01:00
Samuel Husso 8559b2dc23 Merge pull request #1305 from freqtrade/pyup-scheduled-update-2018-10-30
Scheduled daily dependency update on tuesday
2018-10-30 17:57:55 +02:00
pyup-bot 936441a853 Update ccxt from 1.17.432 to 1.17.436 2018-10-30 13:33:07 +01:00
Matthias 9065e79f53 Cleanup and add some comments on what's happening in the sample snippets 2018-10-29 20:33:27 +01:00
Matthias 6096f3ca47 Simplify functional tests 2018-10-29 20:17:15 +01:00
Matthias 98050ff594 use all min_roi entries 2018-10-29 19:27:23 +01:00
Matthias 233c442af9 Adjust backtest so sell uses stop-loss or roi value as closerate 2018-10-29 19:27:23 +01:00
Matthias a0e8bfbd77 shift buy-signal to one earlier (backtest shifts it forward to avoid
lookahead)
2018-10-29 19:27:23 +01:00
Matthias 409465ac8e adapt functional tests for new version after rebase 2018-10-29 19:27:23 +01:00
Matthias 30a6e684a6 update with new comments and new data for tc5 2018-10-29 19:27:23 +01:00
Matthias b8f78cb187 Refactor tests, implement @creslinux's data 2018-10-29 19:27:23 +01:00
Matthias e0fda7a5dd Add tests validating backtest details 2018-10-29 19:27:23 +01:00
Matthias 2f55cbde35 fix #1298 2018-10-29 19:23:56 +01:00
Samuel Husso d66ff78e79 Merge pull request #1303 from freqtrade/pyup-scheduled-update-2018-10-29
Scheduled daily dependency update on monday
2018-10-29 14:41:59 +02:00
pyup-bot 35759b372d Update ccxt from 1.17.429 to 1.17.432 2018-10-29 13:33:10 +01:00
Samuel Husso d733657db5 Merge pull request #1300 from freqtrade/doc/hyperopt_roi
Add hyperopt ROI documentation, add note on methology for hyperopt
2018-10-29 11:59:57 +02:00
Matthias 1121ec0724 don't have nb_assets as parameter - it's a config setting as any other 2018-10-28 14:43:35 +01:00
Matthias f9fefc14c9 Merge pull request #1301 from freqtrade/pyup-scheduled-update-2018-10-28
Scheduled daily dependency update on sunday
2018-10-28 09:36:06 -04:00
pyup-bot 86ad0c047c Update pytest from 3.9.2 to 3.9.3 2018-10-28 13:33:10 +01:00
pyup-bot d3387dec45 Update ccxt from 1.17.427 to 1.17.429 2018-10-28 13:33:09 +01:00
Matthias 551dc79cf7 Don't overwrite pair_whitelist in config dict
Doing that will result in an empty whitelist after a short Exchange
downtime
2018-10-28 13:15:49 +01:00
Matthias 7e4a0baef2 improve hyperopt.md 2018-10-27 17:38:15 +02:00
Matthias a4fc5afb66 Add hyperopt ROI documentation, add note on methology for hyperopt 2018-10-27 17:35:08 +02:00
Matthias db9a85f4a2 Merge pull request #1282 from freqtrade/feat/add_missingdata_warning
Show warning if part of backtest data is missing
2018-10-27 11:16:10 -04:00
Samuel Husso 20c48fb351 Merge pull request #1299 from freqtrade/pyup-scheduled-update-2018-10-27
Scheduled daily dependency update on saturday
2018-10-27 16:49:15 +03:00
pyup-bot 57d3a6f7a7 Update ccxt from 1.17.421 to 1.17.427 2018-10-27 14:33:06 +02:00
Samuel Husso ae13f3db17 Merge pull request #1297 from freqtrade/pyup-scheduled-update-2018-10-26
Scheduled daily dependency update on friday
2018-10-27 10:13:02 +03:00
pyup-bot f860aab094 Update ccxt from 1.17.411 to 1.17.421 2018-10-26 14:33:07 +02:00
Matthias 7e1a30f9bf Merge pull request #1287 from freqtrade/backtest_data_validation
Backtest data validation
2018-10-26 07:21:24 -04:00
misagh 8a316aba35 Merge branch 'develop' into money_mgt 2018-10-25 17:37:46 +02:00
misagh 426db72126 removing test line 2018-10-25 17:24:33 +02:00
misagh dfeabcf7e5 Edge tests template refactored to be more readable 2018-10-25 16:59:05 +02:00
misagh c5474794d1 1) open_trade_index refactored 2) sell index is shifted by 1 2018-10-25 16:57:49 +02:00
Matthias 92e2a3c0ea Merge pull request #1296 from freqtrade/pyup-scheduled-update-2018-10-24
Scheduled daily dependency update on wednesday
2018-10-25 06:25:45 -04:00
pyup-bot 5c77dc6b3b Update ccxt from 1.17.402 to 1.17.411 2018-10-24 14:33:06 +02:00
Matthias 1fe066e4ad Merge pull request #1278 from mishaker/mac_install_talib
Mac ta-lib installation + setup.sh script bug resolved
2018-10-23 22:40:53 -04:00
misagh b09a1d1abe 1) do not download ta-lib as we have it offline. 2) removing ta-lib
directory but not the file
2018-10-23 19:36:57 +02:00
misagh 346e155dd9 Merge branch 'develop' into mac_install_talib 2018-10-23 19:34:34 +02:00
misagh 25daf3a0f7 Merge branch 'develop' into money_mgt 2018-10-23 19:33:13 +02:00
misagh 67ace0a76c trade open time bug resolved (was behind of the market) 2018-10-23 19:32:20 +02:00
Samuel Husso a063397447 Merge pull request #1295 from freqtrade/pyup-scheduled-update-2018-10-23
Scheduled daily dependency update on tuesday
2018-10-23 16:05:41 +03:00
pyup-bot b90392f9be Update pytest from 3.9.1 to 3.9.2 2018-10-23 14:33:12 +02:00
pyup-bot 59545013c1 Update numpy from 1.15.2 to 1.15.3 2018-10-23 14:33:10 +02:00
pyup-bot 49d1687229 Update ccxt from 1.17.399 to 1.17.402 2018-10-23 14:33:09 +02:00
Samuel Husso 7da127d28e Merge pull request #1293 from wingsgb/patch-1
Update hyperopt.md
2018-10-23 11:50:49 +03:00
Matthias 11900eff39 Merge pull request #1294 from freqtrade/pyup-scheduled-update-2018-10-22
Scheduled daily dependency update on monday
2018-10-23 04:45:28 +02:00
pyup-bot 764aed2c37 Update ccxt from 1.17.395 to 1.17.399 2018-10-22 14:34:08 +02:00
wingsgb 91dc8644bf Update hyperopt.md 2018-10-22 14:30:01 +13:00
Matthias 130a6f42c5 Merge pull request #1291 from freqtrade/fix/1289_decimals
don't mess with decimals (fixes #1289)
2018-10-21 19:45:54 +02:00
Matthias 7fdd23a29d Merge pull request #1292 from freqtrade/pyup-scheduled-update-2018-10-21
Scheduled daily dependency update on sunday
2018-10-21 19:45:29 +02:00
pyup-bot ee697d609c Update ccxt from 1.17.393 to 1.17.395 2018-10-21 14:34:06 +02:00
Matthias 530d521d78 Rebuild complete image on "cron" events 2018-10-21 14:00:01 +02:00
Matthias 39efda19f4 Add freqtradeorg/freqtrade docker images to the documentation 2018-10-21 13:33:23 +02:00
Matthias 7301d76cff Remove autobuild for technical as it's not versioned
as it's not versioned and installed from github, we cannot guarantee
which version is in the image.
2018-10-21 13:20:51 +02:00
Matthias 0535660db7 build technical image 2018-10-21 13:15:14 +02:00
Matthias af7283017b modify travis to build and push docker
* name steps
* only build for master / develop and this branch (for now)
2018-10-21 13:15:14 +02:00
Matthias 907761f994 Install ta-lib in Docker with script
works for travis, works for Docker
2018-10-21 13:15:14 +02:00
Matthias 98738c482a modify install-ta-lib script to support running in docker 2018-10-21 13:14:21 +02:00
Matthias 184b5ca3fc cleanup root dir and create build_helpers 2018-10-21 13:13:18 +02:00
Matthias 677a9e56af remove skipped test (refresh_whitelist is tested in test_acl_pair) 2018-10-21 09:23:07 +02:00
Matthias 202b1d1f0b fix #1289 - we should not modify decimal context 2018-10-21 09:21:32 +02:00
Matthias 2f81dc8ff4 Merge pull request #1288 from freqtrade/pyup-scheduled-update-2018-10-19
Scheduled daily dependency update on friday
2018-10-19 19:19:40 +02:00
pyup-bot 71814ae2d6 Update requests from 2.19.1 to 2.20.0 2018-10-19 14:35:09 +02:00
pyup-bot c69b87914d Update ccxt from 1.17.392 to 1.17.393 2018-10-19 14:35:07 +02:00
Matthias 7f9f53248c Add validate_backtest_data script 2018-10-18 20:25:21 +02:00
Matthias 3c6d10f03e Print missing value count too 2018-10-18 20:05:57 +02:00
Matthias bc356c4d65 Return true/false for validation function 2018-10-18 19:48:54 +02:00
Matthias 518dcf5209 Cleanup some tests 8m is not a valid ticker value
not in constants.TICKER_INTERVAL_MINUTES map
2018-10-18 19:43:04 +02:00
Matthias fb52d32296 Add validate_backtest_data function 2018-10-18 19:42:54 +02:00
misagh 57bc4a866a average trade duration added 2018-10-18 11:09:10 +02:00
Matthias d7459bbbf3 refactor get_timeframe out of backtesting class 2018-10-17 19:59:33 +02:00
Matthias 8a3272e7c5 don't copy tickerdata_to_dataframe into backtesting
it's used only once, so this does not make sense and hides the origin of
the function
2018-10-17 19:47:19 +02:00
Samuel Husso f9bbeb79fa Merge pull request #1286 from freqtrade/pyup-scheduled-update-2018-10-17
Scheduled daily dependency update on wednesday
2018-10-17 15:47:50 +03:00
pyup-bot d953190ca5 Update pytest from 3.8.2 to 3.9.1 2018-10-17 14:34:10 +02:00
pyup-bot 14e5816975 Update urllib3 from 1.23 to 1.24 2018-10-17 14:34:09 +02:00
pyup-bot 5134736c61 Update ccxt from 1.17.388 to 1.17.392 2018-10-17 14:34:07 +02:00
Samuel Husso ca2ffaa201 Merge pull request #1275 from freqtrade/telegram_enable_stopped
Enable analytical telegram commands when stopped
2018-10-16 19:48:23 +03:00
Matthias b10d41c28a Merge pull request #1284 from freqtrade/pyup-scheduled-update-2018-10-16
Scheduled daily dependency update on tuesday
2018-10-16 17:06:32 +02:00
pyup-bot b546f0302e Update ccxt from 1.17.383 to 1.17.388 2018-10-16 14:33:06 +02:00
Matthias 80bd4129f1 Merge pull request #1283 from freqtrade/pyup-scheduled-update-2018-10-15
Scheduled daily dependency update on monday
2018-10-15 14:59:34 +02:00
pyup-bot b278dcd6db Update ccxt from 1.17.381 to 1.17.383 2018-10-15 14:33:06 +02:00
Matthias a9642dbcdb Merge pull request #1281 from freqtrade/pyup-scheduled-update-2018-10-14
Scheduled daily dependency update on sunday
2018-10-14 14:43:02 +02:00
Matthias 631ba464f3 Show warning if part of backtest data is missing 2018-10-14 14:40:03 +02:00
pyup-bot 4b9d04a2ca Update ccxt from 1.17.376 to 1.17.381 2018-10-14 14:33:06 +02:00
Matthias e0081bcb53 Merge pull request #1279 from freqtrade/pyup-scheduled-update-2018-10-13
Scheduled daily dependency update on saturday
2018-10-13 19:35:56 +02:00
pyup-bot 93503d6051 Update ccxt from 1.17.375 to 1.17.376 2018-10-13 14:33:06 +02:00
misagh 6aa9cd1060 removing outliers per pair and not across all pairs 2018-10-12 19:37:23 +02:00
Matthias cda3ddffac Merge pull request #1277 from freqtrade/feat/hide_dust
Hide low value balances from `/balance`
2018-10-12 19:15:46 +02:00
misagh fb3fd7cb15 setup script and documentation fixed for TA-Lib and MacOS 2018-10-11 19:26:19 +02:00
misagh 912e9bd15c mac installation path 2018-10-11 19:12:12 +02:00
Matthias 138c8152c2 remove unused import 2018-10-10 22:03:54 +02:00
Matthias 701978a4b1 Add test for dust hiding 2018-10-10 22:01:22 +02:00
Matthias 3628659810 Add tests to check if no failure occurs when pair is not available 2018-10-10 21:50:59 +02:00
Matthias 792d2dbe32 Hide "dust" from /balance 2018-10-10 21:29:40 +02:00
Matthias a4d2bb6f29 Fix "No market symbol" exception in telegram calls 2018-10-10 21:28:48 +02:00
Matthias 3e8e8a55fa Enable analytical telegram commands when stopped 2018-10-10 20:58:21 +02:00
Matthias 3de3c246b4 add warning-message when forcebuy_enable is true 2018-10-10 20:23:25 +02:00
Matthias 6ff4c9b888 Update docs for /forcesell 2018-10-10 20:08:29 +02:00
Samuel Husso bb057408b0 Merge pull request #1273 from freqtrade/pyup-scheduled-update-2018-10-10
Scheduled daily dependency update on wednesday
2018-10-10 15:42:30 +03:00
pyup-bot 03fb188555 Update urllib3 from 1.22 to 1.23 2018-10-10 14:29:06 +02:00
Samuel Husso 8cf435f0ba Merge pull request #1267 from freqtrade/telegram_fix
flush session for /forcesell all
2018-10-10 08:44:44 +03:00
Samuel Husso 5b7279793c Merge pull request #1268 from freqtrade/tests_as_packages
convert tests to packages
2018-10-10 08:43:48 +03:00
Matthias a541d0a931 convert tests to packages
source: https://docs.pytest.org/en/latest/goodpractices.html

If you need to have test modules with the same name, you might add __init__.py files to your tests folder and subfolders, changing them to packages:
2018-10-09 21:13:43 +02:00
Matthias 44c275c801 flush session for /forcesell all 2018-10-09 21:08:56 +02:00
Matthias 8c6d7c48ad Add tests for /forcebuy 2018-10-09 20:04:53 +02:00
Matthias fbe69cee3f Add /forcebuy to telegram 2018-10-09 19:25:43 +02:00
Matthias eee0958a58 Merge pull request #1265 from fapaydin/patch-1
Update hyperopt.md
2018-10-09 19:17:06 +02:00
Matthias 29b38bdcbe Merge pull request #1266 from freqtrade/pyup-scheduled-update-2018-10-09
Scheduled daily dependency update on tuesday
2018-10-09 16:59:46 +02:00
pyup-bot 98c1706cdd Update ccxt from 1.17.373 to 1.17.375 2018-10-09 14:29:07 +02:00
fapaydin b1f016b9c0 Update hyperopt.md
Invalid argument in description. 
Replace --timeperiod with --timerange correct format.
2018-10-09 11:49:25 +03:00
Matthias 5029003957 Allow passing price to buy function 2018-10-09 07:06:11 +02:00
Samuel Husso 2126b00dce Merge pull request #1264 from freqtrade/telegram_help
Add reload_conf to telegram help
2018-10-09 08:01:09 +03:00
Matthias a20ceb9e31 Add reload_conf to telegram help 2018-10-08 19:43:37 +02:00
Samuel Husso d23dc3ec41 Merge pull request #1263 from freqtrade/doc_stoploss
Fix spelling in stoploss.md
2018-10-08 20:04:51 +03:00
Matthias 21480d4219 be more expressive on what "this value" is 2018-10-08 15:41:07 +02:00
Matthias 2cd7b40b38 Fix spelling in stoploss.md 2018-10-08 15:39:21 +02:00
Matthias d7994cf9a3 Merge pull request #1262 from freqtrade/pyup-scheduled-update-2018-10-08
Scheduled daily dependency update on monday
2018-10-08 15:37:54 +02:00
pyup-bot 3af655d170 Update ccxt from 1.17.371 to 1.17.373 2018-10-08 14:29:07 +02:00
Matthias 55a6cac966 Merge pull request #1261 from freqtrade/pyup-scheduled-update-2018-10-06
Scheduled daily dependency update on saturday
2018-10-06 14:50:07 +02:00
pyup-bot d5409287e0 Update ccxt from 1.17.369 to 1.17.371 2018-10-06 14:29:05 +02:00
Matthias 6dec05b2a5 Merge pull request #1257 from freqtrade/add_general_params
Add general ccxt configuration options
2018-10-06 13:17:21 +02:00
Matthias 1d38c35e6a Fix typo / word repetition 2018-10-06 09:27:49 +02:00
misagh 6d4f68fcdb unnecessary variables removed 2018-10-05 17:25:56 +02:00
misagh 9e44b260e2 BacktestResult removed as it is not used 2018-10-05 17:19:20 +02:00
misagh bd25212bd6 test case added: edge calculate function 2018-10-05 17:07:20 +02:00
misagh 36d928d411 unnecessary if removed 2018-10-05 17:06:17 +02:00
Matthias f56bd5f5b7 Merge pull request #1259 from freqtrade/pyup-scheduled-update-2018-10-05
Scheduled daily dependency update on friday
2018-10-05 15:14:47 +02:00
pyup-bot 18c04ab4e2 Update ccxt from 1.17.368 to 1.17.369 2018-10-05 14:29:06 +02:00
Matthias ce4f0696e1 Add logging to download script and enable ccxt_async_config 2018-10-04 20:38:30 +02:00
Matthias 3973d3697c deprecate ccxt_rate_limt 2018-10-04 20:35:28 +02:00
Matthias 37088cfb39 add to constants 2018-10-04 20:34:48 +02:00
Matthias ddc1513286 Add ccxt_config to both config_samples 2018-10-04 20:34:33 +02:00
Matthias d1edcf9dcd Add documentation for ccxt_config 2018-10-04 20:17:19 +02:00
Matthias e7d5cf9d9d Allow loading of any additional configuration to ccxt
seperated by async and non-async
2018-10-04 20:16:43 +02:00
Matthias 939aa6009a Merge pull request #1256 from freqtrade/pyup-scheduled-update-2018-10-04
Scheduled daily dependency update on thursday
2018-10-04 19:18:44 +02:00
misagh d3078d7564 test case added: edge stop loss for pair 2018-10-04 18:51:59 +02:00
misagh 77cac9e562 autopep8 applied 2018-10-04 18:07:47 +02:00
misagh 06d75a8bad test cases added: force_stoploss by Edge 2018-10-04 18:05:46 +02:00
pyup-bot 9723300a07 Update ccxt from 1.17.365 to 1.17.368 2018-10-04 14:29:06 +02:00
Matthias 73efe52aea Merge pull request #1255 from freqtrade/update_raspi
update documentation for raspberry
2018-10-04 06:10:17 +02:00
Matthias 3ed486f3a0 update documentation for raspberry
to match as shown in #1236
2018-10-03 19:32:14 +02:00
Matthias 8532e66982 Merge pull request #1254 from freqtrade/pyup-scheduled-update-2018-10-03
Scheduled daily dependency update on wednesday
2018-10-03 19:04:26 +02:00
pyup-bot fa38772942 Update pytest from 3.8.1 to 3.8.2 2018-10-03 14:29:07 +02:00
pyup-bot 1e669c7228 Update ccxt from 1.17.363 to 1.17.365 2018-10-03 14:29:06 +02:00
misagh b57d9edda8 Edge test expectancy function (round 1) 2018-10-03 14:23:10 +02:00
misagh 6f79b55845 - function renamed to be more readable
- expectancy bug resolved
2018-10-03 14:22:27 +02:00
Samuel Husso e63e808521 Merge pull request #1252 from freqtrade/release-0.17.2
Release 0.17.2
2018-10-03 13:09:11 +03:00
misagh a46b3ec9e7 first test completed 2018-10-03 10:37:36 +02:00
Matthias e1ffc11f00 Merge pull request #1253 from freqtrade/dev-version-bump
develop to version 0.17.3
2018-10-03 08:36:52 +02:00
misagh de20e142a0 added 9 use cased for testing Edge 2018-10-02 18:05:24 +02:00
misagh a364a1e40d Edge package test cases drafted 2018-10-02 16:32:57 +02:00
misagh 697493bd01 test cases for Edge package drafted 2018-10-02 16:07:33 +02:00
misagh 23f8980973 edge config added to CONF_SCHEMA and config_full.json.example 2018-10-02 12:42:59 +02:00
misagh 8741a63783 return type of stake_amount set to float 2018-10-02 12:20:48 +02:00
misagh 26b3c3f7a8 removing unnecessary typing 2018-10-02 12:20:30 +02:00
misagh 3b57aef168 config name refactored 2018-10-02 12:16:09 +02:00
misagh 9c4fdc1bc5 initializing Edge in Freqtradebot only if it is enabled 2018-10-02 12:15:54 +02:00
misagh d634a03455 adding DataFrame type 2018-10-02 11:55:14 +02:00
misagh e4fc298bd6 typo corrected 2018-10-02 11:53:59 +02:00
misagh 11c3b3fdb9 trade_df unnecessary type removed 2018-10-02 11:53:16 +02:00
misagh a6c2e40bd4 moving time range to initializer as we have to calculate it once 2018-10-02 11:49:49 +02:00
Samuel Husso d549fe351c Prepare master for release 0.17.2 2018-10-02 09:24:22 +03:00
Samuel Husso 4a9ed02b9b develop to version 0.17.3 2018-10-02 09:18:54 +03:00
Matthias 9137338771 Merge pull request #1251 from freqtrade/pyup-scheduled-update-2018-10-01
Scheduled daily dependency update on monday
2018-10-01 19:27:17 +02:00
misagh f306abb3ee No need for Exchange class in Edge 2018-10-01 17:52:07 +02:00
misagh 8b3631d1ac make “if condition” more readable 2018-10-01 17:49:27 +02:00
misagh 2056b6f5f1 no need to initialize a variable with None 2018-10-01 17:35:27 +02:00
misagh ad666ac65c autopep8 corrected 2018-10-01 17:33:18 +02:00
misagh f72fb0ad04 exchange “None” condition removed as Edge is after Exchange anyway 2018-10-01 17:29:33 +02:00
misagh 114fd7feef declaring local variables. using get for configuration 2018-10-01 17:21:40 +02:00
misagh aa1948750f removing unnecessary constructor docstring 2018-10-01 17:11:48 +02:00
misagh 2a9ca9a3dc Removing future from travis and dockerfile 2018-10-01 17:09:08 +02:00
pyup-bot d0c7b7c582 Update ccxt from 1.17.360 to 1.17.363 2018-10-01 14:29:06 +02:00
Matthias b130a923f7 Merge pull request #1249 from freqtrade/pyup-scheduled-update-2018-09-30
Scheduled daily dependency update on sunday
2018-09-30 17:03:15 +02:00
Matthias 3af3094a56 Merge pull request #1247 from freqtrade/fix_hyperopt_pickle
Fix hyperopt pickle
2018-09-30 16:51:33 +02:00
pyup-bot 9d70d25064 Update scikit-learn from 0.19.2 to 0.20.0 2018-09-30 14:28:07 +02:00
pyup-bot 05adebb536 Update ccxt from 1.17.351 to 1.17.360 2018-09-30 14:28:06 +02:00
Matthias e1ddddad4f Merge pull request #1246 from freqtrade/fix/network_test
Patch exchange to not cause network delays during tests
2018-09-30 08:42:38 +02:00
Matthias 84622dc84b Move test for strategy out of constructor 2018-09-29 14:23:53 +02:00
Matthias 36e9abc841 Manually update scikit-learn to 0.20.0 2018-09-29 13:50:02 +02:00
Matthias 1b290ffb5d Update hyperopt to show errors if non-supported variables are used 2018-09-29 13:49:38 +02:00
Matthias 334e7553e1 Fix hyperopt not working after update of scikit-learn to 0.20.0 2018-09-29 13:49:27 +02:00
Matthias f4585a2745 Patch exchange to not cause network delays during tests 2018-09-29 13:35:48 +02:00
Matthias 448f3a7197 Merge pull request #1241 from freqtrade/fix/loadstrategyonce
Only load strategy once during backtesting
2018-09-29 09:12:41 +02:00
misagh cff83d3e6f bloody autopep8 again 2018-09-28 16:46:42 +02:00
misagh c8d06e2b0e filter pairs according to expectancy + bug at the end of array resolved 2018-09-28 16:40:34 +02:00
misagh f15825e3a7 long line broken to two 2018-09-28 14:28:05 +02:00
misagh e822d5d721 upgrading py_first_1st to 1.1.2: ez_setup.py removed 2018-09-28 14:23:39 +02:00
misagh 96a0fc88cb Moving Edge before refresh_pairs
see comments on edge (line 129)
2018-09-28 14:19:22 +02:00
Matthias 6e66763e5f Only load strategy once during backtesting 2018-09-27 19:23:55 +02:00
misagh d6415f3499 Merge branch 'develop' into money_mgt 2018-09-27 14:54:18 +02:00
Matthias 89b515be60 Merge pull request #1220 from freqtrade/fix/plot_dataframe
Fix plot dataframe
2018-09-27 12:40:34 +02:00
Matthias d481895763 Merge pull request #1211 from freqtrade/fix_no_trades_found
Add offset to "get_trades_for_order"
2018-09-27 12:40:17 +02:00
Matthias 4ad3e96a2f Merge pull request #1225 from freqtrade/test_acl_improvement
Remove direct call to pytest fixture to elliminate pytest warning
2018-09-27 12:39:56 +02:00
Matthias 3893b638fe Merge pull request #1213 from freqtrade/fix_mac_install
Fix mac install documentation
2018-09-27 12:39:42 +02:00
misagh 21f5a94eca using autopep8 for formatting file 2018-09-27 12:23:46 +02:00
Matthias 5dac3b5664 Merge pull request #1238 from freqtrade/fix/buyexception
Fix exception when order cannot be found
2018-09-26 19:26:17 +02:00
Matthias bcb13d041e Merge pull request #1239 from freqtrade/pyup-scheduled-update-2018-09-26
Scheduled daily dependency update on wednesday
2018-09-26 19:25:50 +02:00
misagh 25d6ed319a whitespace removed 2018-09-26 17:09:20 +02:00
misagh 24364a56ea keeping mypy happy 2018-09-26 17:03:10 +02:00
misagh 0594deafc6 removing whitespaces and long lines 2018-09-26 16:50:17 +02:00
misagh 75ba6578a3 unused library + trailing whitespaces removed. 2018-09-26 16:36:41 +02:00
misagh abb398786e Merge branch 'develop' into money_mgt 2018-09-26 16:06:38 +02:00
misagh fcf837bfda refactoring variable declaration 2018-09-26 16:03:51 +02:00
misagh 87df4e4556 refactoring backslap (round 2) 2018-09-26 15:20:53 +02:00
pyup-bot f790f95319 Update ccxt from 1.17.350 to 1.17.351 2018-09-26 14:28:07 +02:00
Matthias 766d32897d Merge pull request #1204 from freqtrade/move_load_markets
refactor load_markets out of validate_pairs
2018-09-26 06:38:37 +02:00
Matthias e09674b77f Merge pull request #1227 from freqtrade/feat/reduce_backtestnoise
don't print "NAN" lines in "left_open_trades"
2018-09-26 06:37:33 +02:00
Matthias 88ccdc0366 Fix exception when order cannot be found 2018-09-25 20:45:01 +02:00
Matthias d04247cd9e Merge pull request #1235 from freqtrade/pyup-scheduled-update-2018-09-25
Scheduled daily dependency update on tuesday
2018-09-25 19:20:54 +02:00
pyup-bot d13e87d7a4 Update ccxt from 1.17.341 to 1.17.350 2018-09-25 14:28:07 +02:00
misagh 40d73de357 refactoring backslap (round one) 2018-09-24 19:22:30 +02:00
misagh e8716f16ad calculating expectancy and sort pairs accordingly instead of delta 2018-09-24 17:47:50 +02:00
misagh a806dd45f2 lost in branches ! typo for some magical unknown reasons 2018-09-24 16:02:29 +02:00
misagh 027ec4d98e test_sell_profit_only_enable_loss and test_create_trade_limit_reached
fixed
2018-09-24 15:47:07 +02:00
misagh 308428644b test_process_trade_creation log message changed: in reality the buy
signal is actually triggered
2018-09-24 15:27:26 +02:00
misagh 76dd754963 test_get_trade_stake_amount and
test_get_trade_stake_amount_no_stake_amount fixed: “pair” arg added to
_get_trade_stake_amount
2018-09-24 15:02:50 +02:00
misagh 303eefda76 test_get_trade_stake_amount_unlimited_amount fixed: “pair” argument
added to _get_trade_stake_amount
2018-09-24 14:55:49 +02:00
misagh 1366783517 Dockerfile: installing future before requirements.txt 2018-09-24 14:28:16 +02:00
misagh a26131cea3 .travis: install future before requirements.txt 2018-09-24 14:21:37 +02:00
misagh 56050e5afe Merge branch 'develop' into money_mgt 2018-09-24 14:20:05 +02:00
Matthias bbcbf6adc8 Merge pull request #1234 from freqtrade/pyup-scheduled-update-2018-09-23
Scheduled daily dependency update on sunday
2018-09-23 19:20:57 +02:00
pyup-bot 6116c27aa9 Update pytest from 3.8.0 to 3.8.1 2018-09-23 14:28:09 +02:00
pyup-bot 12e6287875 Update numpy from 1.15.1 to 1.15.2 2018-09-23 14:28:08 +02:00
pyup-bot 0e168159c1 Update ccxt from 1.17.335 to 1.17.341 2018-09-23 14:28:06 +02:00
misagh 29459d7d30 import libraries organized. 2018-09-23 04:51:53 +02:00
Matthias e1c9b77c44 Merge pull request #1230 from freqtrade/pyup-scheduled-update-2018-09-22
Scheduled daily dependency update on saturday
2018-09-22 15:44:51 +02:00
misagh f1b4e4b36c stop loss range “start, end, step” configurable for Edge 2018-09-22 15:43:41 +02:00
pyup-bot 54b714ba3f Update ccxt from 1.17.327 to 1.17.335 2018-09-22 14:28:05 +02:00
misagh cf37093e5a empty dict default removed 2018-09-21 22:07:12 +02:00
misagh d6d3dfdcc2 removing “if ujson_found is not None:” as “json” refers to “ujson” if
it exists
2018-09-21 22:06:09 +02:00
misagh c11e97caf6 Merge branch 'money_mgt' of https://github.com/mishaker/freqtrade into money_mgt 2018-09-21 21:59:57 +02:00
misagh 66b1eac1db removing unnecessary ujson import 2018-09-21 21:59:35 +02:00
misagh e1ca80734d removing unnecessary ujson import 2018-09-21 21:58:37 +02:00
misagh fbc77c1f28 moving stake_currency line back to its initial place 2018-09-21 21:55:36 +02:00
misagh 3b925e46be removing default pair value of _get_trade_stake_amount 2018-09-21 21:48:27 +02:00
misagh 3e3ed947cc added “max_trade_duration” config + using “remove_dumps” config 2018-09-21 21:46:18 +02:00
misagh 61095db071 edge config enriched 2018-09-21 21:36:26 +02:00
misagh 4fd037f83f removing pdb 2018-09-21 18:00:37 +02:00
misagh 4bd956d5b1 test file removed 2018-09-21 17:58:20 +02:00
misagh 74979943ba backslap removed from arguments 2018-09-21 17:57:29 +02:00
misagh 2d432bfa95 backtesting rollbacked to develop branch 2018-09-21 17:54:37 +02:00
misagh 21f4b85c7f Merge branch 'develop' into money_mgt 2018-09-21 17:42:42 +02:00
misagh 4746aea05c test file for edge (will be removed) 2018-09-21 17:42:04 +02:00
misagh ef52c7b510 edge positioning put into package 2018-09-21 17:41:31 +02:00
Matthias f302882f67 Merge pull request #1228 from freqtrade/pyup-scheduled-update-2018-09-21
Scheduled daily dependency update on friday
2018-09-21 16:03:29 +02:00
pyup-bot 8e659af580 Update ccxt from 1.17.324 to 1.17.327 2018-09-21 14:28:07 +02:00
Matthias 567211e9f9 don't print "NAN" lines in "left_open_trades" 2018-09-20 20:35:26 +02:00
Matthias 95f884f4f3 Merge pull request #1226 from freqtrade/pyup-scheduled-update-2018-09-20
Scheduled daily dependency update on thursday
2018-09-20 19:22:08 +02:00
misagh decaf6c42e Backslap bug on “stop loss triggered” indexes resolved 2018-09-20 16:15:53 +02:00
pyup-bot 53c0f01bef Update sqlalchemy from 1.2.11 to 1.2.12 2018-09-20 14:28:10 +02:00
pyup-bot 0aa8557c03 Update ccxt from 1.17.316 to 1.17.324 2018-09-20 14:28:08 +02:00
Matthias 4d5e368c2e Remove direct call to pytest fixture to elliminate pytest warning 2018-09-19 19:40:32 +02:00
Matthias 2d4d1d7306 Merge pull request #1224 from freqtrade/pyup-scheduled-update-2018-09-19
Scheduled daily dependency update on wednesday
2018-09-19 19:14:47 +02:00
pyup-bot 2c5b6aca91 Update ccxt from 1.17.311 to 1.17.316 2018-09-19 14:28:06 +02:00
Matthias eaa657aa3b Merge pull request #1222 from freqtrade/pyup-scheduled-update-2018-09-18
Scheduled daily dependency update on tuesday
2018-09-18 19:15:01 +02:00
pyup-bot a5d4de8037 Update ccxt from 1.17.305 to 1.17.311 2018-09-18 14:28:06 +02:00
Matthias 52b75c5997 Merge pull request #1218 from jin10086/develop
use --no-cache-dir for docker build
2018-09-17 20:49:55 +02:00
Matthias f04e4f2123 Fix trailing whitespace 2018-09-17 20:49:41 +02:00
Matthias 176bae2d59 Set default-db url in configuration, not arguments
* Fixes a bug in plot_dataframe.py (#1217)
* db_url is eventually overwritten here anyway.
2018-09-17 19:57:47 +02:00
Matthias 14e21765f2 Fix missing column to load current backtesting export files 2018-09-17 19:44:40 +02:00
Matthias eebaede80d Merge pull request #1219 from freqtrade/pyup-scheduled-update-2018-09-17
Scheduled daily dependency update on monday
2018-09-17 19:20:00 +02:00
pyup-bot 9b83a09224 Update ccxt from 1.17.300 to 1.17.305 2018-09-17 14:28:06 +02:00
gaojin 0a4b2f19e3 use --no-cache-dir for docker build
use --no-cache can save about 90M
```
➜  freqtrade git:(develop) ✗ docker images freq
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
freq                latest              b15db8341067        7 minutes ago       800MB
➜  freqtrade git:(develop) ✗ docker images freq_nocache
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
freq_nocache        latest              e5731f28ac54        20 seconds ago      709MB
```
2018-09-17 10:37:25 +08:00
Matthias 3abc294e5f Merge pull request #1216 from 0xflotus/patch-1
fixed being
2018-09-16 20:11:08 +02:00
0xflotus 6aa18bddc9 fixed being 2018-09-16 17:34:01 +02:00
Matthias 16279bc171 Merge pull request #1215 from freqtrade/pyup-scheduled-update-2018-09-16
Scheduled daily dependency update on sunday
2018-09-16 15:12:43 +02:00
pyup-bot 14961e2e38 Update ccxt from 1.17.294 to 1.17.300 2018-09-16 14:28:06 +02:00
Matthias 30ae5829f5 Fix SED command for macos
Mac uses the bsd version, where -i without backup is not allowed.
2018-09-16 11:26:20 +02:00
Matthias 200dfa7575 Wording for readme.md 2018-09-16 11:22:15 +02:00
Matthias 51b3eb78d7 Add section about about clock accuracy to readme.md 2018-09-15 20:38:09 +02:00
Matthias 9685c09c1a Add offset to "get_trades_for_order" 2018-09-15 20:28:36 +02:00
Matthias 4303e86e09 Merge pull request #1210 from freqtrade/pyup-scheduled-update-2018-09-15
Scheduled daily dependency update on saturday
2018-09-15 17:40:49 +02:00
misagh 88854cba2d removing only pumps from dataset 2018-09-15 15:53:42 +02:00
misagh 07ba14d1ea backslap bug resolved 2018-09-15 15:52:10 +02:00
pyup-bot f4d26961c8 Update ccxt from 1.17.291 to 1.17.294 2018-09-15 14:28:05 +02:00
Matthias 5d9c7fa82d add point about malicious code 2018-09-14 19:56:04 +02:00
Matthias 029a6798a4 Merge pull request #1209 from freqtrade/pyup-scheduled-update-2018-09-14
Scheduled daily dependency update on friday
2018-09-14 19:39:08 +02:00
misagh 5f68834ccc merging develop into money_mgt (updated to async) 2018-09-14 19:20:49 +02:00
misagh e30d23cf23 [draft] First version of edge positioning 2018-09-14 19:04:54 +02:00
pyup-bot f5ba34addf Update ccxt from 1.17.283 to 1.17.291 2018-09-14 14:28:05 +02:00
Matthias bcf47b29ed Merge pull request #1208 from freqtrade/pyup-scheduled-update-2018-09-13
Scheduled daily dependency update on thursday
2018-09-13 19:23:10 +02:00
pyup-bot 91c0e3640f Update ccxt from 1.17.276 to 1.17.283 2018-09-13 14:29:06 +02:00
Matthias 3f890335c5 Introduce Commiter guide 2018-09-12 20:40:52 +02:00
Matthias 601ae05459 formatting for contributing.md 2018-09-12 20:25:15 +02:00
Samuel Husso fadf82dd32 Merge pull request #1205 from freqtrade/pyup-scheduled-update-2018-09-12
Scheduled daily dependency update on wednesday
2018-09-12 17:44:27 +03:00
pyup-bot 241b23e5d8 Update ccxt from 1.17.271 to 1.17.276 2018-09-12 14:28:06 +02:00
Matthias c429eae6e4 Adjust remaining tests to _load_markets refactoring 2018-09-11 19:59:01 +02:00
Matthias 674bad2a4f Add and fix tests for load_markets 2018-09-11 19:46:47 +02:00
Matthias 14b7fc42fa Change returntype for _load_markets to dict 2018-09-11 19:46:31 +02:00
Matthias 14717b1701 Merge pull request #1203 from freqtrade/pyup-scheduled-update-2018-09-11
Scheduled daily dependency update on tuesday
2018-09-11 16:55:16 +02:00
pyup-bot 51ef137981 Update ccxt from 1.17.257 to 1.17.271 2018-09-11 14:27:07 +02:00
Matthias f954efbd64 Adapt tests to not _load_markets 2018-09-10 20:19:28 +02:00
Matthias 0a29096794 Refactor load_market out of validate_pairs 2018-09-10 20:19:12 +02:00
Matthias 687dc78dbd Merge pull request #1202 from freqtrade/pyup-scheduled-update-2018-09-10
Scheduled daily dependency update on monday
2018-09-10 19:04:23 +02:00
pyup-bot 8aaf174578 Update ccxt from 1.17.250 to 1.17.257 2018-09-10 14:27:08 +02:00
Matthias 2660be9b13 Merge pull request #1201 from freqtrade/pyup-scheduled-update-2018-09-09
Scheduled daily dependency update on sunday
2018-09-09 15:47:09 +02:00
pyup-bot 65ad9cf741 Update ccxt from 1.17.242 to 1.17.250 2018-09-09 14:27:06 +02:00
Matthias 179bcf3907 Merge pull request #1101 from mishaker/ccxt-async
use ccxt async for ticker_history download
2018-09-09 08:39:57 +02:00
Samuel Husso 062eca19b8 Merge pull request #1199 from freqtrade/doc_ratelimit
Document ccxt_rate_limit
2018-09-08 16:06:59 +03:00
Samuel Husso 4692174677 Merge pull request #1200 from freqtrade/pyup-scheduled-update-2018-09-08
Scheduled daily dependency update on saturday
2018-09-08 16:06:35 +03:00
pyup-bot 65699f702e Update ccxt from 1.17.240 to 1.17.242 2018-09-08 14:27:07 +02:00
Matthias e57be10772 Document ccxt_rate_limit 2018-09-08 13:01:33 +02:00
Samuel Husso 5ba6cfe406 Merge pull request #1195 from freqtrade/update_hyperopt_doc
explicitly ask for more ressources in hyperopt documentation
2018-09-07 15:56:47 +03:00
Samuel Husso f0c7394bc8 Merge pull request #1197 from freqtrade/pyup-scheduled-update-2018-09-07
Scheduled daily dependency update on friday
2018-09-07 15:56:26 +03:00
pyup-bot fb4f83b32c Update pytest from 3.7.4 to 3.8.0 2018-09-07 14:28:09 +02:00
pyup-bot a49a60b4fa Update ccxt from 1.17.233 to 1.17.240 2018-09-07 14:28:07 +02:00
misagh 13ffd88053 merging develop into async. requirement.txt conflict resolved 2018-09-06 20:28:07 +02:00
Matthias 4e847f26bc explicitly ask for more ressources in hyperopt documentation 2018-09-06 20:12:16 +02:00
Matthias 0004b32411 Merge pull request #1194 from freqtrade/pyup-scheduled-update-2018-09-06
Scheduled daily dependency update on thursday
2018-09-06 19:51:42 +02:00
misagh a74953cb4d Draft of money management 2018-09-06 16:59:51 +02:00
pyup-bot 4f583d61c8 Update ccxt from 1.17.231 to 1.17.233 2018-09-06 14:28:06 +02:00
Samuel Husso 3eb2e92d53 Merge pull request #1191 from freqtrade/pyup-scheduled-update-2018-09-05
Scheduled daily dependency update on wednesday
2018-09-05 16:01:27 +03:00
pyup-bot a748c0794e Update ccxt from 1.17.229 to 1.17.231 2018-09-05 14:28:06 +02:00
Matthias 1682d6b365 Merge pull request #1188 from freqtrade/pyup-scheduled-update-2018-09-04
Scheduled daily dependency update on tuesday
2018-09-04 19:22:29 +02:00
pyup-bot 27ffce4c3f Update pytest-cov from 2.5.1 to 2.6.0 2018-09-04 14:28:08 +02:00
pyup-bot d62f97dc3b Update ccxt from 1.17.223 to 1.17.229 2018-09-04 14:28:06 +02:00
Matthias 9c1cd4bee2 Merge pull request #1187 from freqtrade/pyup-scheduled-update-2018-09-03
Scheduled daily dependency update on monday
2018-09-03 19:15:03 +02:00
pyup-bot 754027efed Update ccxt from 1.17.222 to 1.17.223 2018-09-03 14:28:07 +02:00
Matthias e9deb928f6 Fix bug when exchange result is empty 2018-09-02 19:15:23 +02:00
Matthias 6b74fb0893 Merge pull request #1119 from creslinux/ta_on_candle
ta_on_candle (not loop, with optional flag in config.json) Resubmitting - because GIT.
2018-09-02 17:01:21 +02:00
Samuel Husso feb14990c2 Merge pull request #1186 from freqtrade/pyup-scheduled-update-2018-09-02
Scheduled daily dependency update on sunday
2018-09-02 16:10:26 +03:00
pyup-bot 3831f198e9 Update python-telegram-bot from 11.0.0 to 11.1.0 2018-09-02 14:28:07 +02:00
pyup-bot adfd8c7f5c Update ccxt from 1.17.216 to 1.17.222 2018-09-02 14:28:06 +02:00
Matthias 3fd00c9a9c Merge branch 'develop' into ta_on_candle 2018-09-01 20:01:18 +02:00
Matthias 2ec5a536aa Fix comment location 2018-09-01 19:57:12 +02:00
Matthias d35d3bb38c rename ta_on_candle to process_only_new_candles
be more expressive
2018-09-01 19:52:40 +02:00
Matthias cb46aeb73c rename variable to be more expressive 2018-09-01 19:50:45 +02:00
Matthias b8624e5909 Merge pull request #1183 from freqtrade/pyup-scheduled-update-2018-09-01
Scheduled daily dependency update on saturday
2018-09-01 19:27:15 +02:00
pyup-bot fa5c8e4bb1 Update ccxt from 1.17.210 to 1.17.216 2018-09-01 14:28:06 +02:00
Samuel Husso 9945b97595 Merge pull request #1175 from freqtrade/doc/installation
installation documentation update
2018-08-31 23:05:12 +03:00
Matthias 17d6d92302 Merge pull request #1179 from freqtrade/pyup-scheduled-update-2018-08-30
Scheduled daily dependency update on thursday
2018-08-30 19:10:00 +02:00
pyup-bot 9560cb8056 Update pytest from 3.7.3 to 3.7.4 2018-08-30 14:28:10 +02:00
pyup-bot 3ed97fe5e8 Update python-telegram-bot from 10.1.0 to 11.0.0 2018-08-30 14:28:08 +02:00
pyup-bot 35c5d4f580 Update ccxt from 1.17.205 to 1.17.210 2018-08-30 14:28:07 +02:00
Matthias a1bd30aa60 Fix documentation string 2018-08-29 19:59:25 +02:00
Matthias ffd4469c1d fix typo, refresh_tickers does not need a return value 2018-08-29 19:56:38 +02:00
Matthias 54ddd908e6 Merge branch 'develop' into ccxt-async 2018-08-29 19:43:09 +02:00
Matthias d41f0667b8 Merge pull request #1125 from nullart2/order-book
Order Book with tests
2018-08-29 19:36:01 +02:00
Matthias 9f8e68ce02 Merge branch 'develop' into order-book 2018-08-29 19:32:44 +02:00
Matthias f7b67cec5b Fix missing docstring 2018-08-29 19:16:41 +02:00
Matthias e14e7d9b8a Merge pull request #1177 from freqtrade/pyup-scheduled-update-2018-08-29
Scheduled daily dependency update on wednesday
2018-08-29 17:04:41 +02:00
pyup-bot b659ec00ee Update ccxt from 1.17.199 to 1.17.205 2018-08-29 14:28:07 +02:00
Nullart2 b6b89a464f move order_book config out of experimental 2018-08-29 17:38:43 +08:00
Matthias c9ee528050 Add section about raspberry / conda to install.md 2018-08-28 22:06:46 +02:00
Matthias 9bce6c5f48 Add error-section for windows 2018-08-28 19:30:26 +02:00
Matthias cdfff57403 Merge pull request #1174 from freqtrade/pyup-scheduled-update-2018-08-28
Scheduled daily dependency update on tuesday
2018-08-28 19:11:09 +02:00
pyup-bot 19628d317a Update ccxt from 1.17.194 to 1.17.199 2018-08-28 14:28:06 +02:00
Matthias 32ae344e59 Merge pull request #1172 from freqtrade/pyup-scheduled-update-2018-08-27
Scheduled daily dependency update on monday
2018-08-27 15:51:22 +02:00
pyup-bot c99ff78f2f Update pytest from 3.7.2 to 3.7.3 2018-08-27 14:28:07 +02:00
pyup-bot 188cfc435d Update ccxt from 1.17.188 to 1.17.194 2018-08-27 14:28:05 +02:00
Matthias 1a9c085f10 Restructure install documentation 2018-08-26 20:09:12 +02:00
Samuel Husso eefc5349c8 Merge pull request #1171 from freqtrade/pyup-scheduled-update-2018-08-26
Scheduled daily dependency update on sunday
2018-08-26 18:55:47 +03:00
pyup-bot fe169483ed Update ccxt from 1.17.184 to 1.17.188 2018-08-26 14:28:07 +02:00
nullart2 4dfaf1d284 Merge pull request #5 from xmatthias/order_book_xmatt
fix some test mockings in orderbook pr
2018-08-26 20:01:42 +08:00
Matthias c5efcace47 change pip3.6 to pip3 2018-08-26 12:49:39 +02:00
Samuel Husso c770eae70b Merge pull request #1168 from freqtrade/pyup-scheduled-update-2018-08-25
Scheduled daily dependency update on saturday
2018-08-25 17:06:58 +03:00
pyup-bot 2ee1a2d851 Update ccxt from 1.17.176 to 1.17.184 2018-08-25 14:28:06 +02:00
Matthias 42587741dd mock exchange to avoid random failures 2018-08-25 13:21:10 +02:00
Matthias a489a044ad Mock Exchange results to avoid random test-failures 2018-08-25 13:17:07 +02:00
Matthias 1d0802192d Merge pull request #1167 from freqtrade/pyup-scheduled-update-2018-08-24
Scheduled daily dependency update on friday
2018-08-24 15:36:33 +02:00
pyup-bot ab628c1381 Update ccxt from 1.17.170 to 1.17.176 2018-08-24 14:28:06 +02:00
misagh bc6b80ff38 Edge functionality drafted 2018-08-24 11:59:10 +02:00
Matthias a37802e21c Merge pull request #1165 from freqtrade/pyup-scheduled-update-2018-08-23
Scheduled daily dependency update on thursday
2018-08-23 16:14:14 +02:00
pyup-bot 8c0e33753e Update ccxt from 1.17.163 to 1.17.170 2018-08-23 14:28:07 +02:00
Matthias cac7e2c745 Merge pull request #1164 from freqtrade/pyup-scheduled-update-2018-08-22
Scheduled daily dependency update on wednesday
2018-08-22 19:29:07 +02:00
pyup-bot ebc072396b Update numpy from 1.15.0 to 1.15.1 2018-08-22 14:28:09 +02:00
pyup-bot 4508349d07 Update ccxt from 1.17.157 to 1.17.163 2018-08-22 14:28:07 +02:00
Samuel Husso 7376a0d538 Merge pull request #1131 from freqtrade/parametrize_outdated_ticker
parametrize outdated_offset to simplify sandbox usage
2018-08-22 07:02:38 +03:00
Samuel Husso 36e0e652f0 Merge pull request #1135 from freqtrade/fix/rpc_balance_vtho
Fix /balance rpc call if coin is not properly listed
2018-08-22 07:01:40 +03:00
Samuel Husso 5e4ae46b3c Merge pull request #1163 from freqtrade/remove_amount_to_lots
remove amount_to_lots (deprecated / removed)
2018-08-22 07:01:09 +03:00
Misagh 66d52c1236 Merge pull request #4 from xmatthias/ccxt_async_retrier
Add async retrier
2018-08-21 19:55:30 +02:00
Matthias 6e90d482ef remove amount_to_lots (deprecated / removed)
was removed from ccxt in
https://github.com/ccxt/ccxt/commit/527f082e59e1cd3698cb7ae95bdcaae4459ea218
2018-08-21 19:08:21 +02:00
Samuel Husso 37bb6ac57b Merge pull request #1162 from freqtrade/pyup-scheduled-update-2018-08-21
Scheduled daily dependency update on tuesday
2018-08-21 15:42:57 +03:00
pyup-bot 8a844488d4 Update sqlalchemy from 1.2.10 to 1.2.11 2018-08-21 14:28:08 +02:00
pyup-bot e5707b8a2c Update ccxt from 1.17.152 to 1.17.157 2018-08-21 14:28:06 +02:00
Matthias 8f41e0e190 Use setting in 'exchange' dict 2018-08-20 20:01:57 +02:00
Samuel Husso 4bf0542204 Merge pull request #1161 from freqtrade/pyup-scheduled-update-2018-08-20
Scheduled daily dependency update on monday
2018-08-20 19:07:03 +03:00
pyup-bot 43f73c5aec Update ccxt from 1.17.146 to 1.17.152 2018-08-20 14:28:06 +02:00
Matthias a077955efa update json.load to json_load - followup to #1142 2018-08-19 19:58:07 +02:00
Matthias 0674c3e8f0 Merge pull request #1142 from freqtrade/ujson-loader
backtesting: try to load data with ujson if it exists
2018-08-19 19:53:38 +02:00
Matthias 6d1c82a5fa Remove last refreence to get_candle_history 2018-08-19 19:50:14 +02:00
Matthias de0f3e43bf remove unused mocks 2018-08-19 19:49:39 +02:00
Matthias 694b8be32f Move variables from class to instance 2018-08-19 19:49:02 +02:00
Matthias 9403248e4d have plot-script use async ticker-refresh 2018-08-19 19:48:24 +02:00
Samuel Husso c955c7c494 Merge pull request #1160 from freqtrade/pyup-scheduled-update-2018-08-19
Scheduled daily dependency update on sunday
2018-08-19 18:14:46 +03:00
pyup-bot 5a0876704a Update pytest from 3.7.1 to 3.7.2 2018-08-19 14:28:07 +02:00
pyup-bot 97e9a44fd2 Update ccxt from 1.17.139 to 1.17.146 2018-08-19 14:28:06 +02:00
Matthias 088c54b88c remove unnecessary function 2018-08-19 09:17:17 +02:00
Matthias d722c12109 fix bug in async download script 2018-08-18 21:08:59 +02:00
Matthias d556f669b0 Add async retrier 2018-08-18 21:05:38 +02:00
Matthias 66255b8c61 Merge pull request #1159 from freqtrade/pyup-scheduled-update-2018-08-18
Scheduled daily dependency update on saturday
2018-08-18 17:24:38 +02:00
pyup-bot bc22320f77 Update ccxt from 1.17.134 to 1.17.139 2018-08-18 14:27:07 +02:00
Samuel Husso 64781643d3 Merge pull request #1157 from freqtrade/pyup-scheduled-update-2018-08-17
Scheduled daily dependency update on friday
2018-08-17 18:55:04 +03:00
pyup-bot 56188f2f67 Update ccxt from 1.17.132 to 1.17.134 2018-08-17 14:27:07 +02:00
Samuel Husso eb4bc66443 Merge pull request #1156 from freqtrade/add_min_roi_test
Add explicit test on handling min_roi_reached
2018-08-17 09:59:10 +03:00
Matthias d1c5eebff2 Add explicit test on handling min_roi_reached 2018-08-17 06:50:36 +02:00
Samuel Husso 98240e0e48 Merge pull request #1154 from freqtrade/min_roi_output
Output min-roi setting when overwriting from config
2018-08-16 20:18:49 +03:00
Samuel Husso 0750d356a1 Merge pull request #1141 from freqtrade/fix/python3.7
fix running freqtrade on python3.7
2018-08-16 20:17:24 +03:00
Matthias f57bf8f269 Merge pull request #1155 from freqtrade/pyup-scheduled-update-2018-08-16
Scheduled daily dependency update on thursday
2018-08-16 14:36:53 +02:00
pyup-bot dc41a19f99 Update ccxt from 1.17.126 to 1.17.132 2018-08-16 14:27:06 +02:00
Matthias 16fa877b67 Remove verbosity of trying backup tables - properly log if
databasemigration happened
2018-08-16 13:15:46 +02:00
Matthias ff8ed564f1 Refactor refresh_pairs to exchange and fix tests 2018-08-16 12:15:09 +02:00
misagh e6e2799f03 Keeping cached Klines only in exchange and renaming _cached_klines to
klines.
2018-08-16 11:37:31 +02:00
Matthias 4a8c120926 Output min-roi setting when overwriting from config 2018-08-16 11:35:41 +02:00
Samuel Husso aa10c6e6fe master to RELEASE 0.17.1 2018-08-16 08:12:36 +03:00
misagh a2d9126917 Merge branch 'develop' into ccxt-async 2018-08-15 15:09:35 +02:00
Samuel Husso e02f964e3a Merge pull request #1152 from freqtrade/pyup-scheduled-update-2018-08-15
Scheduled daily dependency update on wednesday
2018-08-15 15:46:24 +03:00
pyup-bot be373e7563 Update ccxt from 1.17.122 to 1.17.126 2018-08-15 14:27:06 +02:00
Matthias baeffee80d Replace time.time with arrow.utcnow().timestamp
arrow is imported already
2018-08-15 13:26:01 +02:00
Matthias 76914c2c07 remove todo comment as this is actually done 2018-08-15 12:57:27 +02:00
Matthias ca6594cd24 remove comment, add docstring 2018-08-15 12:49:39 +02:00
Matthias d007ac4b96 check version explicitly, use "python" in venv 2018-08-15 08:37:20 +02:00
Janne Sinivirta 6e2a2abe80 Merge pull request #1151 from freqtrade/version-bump
Push develop as 0.17.2
2018-08-15 08:26:43 +03:00
Samuel Husso dd7f540e5a Push develop as 0.17.2 2018-08-15 08:25:04 +03:00
Samuel Husso 78d1a677d7 Merge pull request #1140 from freqtrade/update_plotly
update plotly dependency
2018-08-15 08:18:06 +03:00
Matthias 2999588ea7 Merge pull request #1150 from nullart2/informative_startup
Informative startup
2018-08-15 06:43:51 +02:00
Nullart2 1edbc494ee refactor 2018-08-15 12:37:30 +08:00
Nullart2 b34aa46181 additional tests 2018-08-15 12:05:56 +08:00
Nullart2 48e218d6c0 test_talib fix 2018-08-15 11:01:59 +08:00
Nullart2 2bc7a668a3 informative startup 2018-08-15 10:39:32 +08:00
nullart2 8b9f1cadaa Merge pull request #2 from freqtrade/develop
dev update
2018-08-15 09:59:42 +08:00
Matthias 3aa210cf93 Add test for get_history 2018-08-14 20:53:58 +02:00
Matthias e37cb49dc2 Ad test for async_load_markets 2018-08-14 20:42:13 +02:00
Matthias 67cbbc86f2 Add test for exception 2018-08-14 20:35:12 +02:00
Matthias 37e504610a refactor private method - improve some async tests 2018-08-14 20:33:03 +02:00
Matthias 8528143ffa Properly close async exchange as requested by ccxt 2018-08-14 19:52:09 +02:00
Matthias 69cc6aa958 Add test to async 2018-08-14 16:02:03 +02:00
misagh a6b69da391 Merge branch 'develop' into ccxt-async 2018-08-14 15:30:34 +02:00
Matthias 05cfbde8fc Merge pull request #1146 from freqtrade/pyup-scheduled-update-2018-08-14
Scheduled daily dependency update on tuesday
2018-08-14 14:40:58 +02:00
pyup-bot 04878da66b Update ccxt from 1.17.118 to 1.17.122 2018-08-14 14:27:07 +02:00
misagh 0b44dda7b7 Merge pull request #3 from xmatthias/ccxt-async_xmatt
ccxt async download
2018-08-14 13:21:13 +02:00
Nullart2 78610bb47f mock order_book and additional test 2018-08-14 18:12:44 +08:00
Matthias 721fb3e326 remove unused profile import 2018-08-14 10:12:57 +02:00
Matthias 50494858f1 Merge pull request #1144 from freqtrade/pyup-scheduled-update-2018-08-13
Scheduled daily dependency update on monday
2018-08-13 14:42:09 +02:00
pyup-bot eca8682528 Update ccxt from 1.17.113 to 1.17.118 2018-08-13 14:26:06 +02:00
Matthias a488734efa Merge pull request #1143 from freqtrade/pyup-scheduled-update-2018-08-12
Scheduled daily dependency update on sunday
2018-08-12 19:03:17 +02:00
pyup-bot 2e7837976d Update ccxt from 1.17.106 to 1.17.113 2018-08-12 14:26:06 +02:00
Matthias a0bc17d1ef Update dockerfile to 3.7.0 2018-08-12 13:59:50 +02:00
Matthias 2b37c1ff0e Merge branch 'develop' into ujson-loader 2018-08-12 13:11:40 +02:00
Matthias 7d72e364aa Remove broken ujson loading - replace with variable-based fix 2018-08-12 13:08:10 +02:00
creslin bd61478367 Merge pull request #2 from xmatthias/ta_on_candle_xmatt
Ta on candle xmatt
2018-08-12 10:07:58 +00:00
Matthias f7afd9a5ff update setup.sh to support 3.7 2018-08-12 10:37:10 +02:00
Matthias 7f6f5791ea update plotly dependency 2018-08-12 10:25:19 +02:00
Matthias e3e79a55fa Fix _abc_data pickle error in 3.7 2018-08-12 10:16:51 +02:00
Matthias e73331b9b6 Merge pull request #1124 from berlinguyinca/database_tuning
Database tuning
2018-08-12 09:45:48 +02:00
Matthias ffa47151ee Flake8 fix 2018-08-12 09:30:12 +02:00
Matthias 5f8ec82319 Revert "updated dockerfile and requirements"
This reverts commit 2cfa3b7607.
2018-08-12 09:18:30 +02:00
Matthias 3ad6ee6b2c Merge pull request #1139 from freqtrade/pyup-scheduled-update-2018-08-11
Scheduled daily dependency update on saturday
2018-08-11 19:27:52 +02:00
pyup-bot 5bec389e85 Update ccxt from 1.17.94 to 1.17.106 2018-08-11 14:26:06 +02:00
Matthias 88e85e8d33 fix tests - move load_async_markets call to validate_pairs 2018-08-10 13:11:04 +02:00
Matthias fce071843d Move async-load to seperate function 2018-08-10 13:04:43 +02:00
Matthias a852d2ff32 default since_ms to 30 days if no timerange is given 2018-08-10 11:15:02 +02:00
Matthias a107c4c7b4 Download using asyncio 2018-08-10 11:08:28 +02:00
Matthias 74d6816a1a Fix some comments 2018-08-10 11:00:07 +02:00
Matthias e34f2abc3a Add some typehints 2018-08-10 09:58:04 +02:00
Matthias 8a0fc888d6 log if using cached data 2018-08-10 09:48:54 +02:00
Matthias 36f05af79a sort fetch_olvhc result, refactor some
* add exception for since_ms - if this is set it should always download
2018-08-10 09:44:15 +02:00
Matthias e654b76bc8 Fix async test 2018-08-10 09:44:03 +02:00
Matthias 56768f1a61 Flake8 in tests ... 2018-08-09 20:17:55 +02:00
Matthias b008649d79 Remove unnecessary quote escaping 2018-08-09 20:13:07 +02:00
Matthias 3b2f161573 Add test for ta_on_candle override 2018-08-09 20:12:45 +02:00
Matthias df960241bd Add log-message for skipped candle and tests 2018-08-09 20:07:01 +02:00
Matthias 4ece5d6d7a Add tests for ta_on_candle 2018-08-09 20:02:24 +02:00
Matthias e36067afd3 refactor candle_seen to private 2018-08-09 19:58:47 +02:00
Matthias c4e43039f2 Allow control from strategy 2018-08-09 19:24:00 +02:00
Matthias 853374d156 Merge pull request #1136 from freqtrade/pyup-scheduled-update-2018-08-09
Scheduled daily dependency update on thursday
2018-08-09 19:15:47 +02:00
pyup-bot 1bcd4333fc Update ccxt from 1.17.86 to 1.17.94 2018-08-09 14:26:06 +02:00
Matthias 029d61b8c5 Add ta_on_candle descripton to support strategy 2018-08-09 13:12:12 +02:00
misagh 280ead7bdb Merge branch 'develop' into ccxt-async 2018-08-09 13:04:01 +02:00
Matthias 98730939d4 Refactor to use a plain dict
* check config-setting first - avoids any call to "candle_seen"
eventually
2018-08-09 13:02:41 +02:00
Matthias d1306a2177 Fix failing tests when metadata in analyze_ticker is actually used 2018-08-09 13:01:57 +02:00
misagh cb26085229 Moving should_not_update logic to async function per pair. if there is
no new candle, async function will just return the last cached candle
locally and doesn’t hit the API
2018-08-09 12:47:26 +02:00
Matthias ed4771bf6e Merge pull request #1130 from freqtrade/fix_metadatatests
Fix failing tests when metadata in `analyze_ticker` is actually used
2018-08-09 12:46:35 +02:00
misagh cef09f49a6 wait for markets to be loaded before looping in symbols. 2018-08-09 11:51:38 +02:00
Matthias e1921c8849 Fix bug causing /balance to fail 2018-08-08 22:00:39 +02:00
Matthias 3c451e0677 Add test for bugreport #1111 2018-08-08 21:54:52 +02:00
Matthias 636ae1dcd8 Merge pull request #1134 from freqtrade/pyup-scheduled-update-2018-08-08
Scheduled daily dependency update on wednesday
2018-08-08 19:19:39 +02:00
pyup-bot 4d03fc213f Update ccxt from 1.17.84 to 1.17.86 2018-08-08 14:26:07 +02:00
Samuel Husso 863110422a Merge pull request #1132 from freqtrade/pyup-scheduled-update-2018-08-07
Scheduled daily dependency update on tuesday
2018-08-07 17:54:11 +03:00
pyup-bot 3d94720be9 Update ccxt from 1.17.81 to 1.17.84 2018-08-07 14:26:07 +02:00
Nullart2 c9c0e108ab refactor 2018-08-07 18:29:37 +08:00
Matthias c9580b31d0 parametrize outdated_offset to simplify sandbox usage 2018-08-07 09:25:21 +02:00
Matthias 255f303850 Fix tests and flake8 2018-08-07 08:56:06 +02:00
Matthias 131d268721 Fix failing tests when metadata in analyze_ticker is actually used 2018-08-06 19:15:30 +02:00
Matthias eca5c6f389 Merge pull request #1129 from freqtrade/pyup-scheduled-update-2018-08-06
Scheduled daily dependency update on monday
2018-08-06 15:29:56 +02:00
pyup-bot bc62f626c5 Update ccxt from 1.17.78 to 1.17.81 2018-08-06 14:26:06 +02:00
Samuel Husso 199bd7bc50 Merge pull request #1123 from freqtrade/fix-db_migration
Fix db migration
2018-08-06 12:00:22 +03:00
Janne Sinivirta 8fc0f6ecec Merge pull request #1128 from Axel-CH/fix-talib-prescision
fix talib bug on bollinger bands and other indicators
2018-08-06 08:35:35 +03:00
Axel Cherubin 65f7b75c34 fix flake8 issue 2018-08-05 17:52:06 -04:00
Axel Cherubin 848ecb91bb remove unnecessary seb command 2018-08-05 17:28:53 -04:00
Axel Cherubin a5554604e0 add sed command in doc, fix travis error 2018-08-05 16:59:18 -04:00
Axel Cherubin 0b825e96aa fix talib bug on bollinger bands and other indicators when working on small assets, rise talib prescision and add test associated 2018-08-05 16:08:49 -04:00
Matthias a2730cd86e Merge pull request #1126 from freqtrade/pyup-scheduled-update-2018-08-05
Scheduled daily dependency update on sunday
2018-08-05 19:18:11 +02:00
Nullart2 1309c2b14f tests update 2018-08-05 22:56:14 +08:00
Nullart2 7143b64fb7 tests for coverage 2018-08-05 22:41:58 +08:00
Nullart2 26d591ea43 mypy fix 2018-08-05 21:08:07 +08:00
pyup-bot ba4de4137e Update pandas from 0.23.3 to 0.23.4 2018-08-05 14:26:08 +02:00
pyup-bot be9436b2a6 Update ccxt from 1.17.73 to 1.17.78 2018-08-05 14:26:07 +02:00
Nullart2 4a9bf78770 Order Book with tests 2018-08-05 12:41:06 +08:00
Matthias d73d0a5253 Fix database migration 2018-08-04 20:22:45 +02:00
Matthias ea506b05c6 Add test for failing database migration 2018-08-04 20:22:16 +02:00
Samuel Husso 6ef14677de Merge pull request #1122 from freqtrade/pyup-scheduled-update-2018-08-04
Scheduled daily dependency update on saturday
2018-08-04 19:55:20 +03:00
pyup-bot 721341e412 Update ccxt from 1.17.66 to 1.17.73 2018-08-04 14:26:05 +02:00
misagh 3ce4d20ab9 using constants instead of stripping the string 2018-08-04 13:04:16 +02:00
misagh af93b18475 Do not refresh candles on "process_throttle_secs" but on intervals 2018-08-03 18:10:03 +02:00
Samuel Husso a586a7526e Merge pull request #1120 from freqtrade/pyup-scheduled-update-2018-08-03
Scheduled daily dependency update on friday
2018-08-03 16:11:14 +03:00
misagh 3987a8aeb8 Merge branch 'ccxt-async' of https://github.com/misaghshakeri/freqtrade into ccxt-async 2018-08-03 14:50:11 +02:00
misagh 59b9a6d94d Break the loop as soon as one buy signal is found. 2018-08-03 14:49:55 +02:00
pyup-bot b963b95ee9 Update pytest from 3.7.0 to 3.7.1 2018-08-03 14:26:07 +02:00
pyup-bot 3037d85529 Update ccxt from 1.17.63 to 1.17.66 2018-08-03 14:26:06 +02:00
creslin 10ab6c7ffa Removed unneeded property code 2018-08-03 09:14:16 +00:00
creslin 71b0e15182 updated configuration.md 2018-08-03 08:45:24 +00:00
creslin 1fef384bba flake 8 2018-08-03 08:40:16 +00:00
creslin d2a728cebd flake 8 2018-08-03 08:38:13 +00:00
creslin 6b3e8dcc33 holds a dict of each pair last seen.
to correctly manage the last seen of a pair.
2018-08-03 08:33:37 +00:00
creslin c38d94df2d Resubmitting - because GIT.
This is the last cut that was in #1117 before i closed that PR

This PR allows a user to set the flag "ta_on_candle" in their config.json

This will change the behaviour of the the bot to only process indicators
when there is a new candle to be processed for that pair.

The test is made up of "last dataframe row date + pair" is different to
last_seen OR  ta_on_candle is not True
2018-08-03 07:33:34 +00:00
Gert Wohlgemuth 2cfa3b7607 updated dockerfile and requirements 2018-08-02 17:08:14 -07:00
Gert 85c73ea850 added index 2018-08-02 16:39:13 -07:00
Matthias 337d9174d9 Flake8 fixes 2018-08-02 20:11:27 +02:00
Matthias 80a1c6ea64 Merge pull request #1106 from creslinux/xbt
XBT missing as a market symbol for BTC in constants
2018-08-02 20:07:25 +02:00
misagh 05ca78d2a3 ticker_history changed to candle_history naming 2018-08-02 17:10:38 +02:00
misagh 2ec2f1abce async branch updated to reflect develop branch changes 2018-08-02 16:48:21 +02:00
misagh 7dc440b874 Merge pull request #2 from xmatthias/ccxt-async-xmatt
Ccxt async xmatt
2018-08-02 16:33:02 +02:00
Matthias ea72af7ce4 Merge pull request #1118 from freqtrade/pyup-scheduled-update-2018-08-02
Scheduled daily dependency update on thursday
2018-08-02 14:44:53 +02:00
pyup-bot 145008421f Update ccxt from 1.17.60 to 1.17.63 2018-08-02 14:26:07 +02:00
Samuel Husso 398c61786a Merge pull request #1116 from creslinux/script_get_market_pairs
Script to get market pairs
2018-08-02 13:29:42 +03:00
Matthias 00b81e3f0d fix readme.md spelling 2018-08-02 13:27:37 +03:00
Matthias 0fc4a7910d Add note to readme for binance users 2018-08-02 13:27:37 +03:00
creslin 7f4472ad77 As requested in issue #1111
A python script to return

 - all exchanges supported by CCXT
 - all markets on a exchange

 Invoked as `python get_market_pairs.py` it will list exchanges
 Invoked as `python get_market_pairs binance` it will list all markets on binance
2018-08-02 10:10:44 +00:00
Janne Sinivirta e282d57a91 fix broken test 2018-08-02 12:57:47 +03:00
Janne Sinivirta 3a5b435dfa Merge pull request #1089 from freqtrade/feat/backtest_multi_strat
Allow multi strategy backtest without data reload
2018-08-02 12:35:47 +03:00
Janne Sinivirta 17d78b7807 Merge pull request #1115 from creslinux/candlesnottickers
renamed/refactored get_ticker_history to get_candle_history to stop confusion
2018-08-02 12:33:09 +03:00
creslin 1f97d0d78b fix 2018-08-02 09:15:02 +00:00
creslin a741f1144a missing __init__.py 2018-08-02 08:58:04 +00:00
creslin f619cd1d2a renamed/refactored get_ticker_history to get_candle_history
as it does not fetch any ticker data only candles
and is causing confusion when developer are talking about candles /tickers
incorreclty.

OHLCV < candles and Tickers are two seperate datafeeds from the exchange
2018-08-02 08:45:28 +00:00
Matthias 4f5b530dcb Merge pull request #1113 from berlinguyinca/backslap_develop
Backslap develop
2018-08-02 10:30:36 +02:00
Matthias 9c08cdc81d Fix typehints 2018-08-01 21:58:32 +02:00
Matthias 915160f21f Add tests for tickers-history 2018-08-01 21:44:02 +02:00
Matthias c466a028e0 Add a first async test 2018-08-01 21:40:54 +02:00
Matthias 29dcd2ea43 Merge pull request #1108 from freqtrade/pyup-scheduled-update-2018-08-01
Scheduled daily dependency update on wednesday
2018-08-01 15:38:23 +02:00
pyup-bot f7f75b4b04 Update ccxt from 1.17.56 to 1.17.60 2018-08-01 14:26:05 +02:00
Matthias 7458aa438c Merge pull request #982 from berlinguyinca/BASE64
integrated BASE64 encoded strategy loading
2018-08-01 09:00:12 +02:00
creslin 36f91fcdf5 XBT missing as a market symbol for BTC in constants 2018-08-01 06:03:34 +00:00
Matthias 5b8ee214f9 Adapt to pair_to_strat methology 2018-08-01 07:28:12 +02:00
Matthias 038e97667f Merge branch 'develop' into BASE64 2018-08-01 07:26:13 +02:00
Gert 04d5e857e2 added option to easily switch between backtesting and backslapping from the commandline option 2018-07-31 18:10:23 -07:00
Gert 3428b6666b Merge branch 'develop_current' into backslap_develop 2018-07-31 17:07:30 -07:00
misagh b47c5f1d9a Merge pull request #1 from xmatthias/ccxt-async-xmatt
some fixes and improvements hopefully
2018-07-31 21:21:45 +02:00
Matthias 40ee86b357 Adapt after rebase 2018-07-31 21:08:03 +02:00
Matthias 76fbb89a03 use print for backtest results to avoid odd newline-handling 2018-07-31 21:04:03 +02:00
Matthias c648e2acfc Adjust documentation to strategy table 2018-07-31 21:04:03 +02:00
Matthias 765d1c769c Add test for stratgy summary table 2018-07-31 21:04:03 +02:00
Matthias 028589abd2 Add strategy summary table 2018-07-31 21:04:03 +02:00
Matthias 5125076f5d Fix typo 2018-07-31 21:04:03 +02:00
Matthias 4ea6780153 Update documentation with --strategy-list 2018-07-31 21:04:03 +02:00
Matthias a8b55b8989 Add test for strategy-name injection 2018-07-31 21:04:03 +02:00
Matthias a57a2f4a75 Store backtest-result in different vars 2018-07-31 21:04:03 +02:00
Matthias bd3563df67 Add test for new functionality 2018-07-31 21:04:03 +02:00
Matthias 644f729aea Refactor strategy loading to __init__ 2018-07-31 21:04:03 +02:00
Matthias 5f2e92ec5c Refactor backtesting 2018-07-31 21:04:03 +02:00
Matthias 65aaa3dffd Extract backtest strategy setting 2018-07-31 21:04:03 +02:00
Matthias 9a42aac0f2 Add testcase for --strategylist 2018-07-31 21:04:03 +02:00
Matthias 56046b3cb3 Add strategylist option to backtesting 2018-07-31 21:04:03 +02:00
Matthias e7d0439741 Add new arguments 2018-07-31 21:03:17 +02:00
Matthias 136442245c Add todo's and dockstring 2018-07-31 21:02:04 +02:00
Matthias 12417cc303 fix tests 2018-07-31 20:54:51 +02:00
Matthias 52065178e1 use .get all the time 2018-07-31 20:53:32 +02:00
Matthias b45d465ed8 init _klines properly 2018-07-31 20:50:59 +02:00
Matthias 31870abd25 Refactor async-refresh to it's own function 2018-07-31 20:43:32 +02:00
Matthias a486b1d01c Use Dict instead of tuplelist, run in _process 2018-07-31 20:25:10 +02:00
Matthias e38e0e60e1 Merge pull request #1103 from misaghshakeri/ccxt_ratelimit_configurable
Initializing CCXT with rate_limit parameter optional (default to true) [EDITED]
2018-07-31 19:46:28 +02:00
misagh 74fa4ddca4 CCXT rate limit config default to => true
+ adding config to config_full.json.example
2018-07-31 16:54:02 +02:00
Matthias 66a0986496 Merge pull request #1102 from freqtrade/pyup-scheduled-update-2018-07-31
Scheduled daily dependency update on tuesday
2018-07-31 14:39:48 +02:00
pyup-bot 72480188b7 Update pytest from 3.6.4 to 3.7.0 2018-07-31 14:25:07 +02:00
pyup-bot ab4343b7c0 Update ccxt from 1.17.49 to 1.17.56 2018-07-31 14:25:06 +02:00
misagh be1298dbd2 Initializing CCXT with rate_limit parameter optional (default to false) 2018-07-31 14:19:16 +02:00
misagh 154e4569d7 Merge branch 'develop' into ccxt-async 2018-07-31 12:48:12 +02:00
misagh c8f125dbb9 ccxt async POC 2018-07-31 12:47:32 +02:00
Janne Sinivirta 1044d15b17 Merge pull request #1096 from freqtrade/cleaner-tests
Cleaning unit tests, first set
2018-07-31 08:22:33 +03:00
Janne Sinivirta 2d7ef30185 Merge pull request #1093 from freqtrade/fix/talib-install
install numpy before ta-lib to fix build errors
2018-07-31 08:19:35 +03:00
Gert bf0b1af878 merged latest development branch 2018-07-30 13:43:25 -07:00
Gert b83487cc36 added required changes 2018-07-30 13:00:08 -07:00
Matthias d048f3ce6d Merge pull request #1078 from creslinux/sandbox2
Allow sandbox API use on exchanges
2018-07-30 20:23:28 +02:00
Matthias 5a55cd25ff Merge branch 'develop' into sandbox2 2018-07-30 20:18:48 +02:00
Janne Sinivirta f85cc422a3 Merge branch 'develop' into cleaner-tests 2018-07-30 21:08:55 +03:00
Janne Sinivirta 155e134f50 Merge pull request #1097 from creslinux/gdax3
Enable GDAX support by rounding amount/rate (with unit tests)
2018-07-30 21:04:26 +03:00
Janne Sinivirta 81cf7229be Merge pull request #1044 from freqtrade/pair_to_strat
pair to strategy enhancement
2018-07-30 20:18:46 +03:00
creslin fe27ca63b4 Update test_exchange.py 2018-07-30 17:08:33 +00:00
creslinux 012fe94333 Recommitted as new branch with unit tests - GIT screwd me on the last PR 2018-07-30 16:49:58 +00:00
Matthias 075a42d615 Merge pull request #1095 from freqtrade/pyup-scheduled-update-2018-07-30
Scheduled daily dependency update on monday
2018-07-30 14:53:24 +02:00
Janne Sinivirta 8b8d3f3b75 default_conf is function-scoped fixture, no need to deepcopy it 2018-07-30 15:41:02 +03:00
pyup-bot 3ecc502d86 Update ccxt from 1.17.45 to 1.17.49 2018-07-30 14:24:06 +02:00
Janne Sinivirta 67d1693901 avoid validating default_conf hundreds of times 2018-07-30 14:57:51 +03:00
Janne Sinivirta 3083e5d2be use pytest fixture properly in test_hyperopt 2018-07-30 13:26:54 +03:00
Janne Sinivirta affdeb8fd8 rename func to throttled_func 2018-07-30 12:58:29 +03:00
Janne Sinivirta fb80964b69 freqtradebot tests don't need to mock coinmarketcap anymore 2018-07-30 12:58:29 +03:00
Janne Sinivirta 1c20ef873d remove parens 2018-07-30 12:09:07 +03:00
Janne Sinivirta df53e912f0 fix one more test that was missing mock and needed internet 2018-07-30 12:09:07 +03:00
Janne Sinivirta e242842805 remove more useless docstrings from tests 2018-07-30 12:09:07 +03:00
Matthias 2401fa15d2 Change missed calls to advise_* functions 2018-07-29 21:07:21 +02:00
Matthias 787d6042de Switch from pair(str) to metadata(dict) 2018-07-29 20:56:23 +02:00
Matthias 941879dc19 revert docs to use populate_* functions 2018-07-29 20:55:40 +02:00
Matthias 82680ac6aa improve docstrings for strategy 2018-07-29 20:55:40 +02:00
Matthias 5fbce13830 update hyperopt to use new methods 2018-07-29 20:55:40 +02:00
Matthias 39cf0decce don't use __annotate__
it is only present when typehints are used which cannot be guaranteed
for userdefined classes
2018-07-29 20:55:40 +02:00
Matthias f286ba6b87 overload populate_indicators to work with and without pair argumen
all while not breaking users strategies
2018-07-29 20:55:40 +02:00
Matthias 98665dcef4 revert inadvertent wihtespace changes 2018-07-29 20:55:37 +02:00
Matthias cf83416d69 update script to use new method 2018-07-29 20:55:37 +02:00
Matthias 791c5ff071 update comments to explain what advise methods do 2018-07-29 20:55:37 +02:00
Matthias 8a9c54ed61 use new methods 2018-07-29 20:55:37 +02:00
Matthias 18b8f20f1c fix small test bug 2018-07-29 20:55:37 +02:00
Matthias f12167f0dc Fix backtesting test 2018-07-29 20:55:37 +02:00
Matthias df8700ead0 Adapt after merge from develop 2018-07-29 20:55:37 +02:00
Matthias 0eff6719c2 improve tests for legacy-strategy loading 2018-07-29 20:55:37 +02:00
Matthias aa772c28ad Add tests for advise_indicator methods 2018-07-29 20:55:37 +02:00
Matthias 4ebd706cb8 improve comments 2018-07-29 20:55:32 +02:00
Matthias fa48b8a535 Update documentation with advise-* methods 2018-07-29 20:55:32 +02:00
Matthias c9a97bccb7 Add tests for deprecation 2018-07-29 20:55:32 +02:00
Matthias 2f905cb696 Update test-strategy with new methods 2018-07-29 20:55:06 +02:00
Matthias 7300c0a0fe remove @abstractmethod as this method may not be present in new
strategies
2018-07-29 20:55:06 +02:00
Gert Wohlgemuth 921f645623 fixing tests... 2018-07-29 20:55:06 +02:00
Gert Wohlgemuth 0dcaa82c3b fixed test? 2018-07-29 20:55:06 +02:00
Gert Wohlgemuth 3dd7d209e9 more test fixes 2018-07-29 20:55:06 +02:00
Gert Wohlgemuth abc55a6e6b fixing? hyperopt 2018-07-29 20:55:06 +02:00
Gert Wohlgemuth 5871488858 fixed errors and making flake pass 2018-07-29 20:55:06 +02:00
xmatthias 2e6e5029ba fix mypy and tests 2018-07-29 20:55:06 +02:00
Gert Wohlgemuth 19b9966417 satisfied flake8 again 2018-07-29 20:55:06 +02:00
Gert Wohlgemuth 57f683697d revised code 2018-07-29 20:55:06 +02:00
Gert Wohlgemuth 296d3d8bbe working on refacturing of the strategy class 2018-07-29 20:55:06 +02:00
Matthias 336cd524a3 Merge pull request #1094 from freqtrade/pyup-scheduled-update-2018-07-29
Scheduled daily dependency update on sunday
2018-07-29 19:02:17 +02:00
Janne Sinivirta f832edf5bc remove useless docstrings from tests 2018-07-29 17:09:44 +03:00
Janne Sinivirta 1bbb86c621 remove nonsense asserts 2018-07-29 16:23:17 +03:00
pyup-bot 2ef35400c9 Update pytest from 3.6.3 to 3.6.4 2018-07-29 14:24:08 +02:00
pyup-bot 9c7f53d90d Update ccxt from 1.17.39 to 1.17.45 2018-07-29 14:24:06 +02:00
Matthias ebfcc0fc13 install numpy before ta-lib to fix build errors 2018-07-29 14:01:50 +02:00
Matthias 42024134ec Merge pull request #1092 from freqtrade/revert-1090-ujson-loader
Revert "backtesting: try to load data with ujson if it exists"
2018-07-29 12:23:25 +01:00
Matthias 7f27beff4b Revert "backtesting: try to load data with ujson if it exists" 2018-07-29 13:23:11 +02:00
creslinux dd71071740 Added logger.info when Sandbox is enabled. 2018-07-29 09:15:13 +00:00
creslinux c85c7a3a77 Documentation fixes. 2018-07-29 09:12:05 +00:00
creslinux 1e804c0df5 flake 8 2018-07-29 08:10:55 +00:00
creslinux fc06d028b8 Unit tests for sandbox pass / fail scenarios
Big Wave of appreciation to xmatthias for the guidence on how
Mocker works
2018-07-29 08:02:04 +00:00
Matthias 618784d060 Merge pull request #1090 from freqtrade/ujson-loader
backtesting: try to load data with ujson if it exists
2018-07-29 08:54:02 +01:00
Samuel Husso cfcc2e61e5 Merge pull request #1088 from freqtrade/fix/unpatched_mock
fix rpc test going to network
2018-07-29 09:53:52 +03:00
Samuel Husso 187e039a58 Merge pull request #1034 from freqtrade/feat/positive_sl_limit
add offset for positive trailing stop loss
2018-07-29 08:30:29 +03:00
Gert b3df1b1ba7 added documentation: 2018-07-28 21:31:20 -07:00
Gert ab66fe1b72 prepared for tracking signals 2018-07-28 19:45:33 -07:00
Gert ed47240b6e working on develop backslap 2018-07-28 18:30:12 -07:00
Gert 1a673c6ac9 working on moving backslap 2018-07-28 14:23:18 -07:00
creslinux 0a059662b3 Submitting with unit test for the working scenario.
Strongly recommend core team check the unit test is even targetting the
correct code in exchange/__init__.py

I have a real knowledge gap on mocker, in so far as how tests map to
what they're targeting.
2018-07-28 20:32:10 +00:00
Samuel Husso cb2fff8909 mypy doesn't handle common idiomacy so disable the line (see the open issue more details) 2018-07-28 22:06:26 +03:00
Samuel Husso cdd8cc551c backtesting: try to load data with ujson if it exists 2018-07-28 21:56:11 +03:00
creslinux 8648ac9da2 Update documentation with hot to sandbox test.
Allowing end-to-end GDAX API use without risking real money.
2018-07-28 17:42:56 +00:00
Samuel Husso 083befaafc Merge pull request #1087 from freqtrade/pyup-scheduled-update-2018-07-28
Scheduled daily dependency update on saturday
2018-07-28 16:26:38 +03:00
pyup-bot 099e7020c8 Update ccxt from 1.17.29 to 1.17.39 2018-07-28 14:24:06 +02:00
Samuel Husso 6ab8fa8c71 Merge pull request #1079 from creslinux/apiAuthPass
add Password option to API login, GDAX as example requires.
2018-07-28 13:53:39 +03:00
creslinux b2b81c8b2d Update documentation with hot to sandbox test.
Allowing end-to-end GDAX API use without risking real money.
2018-07-27 20:18:12 +00:00
Matthias 243b63e39c fix rpc test going to network (unsuitable for flights...) 2018-07-27 21:14:41 +01:00
Janne Sinivirta a3d870ad3e Merge pull request #1075 from freqtrade/extract_get_history
Extract get history from get_signal call
2018-07-27 20:54:20 +03:00
Matthias 1ceaa2200a Merge pull request #1080 from freqtrade/pyup-scheduled-update-2018-07-27
Scheduled daily dependency update on friday
2018-07-27 16:06:07 +01:00
Matthias c8ac98501c Merge pull request #1081 from sandoche/patch-1
Error fixed in the quickstart documentation
2018-07-27 16:05:51 +01:00
Sandoche ADITTANE ca0d658f15 Error fixed in the quickstart documentation 2018-07-27 15:28:06 +02:00
pyup-bot 4547ae930a Update ccxt from 1.17.20 to 1.17.29 2018-07-27 14:24:06 +02:00
creslin 40ae250193 Update constants.py
Adding UID also, as itll get ran into in future on an exchange that needs it.
2018-07-27 12:19:01 +00:00
creslinux c47253133a have to begin before we can stop 2018-07-27 12:07:07 +00:00
creslinux 7efa81073a Removed ; at line end. 2018-07-27 09:10:09 +00:00
creslinux d23b3ccc5e odd cut and paste error fixed. 2018-07-27 08:55:36 +00:00
Matthias 48cd468b6c Don't do all network calls at once without async 2018-07-27 07:40:27 +01:00
creslinux 0372485cf0 Some reason did not push this...
vector calcs redone.
2018-07-26 19:17:00 +00:00
creslinux 93ba80b5a9 Merge remote-tracking branch 'origin/backslap_numpy_poc' into backslap_numpy_poc 2018-07-26 19:15:53 +00:00
creslin 2dc3d6a6b8 Update backtesting.py 2018-07-26 18:42:20 +00:00
creslinux e39ae45d2f Some reason did not push this...
vector calcs redone.
2018-07-26 18:40:45 +00:00
Matthias df3e76a65d Remove legacy code, fix missed call 2018-07-26 19:11:51 +01:00
Matthias f2a9be3684 Adjust tests and remove legacy variable 2018-07-26 19:06:25 +01:00
Matthias 3324cdfcbe add mock for get_history in patch_get_signal 2018-07-26 18:58:49 +01:00
Matthias 484103b957 extract get_history_data from get_signal 2018-07-26 18:23:42 +01:00
Samuel Husso 6e437a7290 Merge pull request #1074 from freqtrade/pyup-scheduled-update-2018-07-26
Scheduled daily dependency update on thursday
2018-07-26 15:48:41 +03:00
pyup-bot 0c7ceadb27 Update ccxt from 1.17.11 to 1.17.20 2018-07-26 14:24:05 +02:00
Janne Sinivirta 726b94b077 Merge pull request #1069 from freqtrade/feat/movefiatconverttorpc
Feat/movefiatconverttorpc
2018-07-26 14:25:58 +03:00
creslin 482b85182a Update setup.py 2018-07-26 06:53:43 +00:00
Matthias 452a1cad9d don't default fiat_convert to None for outputs 2018-07-26 07:26:23 +01:00
Matthias 7b49f746d1 remove #FIX which was fixed 2018-07-25 22:47:20 +01:00
Matthias 78f8c6566e Merge pull request #1072 from freqtrade/datesorting-backtest-fix
Use pandas own min and max
2018-07-25 22:45:24 +01:00
creslin 79f931f296 Update backtesting.py 2018-07-25 19:49:25 +00:00
Janne Sinivirta 4b38c8b11d use pandas own min and max for column sorting 2018-07-25 17:04:25 +03:00
Samuel Husso 3fa1c5b19f Merge pull request #1070 from freqtrade/pyup-scheduled-update-2018-07-25
Scheduled daily dependency update on wednesday
2018-07-25 07:33:00 -05:00
pyup-bot 4f4daf4071 Update ccxt from 1.16.89 to 1.17.11 2018-07-25 14:24:07 +02:00
Matthias dc1ad3cbf6 whitespace issues 2018-07-24 23:08:40 +01:00
Matthias ff6435948e Fix random test failure 2018-07-24 22:53:10 +01:00
Matthias 23c2a75fc4 Merge pull request #1066 from freqtrade/pyup-scheduled-update-2018-07-24
Scheduled daily dependency update on tuesday
2018-07-24 13:53:35 +01:00
pyup-bot 7feea8c7a6 Update numpy from 1.14.5 to 1.15.0 2018-07-24 14:24:08 +02:00
pyup-bot cf6e229729 Update ccxt from 1.16.88 to 1.16.89 2018-07-24 14:24:06 +02:00
Matthias 4928686af9 Remove currency from daily table 2018-07-24 09:37:25 +01:00
Matthias 30b72ad98a don't show fiat-currency if not set 2018-07-24 08:20:32 +01:00
Matthias 1a9ead45eb fix missed fiat_display_currency config value 2018-07-24 08:00:56 +01:00
Janne Sinivirta 0b3190552e Merge pull request #1018 from freqtrade/feat/sell_reason
Record sell reason
2018-07-24 09:09:45 +03:00
Matthias 456e49fe35 default fiat_currency to none 2018-07-24 00:01:51 +01:00
Janne Sinivirta ab67822af2 Merge pull request #1062 from freqtrade/fix/migratescript
fix a bug in the database migration script
2018-07-23 16:48:12 +03:00
Janne Sinivirta 7f877aed6f Merge pull request #1063 from freqtrade/pyup-scheduled-update-2018-07-23
Scheduled daily dependency update on monday
2018-07-23 16:47:19 +03:00
pyup-bot 4575919d78 Update ccxt from 1.16.86 to 1.16.88 2018-07-23 14:24:05 +02:00
Matthias 10fc2c67c7 Fix bug causing a database-migration to fail from aspecific state 2018-07-23 09:10:37 +01:00
Matthias 643de58c4d Add test to check for a mid-migrated database (not old but not new) 2018-07-23 09:09:56 +01:00
Janne Sinivirta aba3c69765 Merge pull request #1061 from freqtrade/fix_networkcall
Add missing mock
2018-07-23 07:19:37 +03:00
Matthias 0775a371fe rename sellreason to sell_Reason, fix typos 2018-07-23 00:54:20 +01:00
Matthias 23fe0db2df Add missing mock 2018-07-22 17:06:42 +01:00
Matthias f54ac5a8de revert bugfix done in it's own branch 2018-07-22 17:05:22 +01:00
Matthias 4c8411537f Don't require fiat-currency 2018-07-22 14:53:46 +02:00
Matthias bd2771b8f9 use correct property 2018-07-22 14:52:58 +02:00
Matthias 4d864df59e Add tests for no_fiat functionality 2018-07-22 14:49:07 +02:00
Matthias fae4c3a4e3 only init if stake_currency is set 2018-07-22 14:48:06 +02:00
Matthias 2b297869a1 adjust checks to fit new functionality 2018-07-22 14:35:59 +02:00
Matthias 6cc0a72bca ADd optional to class _fiat_convert 2018-07-22 14:35:37 +02:00
Samuel Husso f53e03767c Merge pull request #1060 from freqtrade/pyup-scheduled-update-2018-07-22
Scheduled daily dependency update on sunday
2018-07-22 07:34:40 -05:00
pyup-bot 5ab1e66978 Update ccxt from 1.16.80 to 1.16.86 2018-07-22 14:24:05 +02:00
Samuel Husso 849ded7772 Merge pull request #1057 from freqtrade/fix/fiatconvert_error
Catch all exceptions from fiat-convert api calls
2018-07-21 23:12:56 -05:00
Matthias f297d22edb fix some tests in rpc_telegram 2018-07-21 20:49:57 +02:00
Matthias 0681a806cc move cryptofiatconvert to rpc 2018-07-21 20:44:38 +02:00
Matthias be3f04775a remove unnecessary mocks - add mocks which went to exchange 2018-07-21 20:21:00 +02:00
Matthias 9467461160 only init FIATConvert when telegram is enabled 2018-07-21 20:13:32 +02:00
Matthias 66af41192a Catch all exceptions from fiat-convert api calls 2018-07-21 19:50:38 +02:00
Matthias 6f7898809a Merge pull request #1055 from freqtrade/pyup-scheduled-update-2018-07-21
Scheduled daily dependency update on saturday
2018-07-21 14:40:26 +02:00
pyup-bot ab3478a742 Update ccxt from 1.16.75 to 1.16.80 2018-07-21 14:24:05 +02:00
Matthias 00fa41d63f Merge pull request #1051 from freqtrade/pyup-scheduled-update-2018-07-20
Scheduled daily dependency update on friday
2018-07-20 15:52:32 +02:00
pyup-bot 7f6c79eb76 Update ccxt from 1.16.68 to 1.16.75 2018-07-20 14:24:06 +02:00
Janne Sinivirta b45128f53d Merge pull request #1050 from freqtrade/xmatt_verbosity2
Add multiple verbosity levels
2018-07-20 11:42:42 +03:00
Matthias dd1290e38e Add multiple verbosity levels 2018-07-19 21:12:27 +02:00
Janne Sinivirta 62701888c9 Merge pull request #1049 from freqtrade/revert-1045-xmatt_verbosity
Revert "Add more verbosity levels"
2018-07-19 21:50:46 +03:00
Matthias 90915b6b2f Revert "Add more verbosity levels" 2018-07-19 20:43:41 +02:00
Matthias 1b2bfad348 Fix wrong test 2018-07-19 20:36:49 +02:00
Matthias 060469fefc Add stuff after rebase 2018-07-19 20:12:20 +02:00
Matthias 4fb9823cfb fix rebase problem 2018-07-19 19:50:06 +02:00
Matthias 760c79c5e9 Use .center() to output trades header line 2018-07-19 19:39:08 +02:00
Matthias a452864b41 Use namedtuple for sell_return 2018-07-19 19:39:08 +02:00
Matthias ad98c62329 update backtest anlaysis cheatsheet 2018-07-19 19:34:14 +02:00
Matthias 506aa0e3d3 Add print_sales table and test 2018-07-19 19:34:14 +02:00
Matthias 426c25f631 record ticker_interval and strategyname 2018-07-19 19:34:14 +02:00
Matthias 4059871c28 Add get_strategy_name 2018-07-19 19:34:14 +02:00
Matthias 2a61629014 Export sell_reason from backtest 2018-07-19 19:29:31 +02:00
Matthias 8c0b19f80c Check sell-reason for sell-reason-specific tests 2018-07-19 19:29:31 +02:00
Matthias 838b0e7b76 Remove unused import 2018-07-19 19:29:31 +02:00
Matthias cbffd3650b add sell_reason to backtesting 2018-07-19 19:29:31 +02:00
Matthias 0147b1631a remove optional from selltype 2018-07-19 19:27:33 +02:00
Matthias 49a7c7f08e fix tests 2018-07-19 19:27:33 +02:00
Janne Sinivirta 1af24af391 Merge pull request #1047 from freqtrade/pyup-scheduled-update-2018-07-19
Scheduled daily dependency update on thursday
2018-07-19 17:34:02 +03:00
Janne Sinivirta 0cc1b66ae7 Merge pull request #1037 from freqtrade/fix/backtest-comment
replace --realistic with 2 separate flags
2018-07-19 17:33:19 +03:00
Janne Sinivirta 6070d819b8 Merge pull request #1040 from freqtrade/xmatthias_backtest_duration
Fix backtest duration calculation
2018-07-19 17:32:11 +03:00
pyup-bot f2bfc9ccc2 Update ccxt from 1.16.57 to 1.16.68 2018-07-19 14:24:07 +02:00
Matthias f991109b0a Add sell-reason to sell-tree 2018-07-19 13:29:42 +02:00
Matthias 6bb7167b56 Add sellType enum 2018-07-19 13:25:48 +02:00
Matthias 365ba98131 add option to full_json example 2018-07-19 13:22:44 +02:00
Matthias 6a3c8e3933 update docs for trailing stoploss offset 2018-07-19 13:22:44 +02:00
Matthias c0a7725c1f Add stoploss offset 2018-07-19 13:22:44 +02:00
Matthias 71100a67c9 update documentation with new options 2018-07-19 13:20:15 +02:00
Matthias 8f254031c6 Add short form for parameters, change default for hyperopt 2018-07-19 13:19:36 +02:00
Matthias aa69177436 Properly check emptyness and adjust floatfmt 2018-07-19 13:14:21 +02:00
Matthias 64f933477d Merge pull request #1007 from freqtrade/remove-analyze
Remove Analyze
2018-07-19 10:12:36 +02:00
Janne Sinivirta aaa58a956d Merge pull request #1045 from freqtrade/xmatt_verbosity
Add more verbosity levels
2018-07-19 08:11:32 +03:00
Matthias 75c0a476f8 Test setting verbosity in commandline 2018-07-18 23:40:04 +02:00
Matthias 1ab7f5fb6d add tests for more debug levels 2018-07-18 22:53:44 +02:00
Matthias 789b98015f Allow different loglevels 2018-07-18 22:52:57 +02:00
Matthias 7134c15e86 Merge pull request #1024 from freqtrade/feature/webhook
Feature/webhook
2018-07-18 20:39:57 +02:00
Matthias 79b1030435 output duration in a more readable way 2018-07-18 20:08:55 +02:00
Matthias ac6955fd3b Merge pull request #1041 from freqtrade/pyup-scheduled-update-2018-07-18
Scheduled daily dependency update on wednesday
2018-07-18 14:39:57 +02:00
pyup-bot a374f95687 Update ccxt from 1.16.50 to 1.16.57 2018-07-18 14:24:07 +02:00
Matthias f9f6a3bd04 cast to int to keep exports constant 2018-07-18 09:29:51 +02:00
Matthias 8e4d2abd4e Fix typo 2018-07-18 09:10:17 +02:00
Matthias 08237abe20 Fix wrong backtest duration
identified in #1038
2018-07-18 09:06:12 +02:00
Matthias 5b3fa3c635 Merge pull request #1039 from Lufedi/develop
Add docs to get_trade_stake_amount function
2018-07-18 08:57:56 +02:00
Luis Felipe Diaz Chica ee8e890f50 Add docs to get_trade_stake_amount function 2018-07-18 01:36:39 -05:00
creslinux 3184c85dca default settings to trigger low, take stop 2018-07-17 21:33:11 +00:00
Matthias 3df79b8542 fix hanging intend 2018-07-17 21:12:05 +02:00
Matthias a290286fef update documentation 2018-07-17 21:05:31 +02:00
Matthias c82276ecbe add --disable-max-market-positions 2018-07-17 21:05:03 +02:00
Matthias b29eed32ca update documentation 2018-07-17 20:29:53 +02:00
Matthias e17618407b Rename --realistic-simulation to --enable-position-stacking 2018-07-17 20:26:59 +02:00
Janne Sinivirta 85fd4dd3ff rename analyze.py to exchange_helpers.py 2018-07-17 21:26:52 +03:00
Matthias 78205da4f0 Merge pull request #1036 from freqtrade/pyup-scheduled-update-2018-07-17
Scheduled daily dependency update on tuesday
2018-07-17 14:40:25 +02:00
pyup-bot e021d22c7f Update ccxt from 1.16.36 to 1.16.50 2018-07-17 14:24:09 +02:00
creslinux 8cea0517eb Added stop_stops
stop_stops is an int value
when number of stops in a pair reached the int the pair is stopped
trading.

This allows backtest to align with my pre_trade_mgt that does the same
in dry and live operations
2018-07-17 11:22:38 +00:00
creslinux ed4bf32f2a Fixed Stop closing in Index 0
when buy opening on Index 1
2018-07-17 10:59:17 +00:00
Janne Sinivirta 4a26eb34ea fix plot_profit to use strategy instead of Analyze 2018-07-17 11:47:09 +03:00
Janne Sinivirta 50b15b8052 fix plot_dataframe to use strategy instead of Analyze 2018-07-17 11:41:21 +03:00
Janne Sinivirta e11ec28962 remove leftover commented-out code 2018-07-17 11:13:35 +03:00
creslinux baaf0a5b21 Handle when 0 trades are found in any pairs being tested. 2018-07-17 08:12:21 +00:00
Janne Sinivirta 06d024cc46 make pytest ignore this file 2018-07-17 11:07:27 +03:00
Janne Sinivirta 084264669f fix the last failing unit test 2018-07-17 11:02:07 +03:00
Janne Sinivirta dbc3874b4f __init__ must return None to please mypy 2018-07-17 10:47:15 +03:00
Janne Sinivirta 78af4bc785 move and fix tests from Analyze to interface of strategy 2018-07-17 10:23:04 +03:00
creslinux a313917347 Handle a buy on the last candle
We will never see this, as buy is on close which is the end of backtest
e.g there is no next candle OPEN to buy at, or on
2018-07-16 18:59:48 +00:00
creslinux 357c8c0ba0 sensible defaults 2018-07-16 18:32:41 +00:00
creslinux 3b0cb7bc33 Added ujson and py_find_1st to setup.py 2018-07-16 18:06:31 +00:00
creslinux 8d5da4e6ad changed defaults
Seperated save trades and print trades options.
2018-07-16 17:48:11 +00:00
creslinux ec1960530b Added Show trades option
If true, prints trades ordered by date after summary.
Useful for spotting trends.
2018-07-16 17:06:06 +00:00
creslinux 99d16e82c0 disable time calcs output on vector displaying in debug. Excessive. 2018-07-16 16:30:11 +00:00
creslinux 885a653439 Disabled full debug on in last commit
Switched Stops to trigger on Low
Switched Stops to pay stop-rate not close.
2018-07-16 16:18:54 +00:00
creslinux 059aceb582 Disabled full debug on in last commit
Switched Stops to trigger on Low
Switched Stops to pay stop-rate not close.
2018-07-16 16:12:33 +00:00
creslinux 0f3339f74f use ujson to load ticker files 30% faster from disk. 2018-07-16 16:09:42 +00:00
creslinux 4a39a754f4 Fixed: self.use_backslap = Bool on line97
If self.use_backslap = True   Backslap executes
If self.use_backslap = False  Original Backtest Code executes
2018-07-16 15:57:15 +00:00
creslinux 5aaf454f12 GAS trades verified from candle data to excel by hand
All pass
3 sells 1 stop loss
2018-07-16 15:48:06 +00:00
creslinux fb0edd71ff in tech test 2018-07-16 14:16:35 +00:00
creslinux eed29a6b8a update 2018-07-16 13:16:18 +00:00
Matthias 2795db3ea0 Merge pull request #1033 from freqtrade/pyup-scheduled-update-2018-07-16
Scheduled daily dependency update on monday
2018-07-16 15:02:44 +02:00
pyup-bot 4f957728bf Update scikit-learn from 0.19.1 to 0.19.2 2018-07-16 14:24:07 +02:00
pyup-bot 62f4d734b9 Update ccxt from 1.16.33 to 1.16.36 2018-07-16 14:24:06 +02:00
creslinux 7174f27eb8 Rewrite to used algned numpy/dataframes
updated logic
added vector fill for abs/profit/duration in single hit on results.
2018-07-16 12:01:02 +00:00
Samuel Husso a3466f4b42 Merge pull request #1031 from freqtrade/feat/update_configdict
Update config dict with attributes loaded from strategy
2018-07-16 10:00:46 +03:00
Samuel Husso 050afe2bc0 Merge pull request #979 from creslinux/Check_timeframes
Handle if ticker_interval in config.json is not supported on exchange.
2018-07-16 09:57:46 +03:00
Janne Sinivirta 5c87c420c7 restore one analyze test 2018-07-16 08:59:14 +03:00
Janne Sinivirta aeb4102bcb refactor Analyze class methods to base Strategy class 2018-07-16 08:23:39 +03:00
Janne Sinivirta f6b8c2b40f move parse_ticker_dataframe outside Analyze class 2018-07-16 08:23:39 +03:00
Janne Sinivirta 85e6c9585a remove pass-through methods from Analyze 2018-07-16 08:23:39 +03:00
Janne Sinivirta a74147c472 move strategy initialization outside Analyze 2018-07-16 08:23:39 +03:00
creslinux a8b62a21cc hmm 2018-07-15 17:03:47 +00:00
Matthias 727f569e3a Merge pull request #1032 from freqtrade/pyup-scheduled-update-2018-07-15
Scheduled daily dependency update on sunday
2018-07-15 14:42:35 +02:00
pyup-bot 8f59759e97 Update ccxt from 1.16.16 to 1.16.33 2018-07-15 14:24:05 +02:00
creslinux 4e68362d46 Works with reporting output
Bugs
Calculating % prof ok, but abs wrong

BAT/BTC DF is very broken all OHLC are the same - but exposes a
buy after stop on last row "oddness" to be investigated / handled
2018-07-15 10:33:00 +00:00
creslinux 71c3106f8f Added ABS and Fees
Fixed Index Alignment that was off moving from scratch to FT
Fixed Stoploss,
  its a negative in FT, had been using positve stop -1 in scratch
2018-07-15 09:30:01 +00:00
Matthias 158226012a consistent use of the config dict within the test 2018-07-15 09:08:14 +02:00
Matthias b4ba641131 Update config dict with attributes loaded from strategy 2018-07-15 09:01:08 +02:00
creslinux 07175ebc5a up 2018-07-14 23:45:06 +00:00
creslinux 90e3c38757 First cut, Bslap
science project replacement for freqtrade backtest analysis

- appprox 300-500x quicker to execute

- fixes stop on close take close price bug in FT
Bslap is configurable but by default stops are triggerd on
low and pay stop price

Not implimented dynamic stops or roi
2018-07-14 22:54:23 +00:00
Matthias 682f4c1ade Merge pull request #1030 from freqtrade/pyup-scheduled-update-2018-07-14
Scheduled daily dependency update on saturday
2018-07-14 19:39:13 +02:00
pyup-bot e1de988f85 Update sqlalchemy from 1.2.9 to 1.2.10 2018-07-14 14:24:09 +02:00
pyup-bot bc83c34118 Update ccxt from 1.16.12 to 1.16.16 2018-07-14 14:24:07 +02:00
Matthias 278e7159bc adjust webhook tests 2018-07-14 13:32:35 +02:00
Matthias 1284627219 move url to private class level 2018-07-14 13:32:35 +02:00
Matthias 120fc29643 use dict comprehension 2018-07-14 13:32:35 +02:00
Matthias 6336d8a0e2 remove copy leftover 2018-07-14 13:32:35 +02:00
Matthias ee2f6ccbe9 Add test for enable_webhook 2018-07-14 13:32:35 +02:00
Matthias 144d308e5e Allow enabling of webhook 2018-07-14 13:32:35 +02:00
Matthias 3ca161f196 Add webhook config 2018-07-14 13:32:35 +02:00
Matthias f55df7ba63 improve README.md formatting (styling only) 2018-07-14 13:32:35 +02:00
Matthias 71df41c4eb add documentation for rpc_webhook 2018-07-14 13:32:35 +02:00
Matthias a4643066a8 allow more flexibility in webhook 2018-07-14 13:32:35 +02:00
Matthias 25250f7c10 don't hardcode post parameters 2018-07-14 13:32:35 +02:00
Matthias fa8512789f add tests for webhook 2018-07-14 13:32:35 +02:00
Matthias ae22af1ea3 fix typo 2018-07-14 13:32:35 +02:00
Matthias 6e16c1d80d add webhook test file 2018-07-14 13:32:35 +02:00
Matthias 266092a05d Merge pull request #1029 from freqtrade/mypy-fix
rpc: dont re-use variables with different types
2018-07-14 13:15:39 +02:00
Samuel Husso fa8b349200 rpc: dont re-use variables with different types 2018-07-14 08:02:39 +03:00
Samuel Husso 04bed3e53e Merge pull request #1027 from peterkorodi/patch-2
Update plotting.md
2018-07-13 22:50:10 -05:00
peterkorodi 68ddd1b951 Update plotting.md
Fix pairs and db-url in the doc
2018-07-14 00:07:38 +02:00
Samuel Husso b6e1020f39 Merge pull request #1026 from freqtrade/pyup-scheduled-update-2018-07-13
Scheduled daily dependency update on friday
2018-07-13 08:56:51 -05:00
pyup-bot 5b02b87735 Update ccxt from 1.16.6 to 1.16.12 2018-07-13 14:24:06 +02:00
Matthias c17e8d6abb Merge pull request #972 from freqtrade/feature/rewrite-rpc
Rewrite RPC module
2018-07-12 19:38:01 +02:00
gcarq cb8cd21e22 add tests for telegram.send_msg 2018-07-12 17:50:11 +02:00
gcarq a559e22f16 remove duplicate send_msg invocation 2018-07-12 17:29:02 +02:00
gcarq 7eaeb8d146 status: return arrow object instead humanized str 2018-07-12 17:27:40 +02:00
gcarq 0920fb6120 use more granular msg dict for buy/sell notifications 2018-07-12 17:16:31 +02:00
gcarq 4cb1aa1d97 use dict as argument for rpc.send_msg 2018-07-12 17:12:42 +02:00
gcarq 96a405feb7 implement name property in abstract class 2018-07-12 17:11:31 +02:00
gcarq 112998c205 refactor _rpc_balance 2018-07-12 17:11:31 +02:00
gcarq f1a370b3b9 return dict from _rpc_status and handle rendering in module impl 2018-07-12 17:10:04 +02:00
gcarq 29670b9814 remove markdown formatting from exception string 2018-07-12 17:07:19 +02:00
gcarq df8ba28ce5 convert start, stop and reload_conf to return a dict 2018-07-12 17:07:19 +02:00
Matthias 5288e18f2f Merge pull request #1022 from freqtrade/pyup-scheduled-update-2018-07-12
Scheduled daily dependency update on thursday
2018-07-12 14:33:14 +02:00
pyup-bot ddfc4722b9 Update ccxt from 1.15.42 to 1.16.6 2018-07-12 14:23:06 +02:00
Janne Sinivirta bd46b4faf3 Merge pull request #1015 from freqtrade/xmatthias-patch-1
add missing s to Backtest cum results
2018-07-11 16:18:07 +03:00
Matthias 46708e7d29 Merge pull request #1014 from freqtrade/pyup-scheduled-update-2018-07-11
Scheduled daily dependency update on wednesday
2018-07-11 14:50:09 +02:00
Matthias 06c9494a46 add missing s to Backtest cum results 2018-07-11 14:50:04 +02:00
pyup-bot 8f6252b312 Update ccxt from 1.15.35 to 1.15.42 2018-07-11 14:23:06 +02:00
Janne Sinivirta 1f16ff268f Merge pull request #1010 from jblestang/refactoring_create_trade_function
Refactoring Create Trade
2018-07-11 07:23:03 +03:00
Janne Sinivirta aa2366346a Merge pull request #1001 from xmatthias/feat/backtest_cum_profit
Add cumulative profit to backtest result table
2018-07-11 07:21:28 +03:00
Janne Sinivirta 8b72560eba Merge pull request #1006 from freqtrade/update_plotly
Update plotly
2018-07-11 07:20:33 +03:00
Jean-Baptiste LE STANG 773fb5953b Reafcotring Create Trade 2018-07-10 15:10:56 +02:00
Matthias 3540ba3712 Merge pull request #1009 from freqtrade/pyup-scheduled-update-2018-07-10
Scheduled daily dependency update on tuesday
2018-07-10 14:35:33 +02:00
pyup-bot d546a4b29f Update ccxt from 1.15.28 to 1.15.35 2018-07-10 14:23:08 +02:00
Janne Sinivirta b4be3c2499 Merge pull request #1002 from xmatthias/test/use_open_backtest
Use open-rates for backtesting
2018-07-10 09:20:32 +03:00
Matthias 85c60519b0 Fix test crash 2018-07-09 22:11:12 +02:00
Matthias 6be6448334 replace "transparent" with rgb to fix exception in plotly 3.0.0 2018-07-09 21:56:29 +02:00
Matthias f5bc65b877 update plotly 2018-07-09 21:56:24 +02:00
Matthias a7a82635b4 Merge pull request #1004 from berlinguyinca/patch-2
Fixing database issues
2018-07-09 21:54:21 +02:00
Samuel Husso b9916b60f9 Merge pull request #1005 from freqtrade/pyup-scheduled-update-2018-07-09
Scheduled daily dependency update on monday
2018-07-09 08:26:54 -05:00
pyup-bot b773e3472a Update ccxt from 1.15.27 to 1.15.28 2018-07-09 14:23:06 +02:00
Gert Wohlgemuth 4654792784 Fixing database issues
1. if database is defined in config file, it currently tosses an exception that only export file or db is defined
2. if trades are loaded from databases, plot crashes with an exception 'cannot compare tz-naive and tz-aware datetime-like objects'
3. if Trade is not closed, crashes with exception that NoneType has no field timestamp

all should be fixed
2018-07-08 22:43:34 -07:00
Matthias 750d737b7d Add tests for change to open_rate 2018-07-08 20:18:34 +02:00
Matthias 0bd9674b5c Merge pull request #1000 from pan-long/fix-doc
Update doc for manually fix trade
2018-07-08 20:07:25 +02:00
Matthias 8b06000f0f Use open-rates for backtesting 2018-07-08 20:03:11 +02:00
Matthias efaa8f16e7 Improve formattiong of table 2018-07-08 20:01:33 +02:00
Matthias 38487644f0 fix tests for backtest-result output table 2018-07-08 19:55:16 +02:00
Matthias 1a24afef77 add cumsum to backtest-results 2018-07-08 19:55:04 +02:00
Janne Sinivirta 8fb146ba6a Merge pull request #992 from freqtrade/backtest_optimize
reduce calculation effort by removing a call to calc_profit_percent
2018-07-08 17:41:50 +03:00
Janne Sinivirta 05b078b8dd Merge pull request #999 from freqtrade/pyup-scheduled-update-2018-07-08
Scheduled daily dependency update on sunday
2018-07-08 17:40:42 +03:00
Janne Sinivirta 6926e468a4 Merge pull request #984 from freqtrade/test_backtest_results
Test backtest results
2018-07-08 17:40:12 +03:00
Janne Sinivirta 34764108cc Merge pull request #997 from freqtrade/fix/timedout_candle
don't flag data as outdated which isn't
2018-07-08 17:36:03 +03:00
pyup-bot 17c9c183f5 Update pandas from 0.23.2 to 0.23.3 2018-07-08 14:23:07 +02:00
pyup-bot cc107bb3cc Update ccxt from 1.15.25 to 1.15.27 2018-07-08 14:23:05 +02:00
Matthias 8dd6e29426 don't flag data as outdated which isn't 2018-07-08 13:34:47 +02:00
Matthias 3e03a208f1 reduce calculation effort (slightly!) 2018-07-07 20:17:53 +02:00
Matthias 570d27a0c4 Add testcase where ticker_interval is not in the configuration 2018-07-07 15:30:29 +02:00
Samuel Husso 7c8c8e83d3 Merge pull request #990 from freqtrade/update_dockerfile
Update Dockerfile to 3.6.6
2018-07-07 08:15:20 -05:00
Matthias 2b488d1da2 Update Dockerfile to 3.6.6 2018-07-07 14:52:39 +02:00
Matthias e98efe3a35 Merge pull request #989 from freqtrade/pyup-scheduled-update-2018-07-07
Scheduled daily dependency update on saturday
2018-07-07 14:43:32 +02:00
Matthias 3f6e9cd28f Add tests for validate_timeframes 2018-07-07 14:42:53 +02:00
Matthias af17cef002 fix existing tests to work with validate_timeframes 2018-07-07 14:41:42 +02:00
pyup-bot 742fefa786 Update pandas from 0.23.1 to 0.23.2 2018-07-07 14:23:08 +02:00
pyup-bot 08fe10e302 Update ccxt from 1.15.21 to 1.15.25 2018-07-07 14:23:06 +02:00
Matthias 9906da46f6 move comment to correct place 2018-07-06 20:00:54 +02:00
Matthias 54976fa103 Add more tests to validate buy/sell rows 2018-07-06 19:56:16 +02:00
Samuel Husso e1d7c72bb8 Merge pull request #983 from freqtrade/pyup-scheduled-update-2018-07-06
Scheduled daily dependency update on friday
2018-07-06 09:41:10 -05:00
pyup-bot af03c17209 Update ccxt from 1.15.13 to 1.15.21 2018-07-06 14:23:06 +02:00
Gert Wohlgemuth 1897a1cb6a fixed mypy issues, seriosuly... 2018-07-05 16:10:38 -07:00
Gert Wohlgemuth 58879ff012 fixed braket 2018-07-05 15:01:53 -07:00
Gert Wohlgemuth e1f5745f59 Update resolver.py 2018-07-05 14:50:23 -07:00
Gert Wohlgemuth 1c48902e64 Merge branch 'develop' into BASE64 2018-07-05 14:40:04 -07:00
Gert Wohlgemuth 8bbee4038b integrated BASE64 encoded strategy loading 2018-07-05 14:30:24 -07:00
Matthias c35d1b9c9d Add test which checks the backtest result 2018-07-05 23:22:35 +02:00
Matthias 4f642b769c Merge pull request #981 from freqtrade/fstrings-in-use
Fstrings in use
2018-07-05 22:18:15 +02:00
Samuel Husso e808b3a2a1 rpc: get rid of extra else and fix mypy warning 2018-07-05 10:47:08 -05:00
Samuel Husso df68b0990f rpc: fstrings 2018-07-05 10:11:29 -05:00
Samuel Husso adbffc69e1 telegram: fstrings in use 2018-07-05 10:11:29 -05:00
Samuel Husso 21fc933678 convert_backtesting: fstrings in use 2018-07-05 10:11:29 -05:00
Samuel Husso a2063ede55 persistence: fstrings in use 2018-07-05 10:11:29 -05:00
Samuel Husso 7dca3c6d03 freqtradebot,main,hyperopt: fstrings in use 2018-07-05 10:11:29 -05:00
Samuel Husso 03c112a601 config, optimize: fstrings in use 2018-07-05 10:11:29 -05:00
Matthias c77686c7a7 Merge pull request #980 from freqtrade/pyup-scheduled-update-2018-07-05
Scheduled daily dependency update on thursday
2018-07-05 15:39:57 +02:00
pyup-bot 239f8606e1 Update pytest from 3.6.2 to 3.6.3 2018-07-05 14:23:12 +02:00
pyup-bot bfd1e90154 Update ccxt from 1.15.8 to 1.15.13 2018-07-05 14:23:11 +02:00
creslinux 5ab644dea6 flake 8 fix 2018-07-05 12:05:31 +00:00
creslinux 966668f48a Handle if ticker_interval in config.json is not supported on exchange.
Returns.

Tested positive and negative data.
The ticker list in constants.py may be obsolete now, im not sure.

 raise OperationalException(f'Invalid ticker {timeframe}, this Exchange supports {timeframes}')
freqtrade.OperationalException: Invalid ticker 14m, this Exchange supports {'1m': '1m', '3m': '3m', '5m': '5m', '15m': '15m', '30m': '30m', '1h': '1h', '2h': '2h', '4h': '4h', '6h': '6h', '8h': '8h', '12h': '12h', '1d': '1d', '3d': '3d', '1w': '1w', '1M': '1M'}
2018-07-05 11:57:59 +00:00
Samuel Husso d8d0579c5a Merge pull request #930 from freqtrade/skopt
Replace Hyperopt with scikit-optimize
2018-07-04 13:51:14 -05:00
Michael Egger 64c68d93c3 Merge pull request #976 from freqtrade/sort-imports
sort imports
2018-07-04 16:59:42 +02:00
Matthias 700f02dde8 Merge pull request #977 from freqtrade/pyup-scheduled-update-2018-07-04
Scheduled daily dependency update on wednesday
2018-07-04 15:26:32 +02:00
pyup-bot ac20bf31df Update ccxt from 1.15.7 to 1.15.8 2018-07-04 14:23:06 +02:00
Janne Sinivirta bf4d0a9b70 sort imports 2018-07-04 10:31:35 +03:00
Janne Sinivirta 96bb2efe69 use joblib.dump and load for trials 2018-07-03 23:08:29 +03:00
Janne Sinivirta c4a8435e00 change pickle file name to better suit it's current purpose 2018-07-03 22:17:43 +03:00
Janne Sinivirta 9dbe0f50a3 fix tests after changing the dumping and pickling dataframe in hyperopt 2018-07-03 22:09:59 +03:00
Janne Sinivirta 3a7056ea1b run at least one epoch 2018-07-03 21:55:22 +03:00
Janne Sinivirta 2cde540645 remove dead code 2018-07-03 21:50:45 +03:00
Janne Sinivirta ef59f9ad24 sort imports in hyperopt.py 2018-07-03 21:50:24 +03:00
Matthias e91cfbfeeb Merge pull request #975 from freqtrade/pyup-scheduled-update-2018-07-03
Scheduled daily dependency update on tuesday
2018-07-03 14:35:45 +02:00
pyup-bot 2c0e950486 Update ccxt from 1.15.3 to 1.15.7 2018-07-03 14:23:05 +02:00
Janne Sinivirta ee4754cfb9 avoid re-serialization of whole dataframe 2018-07-03 14:49:58 +03:00
Janne Sinivirta 4a26b88a17 improve documentation 2018-07-03 12:51:02 +03:00
Janne Sinivirta 2713fdb860 use cpu count explicitly in job count 2018-07-03 11:46:56 +03:00
Janne Sinivirta 79aab4cce2 use fstring 2018-07-03 11:44:54 +03:00
Samuel Husso 2b34d10973 Merge pull request #973 from freqtrade/pyup-scheduled-update-2018-07-02
Scheduled daily dependency update on monday
2018-07-02 08:57:27 -05:00
pyup-bot 76343ecb77 Update ccxt from 1.14.301 to 1.15.3 2018-07-02 14:23:06 +02:00
Janne Sinivirta fa8fc3e4ce handle the case where we have zero buys 2018-07-02 11:46:55 +03:00
Janne Sinivirta aec3f582e1 Merge branch 'develop' into skopt 2018-07-02 11:27:27 +03:00
Janne Sinivirta a58d51ded0 update hyperopt documentation 2018-07-02 09:56:58 +03:00
Michael Egger 5e4a6ba7ba Merge pull request #963 from freqtrade/feat/stop_loss
Feat/stop loss
2018-07-01 20:50:13 +02:00
xmatthias 3c5be55eb9 remove unnecessary variable 2018-07-01 20:17:30 +02:00
xmatthias 782570e71e Address PR comment 2018-07-01 20:03:07 +02:00
Matthias ed2a1becef Merge branch 'develop' into feat/stop_loss 2018-07-01 20:01:02 +02:00
xmatthias 937644a04b change while-loop to enumerate - add intensified test for this scenario 2018-07-01 19:55:51 +02:00
xmatthias e39d88ef65 Address some PR comments 2018-07-01 19:54:26 +02:00
Michael Egger f91263c8ef Merge pull request #966 from freqtrade/feat/revamp_exchangetest
Rewrite standard ccxt exception handling
2018-07-01 19:47:57 +02:00
Michael Egger e2127f5af1 Merge pull request #969 from xmatthias/split_unfilled
separating unfulfilled timeouts for buy and sell
2018-07-01 19:47:24 +02:00
xmatthias 2dc881558d address PR comments 2018-07-01 19:41:19 +02:00
xmatthias c66f858b98 rename innerfun to mock_ccxt_fun 2018-07-01 19:37:55 +02:00
Michael Egger 8023fdf923 Merge pull request #971 from freqtrade/fix/nonmocked_markets
Add get_markets mock to new tests
2018-07-01 15:11:22 +02:00
Michael Egger 2cee8e52c1 Merge pull request #965 from freqtrade/fix/fix_959
catch crash with cobinhood
2018-07-01 14:28:01 +02:00
Nullart 8f49d5eb10 documentation updates 2018-06-30 19:32:56 +02:00
xmatthias 9e3e900f78 Add get_markets mock to new tests 2018-06-30 17:49:46 +02:00
xmatthias 14e12bd3c0 Fix missing comma in example.json 2018-06-30 17:37:34 +02:00
Samuel Husso c29163a51c Merge pull request #970 from freqtrade/pyup-scheduled-update-2018-06-30
Scheduled daily dependency update on saturday
2018-06-30 09:37:38 -05:00
pyup-bot 5a591e01c0 Update sqlalchemy from 1.2.8 to 1.2.9 2018-06-30 14:23:07 +02:00
pyup-bot c447644fd1 Update ccxt from 1.14.295 to 1.14.301 2018-06-30 14:23:06 +02:00
Nullart 98108a78f1 separating unfulfilled timeouts for buy and sell 2018-06-30 13:44:42 +02:00
Janne Sinivirta 0ce08932ed mypy fixes 2018-06-30 09:54:31 +03:00
Michael Egger 6dd5f85fb6 Merge pull request #954 from freqtrade/feat/allow_backtest_plot
allow backtest ploting
2018-06-29 19:44:06 +02:00
Samuel Husso d8f2a683c6 Merge pull request #967 from freqtrade/pyup-scheduled-update-2018-06-29
Scheduled daily dependency update on friday
2018-06-29 08:32:34 -05:00
pyup-bot 8a941f3aa8 Update ccxt from 1.14.289 to 1.14.295 2018-06-29 14:23:06 +02:00
xmatthias cf6b1a637a increase exchange code coverage 2018-06-28 22:32:28 +02:00
xmatthias dcdc18a338 rename test-function 2018-06-28 22:18:38 +02:00
xmatthias 15c7854e7f add test for exchange_has 2018-06-28 22:11:45 +02:00
xmatthias fe8a21681e add test for Not supported 2018-06-28 21:56:37 +02:00
xmatthias ebbfc720b2 increase test coverage 2018-06-28 21:51:59 +02:00
xmatthias 8ec9a09749 Standardize retrier exception testing 2018-06-28 21:22:43 +02:00
xmatthias 2d4ce593b5 catch crash with cobinhood
fixes #959
2018-06-28 19:53:51 +02:00
Matthias c5a00b4d45 Merge pull request #964 from freqtrade/pyup-scheduled-update-2018-06-28
Scheduled daily dependency update on thursday
2018-06-28 14:42:55 +02:00
pyup-bot 7cecae5279 Update ccxt from 1.14.288 to 1.14.289 2018-06-28 14:23:07 +02:00
xmatthias d5ad066f8d support multiple db transitions by keeping the backup-table dynamic 2018-06-27 20:15:25 +02:00
xmatthias 860b270e30 update db migrate script to work for more changes 2018-06-27 19:49:08 +02:00
Samuel Husso 35e07bf11e Merge pull request #962 from freqtrade/pyup-scheduled-update-2018-06-27
Scheduled daily dependency update on wednesday
2018-06-27 08:40:39 -05:00
pyup-bot 19beb0941f Update ccxt from 1.14.272 to 1.14.288 2018-06-27 14:23:07 +02:00
xmatthias 8ecdae67e1 add mypy ignore (and comment as to why) 2018-06-27 06:57:41 +02:00
xmatthias e6e868a03c remove markdown code type as it is not valid json 2018-06-27 06:54:29 +02:00
xmatthias 78e6c9fdf6 add tests for trailing stoploss 2018-06-27 06:52:31 +02:00
xmatthias c997aa9864 move initial logic to persistence 2018-06-27 06:38:49 +02:00
xmatthias a91d75b3b2 Add test for adjust_stop-loss 2018-06-27 06:23:49 +02:00
xmatthias e9d5bceeb9 cleanly check if stop_loss is initialized 2018-06-27 00:18:50 +02:00
xmatthias 88b898cce4 add test for moving stoploss 2018-06-27 00:18:30 +02:00
xmatthias 8bec505bbe add test for trailing_stoploss 2018-06-26 23:40:36 +02:00
xmatthias a3708bc56e add missing test 2018-06-26 23:40:20 +02:00
xmatthias 03005bc0f1 update documentation 2018-06-26 23:14:12 +02:00
xmatthias da5be9fbd0 add stop_loss based on work from @berlinguyinca 2018-06-26 23:06:27 +02:00
xmatthias 3e167e1170 update sample configs 2018-06-26 22:41:38 +02:00
xmatthias 5015bc9bb0 slight update to persistence 2018-06-26 22:41:28 +02:00
xmatthias 243c36b39b get persistence.py for stop_loss 2018-06-26 20:49:07 +02:00
xmatthias 9ac3c559b6 fix some stoploss documentation 2018-06-26 20:30:16 +02:00
peterkorodi 257e1847b1 Update stoploss.md 2018-06-26 20:30:10 +02:00
Gert Wohlgemuth 54f52fb366 Create stoploss.md 2018-06-26 20:30:03 +02:00
Matthias e1d8a59b69 Merge pull request #960 from freqtrade/pyup-scheduled-update-2018-06-26
Scheduled daily dependency update on tuesday
2018-06-26 14:43:31 +02:00
pyup-bot 7c2a50cef9 Update ccxt from 1.14.267 to 1.14.272 2018-06-26 14:23:06 +02:00
Samuel Husso 4c7d1c90db Merge pull request #957 from freqtrade/pyup-scheduled-update-2018-06-25
Scheduled daily dependency update on monday
2018-06-25 08:15:30 -05:00
pyup-bot 4f1fa28658 Update ccxt from 1.14.257 to 1.14.267 2018-06-25 14:23:06 +02:00
Janne Sinivirta 2b6407e598 remove unused tests from hyperopt 2018-06-25 11:38:42 +03:00
Janne Sinivirta 0bddc58ec4 extract loading previous results to a method 2018-06-25 11:38:14 +03:00
Janne Sinivirta 17ee7f8be5 fix typo in requirements.txt 2018-06-25 11:15:11 +03:00
Michael Egger 375ea940f4 Merge pull request #956 from freqtrade/fix/download_backtest
slight rework of download script
2018-06-24 21:44:09 +02:00
xmatthias 43f1a1d264 rework download_backtest script 2018-06-24 19:52:12 +02:00
xmatthias e70cb963f7 document what to do with exported backtest results 2018-06-24 17:00:00 +02:00
Samuel Husso a8cb0b0321 Merge pull request #955 from freqtrade/pyup-scheduled-update-2018-06-24
Scheduled daily dependency update on sunday
2018-06-24 08:01:04 -05:00
Janne Sinivirta 118a43cbb8 fixing tests for hyperopt 2018-06-24 15:27:53 +03:00
pyup-bot 5e7e977ffa Update ccxt from 1.14.256 to 1.14.257 2018-06-24 14:23:05 +02:00
xmatthias 660ec6f443 fix parameter type 2018-06-24 13:43:27 +02:00
gcarq e98f22ef2f Merge branch 'master' of https://github.com/freqtrade/freqtrade into develop 2018-06-24 00:39:11 +02:00
Samuel Husso 2bb63ba33d Merge pull request #953 from freqtrade/release-0.17.0
Release 0.17.0
2018-06-23 16:22:51 -05:00
Samuel Husso 1529ce8bdb Merge pull request #952 from freqtrade/bump-version
bump develop to 0.17.1
2018-06-23 16:21:56 -05:00
xmatthias d8cb63efdd extract load_trades 2018-06-23 20:19:07 +02:00
xmatthias 5055563458 add --plot-limit 2018-06-23 20:14:15 +02:00
xmatthias f506ebcd62 use Pathlib in the whole script 2018-06-23 19:58:28 +02:00
xmatthias 3cedace2f6 add plotting for backtested trades 2018-06-23 19:54:27 +02:00
Samuel Husso 3384679bad bump develop to 0.17.1 2018-06-23 09:38:20 -05:00
Samuel Husso 46a062d5fb Drafting freqtrade 0.17.0 release 2018-06-23 09:35:52 -05:00
Samuel Husso 8b7183cdbc Merge pull request #951 from freqtrade/readme-update
README: note to open an issue before starting major feature work
2018-06-23 09:32:56 -05:00
Michael Egger beb15532f7 Merge pull request #950 from freqtrade/fix-filenotfounderror
StrategyResolver: Don't fail if user_data isn't present
2018-06-23 16:07:52 +02:00
Michael Egger 107f3ed35b Merge pull request #760 from arudov/feature-unlimited-stake_amount
Feature unlimited stake amount
2018-06-23 16:07:38 +02:00
Anton f82b809fcf Merge with develop 2018-06-23 16:50:27 +03:00
Samuel Husso 9bad75f37d README: note to open an issue before starting major feature work 2018-06-23 08:36:32 -05:00
Samuel Husso 864bbc441a Merge pull request #882 from freqtrade/feature/revamp_readme
Update the README structure
2018-06-23 08:21:56 -05:00
Michael Egger e2df908304 Merge pull request #949 from freqtrade/pyup-scheduled-update-2018-06-23
Scheduled daily dependency update on saturday
2018-06-23 14:56:52 +02:00
Janne Sinivirta 642ad02316 remove unused import 2018-06-23 15:56:38 +03:00
Janne Sinivirta ab9e2fcea0 fix guard names to match search space 2018-06-23 15:47:19 +03:00
Janne Sinivirta 136456afc0 add three triggers to hyperopting 2018-06-23 15:44:51 +03:00
gcarq 4ea5fcc661 resolver: don't fail if user_data can't be found 2018-06-23 14:42:22 +02:00
gcarq 9c66c25890 resolver: use current folder instead of script folder to find user_data 2018-06-23 14:34:36 +02:00
pyup-bot 925b9b0c19 Update ccxt from 1.14.253 to 1.14.256 2018-06-23 14:23:07 +02:00
Janne Sinivirta 09261b11af remove hyperopt and networkx from dependencies 2018-06-23 15:22:14 +03:00
Matthias e25d8f9435 Merge pull request #947 from freqtrade/code-cleanup
Remove global config from persistence module
2018-06-23 14:21:42 +02:00
xmatthias 0440a19171 export open/close rate for backtesting too
preparation to allow plotting of backtest results
2018-06-23 14:19:50 +02:00
gcarq 0b3e4f6bcd remove dead code 2018-06-23 13:50:49 +02:00
gcarq 295dfe2652 persistence: remove obsolete global _CONF variable 2018-06-23 13:50:22 +02:00
Michael Egger df9015a7f1 Merge pull request #942 from xmatthias/feat/buy_on_sell_first
Introduce ignore_roi_if_buy_signal parameter to avoid sell/buy scenarios
2018-06-23 13:42:03 +02:00
Janne Sinivirta e8f2e6956d to avoid pickle problems, get rid of reference to exchange after initialization 2018-06-23 14:37:36 +03:00
Janne Sinivirta dde7df7fd3 add scikit-optimize to dependencies 2018-06-23 14:37:36 +03:00
Janne Sinivirta a525cba8e9 switch signal handler to try catch. fix pickling and formatting output 2018-06-23 14:37:36 +03:00
Janne Sinivirta 8272120c3a convert stoploss and ROI search spaces to skopt format 2018-06-23 14:37:36 +03:00
Janne Sinivirta 8fee2e2409 move result logging out from optimizer 2018-06-23 14:37:36 +03:00
Janne Sinivirta c415014153 use multiple jobs in acq 2018-06-23 14:37:36 +03:00
Janne Sinivirta 964cbdc262 increase initial sampling points 2018-06-23 14:37:36 +03:00
Janne Sinivirta a46badd5c0 reuse pool workers 2018-06-23 14:37:36 +03:00
Janne Sinivirta 0cb1aedf5b problem with pickling 2018-06-23 14:37:36 +03:00
Janne Sinivirta b485e6e0ba start small 2018-06-23 14:37:36 +03:00
gcarq 810d7de869 tests: add dir() assertion 2018-06-23 14:37:36 +03:00
gcarq 398b21a11d implement test for import_strategy 2018-06-23 14:37:36 +03:00
gcarq 78f50a1471 move logic from hyperopt to freqtrade.strategy 2018-06-23 14:37:36 +03:00
gcarq 5aae215c94 wrap strategies with HyperoptStrategy for module lookups with pickle 2018-06-23 14:37:36 +03:00
xmatthias 2738d3aed8 update plotly 2018-06-23 14:37:36 +03:00
Janne Sinivirta 01d45bee76 fix flake8 2018-06-23 14:37:36 +03:00
Janne Sinivirta c1691f21f3 check that we set fee on backtesting init 2018-06-23 14:37:36 +03:00
Janne Sinivirta a68c90c512 avoid calling exchange.get_fee inside loop 2018-06-23 14:37:36 +03:00
Janne Sinivirta 90caa09ae0 Merge pull request #944 from freqtrade/improve-strategy-handling
Improve strategy handling
2018-06-23 14:32:39 +03:00
Michael Egger 909fd39b80 Merge pull request #945 from freqtrade/update_plotly
update plotly
2018-06-23 13:15:15 +02:00
xmatthias d23cd73ba8 update plotly 2018-06-23 13:12:36 +02:00
xmatthias fc219b4e94 move experimental eval below stop_loss_reached to improve performance 2018-06-23 13:10:08 +02:00
gcarq 818a6b12ed tests: add dir() assertion 2018-06-23 11:57:26 +02:00
gcarq 4bd61df3a7 implement test for import_strategy 2018-06-23 11:14:31 +02:00
gcarq c40e6a12d1 move logic from hyperopt to freqtrade.strategy 2018-06-23 11:13:49 +02:00
gcarq 3360bf4001 wrap strategies with HyperoptStrategy for module lookups with pickle 2018-06-23 10:42:33 +02:00
Michael Egger 168ed91fe1 Merge pull request #941 from freqtrade/avoid-fee-calls-backtesting
avoid calling exchange.get_fee inside loop
2018-06-23 08:17:25 +02:00
Janne Sinivirta 9a07d57ed7 fix flake8 2018-06-23 07:58:25 +03:00
xmatthias 2be7b3d9eb fix mocked bid-value to match limt_buy_order config 2018-06-22 21:24:21 +02:00
xmatthias e2a2a0be9b extract stop_loss_reached to allow check before ignore_roi_if_buy_signal 2018-06-22 21:21:34 +02:00
Janne Sinivirta f7e5d2c3a5 check that we set fee on backtesting init 2018-06-22 21:55:09 +03:00
xmatthias cbfee51f32 introduce experimental variable and fix test naming 2018-06-22 20:51:21 +02:00
xmatthias 8a44dff595 don't sell if buy is still active 2018-06-22 20:23:23 +02:00
Janne Sinivirta c73b9f5c77 avoid calling exchange.get_fee inside loop 2018-06-22 21:04:07 +03:00
Pan Long e759a90b2d Update doc for manually fix trade
The profit should be close_rate/open_rate-1   not close_rate/open_rate
2018-06-22 19:16:48 +05:30
Samuel Husso c413e94f83 Merge pull request #940 from freqtrade/pyup-scheduled-update-2018-06-22
Scheduled daily dependency update on friday
2018-06-22 16:14:20 +03:00
pyup-bot 98cd8970f9 Update ccxt from 1.14.242 to 1.14.253 2018-06-22 14:24:06 +02:00
Janne Sinivirta 5fcdd3831c Merge pull request #928 from freqtrade/feat/objectify_exchange
Objectify exchange
2018-06-22 06:36:14 +03:00
xmatthias 7f927b4d7a Squashed commit of the following:
commit 435f299bcf
Author: Gert Wohlgemuth <berlinguyinca@gmail.com>
Date:   Wed Jun 20 01:57:28 2018 -0700

    improve readability of outdated history code
2018-06-21 20:47:53 +02:00
Matthias 99e3c6e526 Merge pull request #936 from freqtrade/pyup-scheduled-update-2018-06-21
Scheduled daily dependency update on thursday
2018-06-21 15:20:22 +02:00
pyup-bot c7976f51e2 Update ccxt from 1.14.230 to 1.14.242 2018-06-21 14:24:06 +02:00
Michael Egger 2c43590268 Merge pull request #933 from freqtrade/pyup-scheduled-update-2018-06-20
Scheduled daily dependency update on wednesday
2018-06-20 14:36:44 +02:00
pyup-bot 36cfea3d0f Update pytest from 3.6.1 to 3.6.2 2018-06-20 14:23:08 +02:00
pyup-bot a493a2ceef Update ccxt from 1.14.224 to 1.14.230 2018-06-20 14:23:06 +02:00
Michael Egger 96b7273b8f Merge pull request #931 from freqtrade/pyup-scheduled-update-2018-06-19
Scheduled daily dependency update on tuesday
2018-06-19 16:27:30 +02:00
pyup-bot e66b861c9e Update ccxt from 1.14.211 to 1.14.224 2018-06-19 14:23:05 +02:00
Michael Egger e0db31e9db Merge pull request #929 from freqtrade/backtest_docker
Update Documentation to include backtesting with docker
2018-06-18 22:54:18 +02:00
xmatthias a7be15d72f Update Documentation to include backtesting with docker 2018-06-18 22:42:14 +02:00
xmatthias f7b46d5404 update docstring 2018-06-18 22:34:28 +02:00
xmatthias 488f1717a1 update plot_dataframe script to objectify exchange 2018-06-18 22:32:29 +02:00
xmatthias 2b0ef54595 update download_script for exchange objectify 2018-06-18 22:28:51 +02:00
xmatthias 896afe7118 convert get_name and get_id to properties 2018-06-18 22:20:50 +02:00
xmatthias ef53134499 lowercase variables 2018-06-18 22:09:46 +02:00
xmatthias c31519fdb2 lowercase _api object 2018-06-18 22:07:15 +02:00
xmatthias 162f948729 add test for non-configured exchange 2018-06-18 19:56:23 +02:00
xmatthias ae4c4e77bf standardize exception tests - add one more 2018-06-18 19:46:42 +02:00
xmatthias 695beecf14 add test for get_markets 2018-06-18 19:36:36 +02:00
Samuel Husso cb015dec7b Merge pull request #927 from freqtrade/pyup-scheduled-update-2018-06-18
Scheduled daily dependency update on monday
2018-06-18 15:47:43 +03:00
pyup-bot 9bc8331667 Update ccxt from 1.14.202 to 1.14.211 2018-06-18 14:23:05 +02:00
xmatthias 520c7feeab Add test for fetch_tickers 2018-06-17 23:38:07 +02:00
xmatthias 1e3d722bc2 add test for get_trades 2018-06-17 23:38:07 +02:00
xmatthias c9f8dfc6c5 increase get_fee coverage 2018-06-17 23:38:07 +02:00
xmatthias d156de39f1 Increase test-coverage 2018-06-17 23:38:07 +02:00
xmatthias 2b099a89e4 fix styling issues 2018-06-17 23:38:07 +02:00
xmatthias 6e6ec969eb cleanup mockings 2018-06-17 23:38:07 +02:00
xmatthias e194af8d25 Streamline validate_pair patching 2018-06-17 23:38:07 +02:00
xmatthias ace5198475 fix optimize tests 2018-06-17 23:38:07 +02:00
xmatthias 52d36c33cf fix optimie test 2018-06-17 23:38:07 +02:00
xmatthias 251f7db3ca require exchange object to delete pairs 2018-06-17 23:38:07 +02:00
xmatthias c83e8b7cb5 fix rpc_test 2018-06-17 23:38:07 +02:00
xmatthias 64e09f74a1 fix rpc tests 2018-06-17 23:38:07 +02:00
xmatthias 63b568989a Fix rpc for exchange objectify 2018-06-17 23:38:07 +02:00
xmatthias 975b42caa3 fix tests for exchange objectify 2018-06-17 23:38:07 +02:00
xmatthias 75d02df60d add exchange to call get_singal 2018-06-17 23:38:07 +02:00
xmatthias 082b6077e9 Fix tests analyze 2018-06-17 23:38:07 +02:00
xmatthias e8ab76f55b fix small in tests 2018-06-17 23:38:07 +02:00
xmatthias 495f15f13c fix exchange tests 2018-06-17 23:38:07 +02:00
xmatthias 68f6423d39 fix most tests 2018-06-17 23:38:07 +02:00
xmatthias 67d345bc08 fix tests for objectify exchange 2018-06-17 23:38:07 +02:00
xmatthias a159db6863 get_exchange 2018-06-17 23:38:07 +02:00
xmatthias dea26fadfe move init_ccxt to class 2018-06-17 23:38:07 +02:00
xmatthias 21edcbdc27 Refactor exchange to class 2018-06-17 23:38:07 +02:00
Janne Sinivirta e3c91df081 Merge pull request #926 from freqtrade/pyup-scheduled-update-2018-06-17
Scheduled daily dependency update on sunday
2018-06-17 16:08:54 +03:00
Janne Sinivirta c608f1e21e Merge pull request #923 from freqtrade/fix_test_hyperopt
fix hyperopt test when no config.json exists
2018-06-17 16:07:57 +03:00
pyup-bot fef267a0dc Update ccxt from 1.14.201 to 1.14.202 2018-06-17 14:23:05 +02:00
Michael Egger 5ce2071279 Merge pull request #925 from freqtrade/increase_test_cov_configuration
increase test-coverate for configuration
2018-06-17 13:19:16 +02:00
xmatthias ad0549414b Revert "also unit tests now need config.json"
This reverts commit 7e2e7946c5.
2018-06-17 11:34:12 +02:00
Janne Sinivirta c6cc9ae29d Merge pull request #922 from freqtrade/fix_fiat_test
Fix fiat_convert missing mockups
2018-06-17 08:52:03 +03:00
Anton ae94ab17f4 Merge branch 'develop' into feature-unlimited-stake_amount 2018-06-17 02:23:40 +03:00
Anton eb909068c5 Add minimal pair stake amount check 2018-06-17 02:23:12 +03:00
xmatthias 90a7fb603d fix typo in coverage-omit 2018-06-16 21:28:41 +02:00
xmatthias 7cfd99d17f exclude __main__.py from coveralls -
if __name__ == '__main__' is close to untestable - and should do nothing
other than calling another function.
2018-06-16 21:00:45 +02:00
xmatthias 972736f0ab increase test-coverate for configureation 2018-06-16 20:55:35 +02:00
Matthias 934974a547 Merge pull request #924 from freqtrade/pyup-scheduled-update-2018-06-16
Scheduled daily dependency update on saturday
2018-06-16 16:14:34 +02:00
pyup-bot 17801871b1 Update ccxt from 1.14.198 to 1.14.201 2018-06-16 14:23:06 +02:00
xmatthias 7564f7e526 fix hyperopt test when no config.json exists 2018-06-16 13:49:03 +02:00
xmatthias fa00157d12 Fix fiat_convert missing mockups 2018-06-16 13:42:25 +02:00
Matthias a5511e2e30 Merge pull request #894 from freqtrade/feature/force_close_backtest
Display open trades after backtest period
2018-06-16 12:49:08 +02:00
Janne Sinivirta 0347ce21fd Merge pull request #920 from freqtrade/hyperopt-strip
Remove mongodb from Hyperopt
2018-06-16 10:33:44 +03:00
Janne Sinivirta 7e2e7946c5 also unit tests now need config.json 2018-06-16 09:09:28 +03:00
Janne Sinivirta 0c85febe76 remove all mongodb related code 2018-06-16 09:09:28 +03:00
Janne Sinivirta c1f8f641e6 remove use of hyperopt_conf.py 2018-06-16 09:09:28 +03:00
pyup-bot af16830a38 Update requests from 2.19.0 to 2.19.1 2018-06-16 09:09:28 +03:00
pyup-bot a8d25266f9 Update ccxt from 1.14.196 to 1.14.198 2018-06-16 09:09:28 +03:00
Matthias b78b9dccc8 Merge pull request #919 from freqtrade/pyup-scheduled-update-2018-06-15
Scheduled daily dependency update on friday
2018-06-15 14:51:54 +02:00
pyup-bot e8fd11d6ce Update requests from 2.19.0 to 2.19.1 2018-06-15 14:23:08 +02:00
pyup-bot 1e208e39b0 Update ccxt from 1.14.196 to 1.14.198 2018-06-15 14:23:07 +02:00
xmatthias 5c3e37412e update docs 2018-06-14 21:20:16 +02:00
Janne Sinivirta c731f7dd29 Merge pull request #917 from freqtrade/pyup-scheduled-update-2018-06-14
Scheduled daily dependency update on thursday
2018-06-14 15:42:52 +03:00
pyup-bot ea805a8fb7 Update ccxt from 1.14.186 to 1.14.196 2018-06-14 14:22:06 +02:00
xmatthias c0289ad844 use list comprehension to build list 2018-06-13 19:53:12 +02:00
xmatthias e600be4f56 Reduce force-sell verbosity 2018-06-13 19:44:00 +02:00
Matthias d7e7ef11f9 Merge pull request #913 from freqtrade/apply-qtpylib-updates
Apply qtpylib upstream changes
2018-06-13 19:34:02 +02:00
gcarq d684ff5715 drop zlma implementation 2018-06-13 16:20:13 +02:00
ran 6edb25f5c2 fixed heikenashi calculation 2018-06-13 16:17:42 +02:00
ran e6e5c5daf0 added zlma 2018-06-13 16:16:02 +02:00
ran 61f92b7460 bugfix 2018-06-13 16:13:36 +02:00
Michael Egger 2b74982a1d Merge pull request #877 from freqtrade/feature/improve-rpc
Simplify RPCManager and RPC module to implement other clients
2018-06-13 15:49:49 +02:00
gcarq 46080f5168 define _rpc_reload_conf as private method 2018-06-13 15:29:27 +02:00
Janne Sinivirta 1dcc2de776 Merge pull request #912 from freqtrade/pyup-scheduled-update-2018-06-13
Scheduled daily dependency update on wednesday
2018-06-13 15:44:00 +03:00
pyup-bot 875408215b Update numpy from 1.14.4 to 1.14.5 2018-06-13 14:22:11 +02:00
pyup-bot 038acd3f5e Update pandas from 0.23.0 to 0.23.1 2018-06-13 14:22:09 +02:00
pyup-bot f404e0f5b3 Update requests from 2.18.4 to 2.19.0 2018-06-13 14:22:08 +02:00
pyup-bot 92b0cbdc19 Update ccxt from 1.14.177 to 1.14.186 2018-06-13 14:22:07 +02:00
gcarq e14c9e2090 fix potential cleanup issue 2018-06-13 12:21:54 +02:00
gcarq 83eb7a0a9d adjust logging a bit and add some comments 2018-06-13 12:21:54 +02:00
gcarq 6c1bb7983b rpc: make freqtrade a private variable 2018-06-13 12:21:54 +02:00
gcarq 34e10a145c remove Telegram.is_enabled() because RPCManager manages lifecycles 2018-06-13 12:21:54 +02:00
gcarq 3787dad212 don't import python-telegram-bot at runtime if disabled in config 2018-06-13 12:21:54 +02:00
gcarq 4048859912 rpc: remove tuple return madness 2018-06-13 12:21:54 +02:00
gcarq cddb062db5 save rpc instances only in registered_modules, add some abstract methods 2018-06-13 12:21:54 +02:00
Samuel Husso 13ba68acc6 Merge pull request #908 from freqtrade/fix/plotprofit
fix default datadir not working in plot-script
2018-06-13 08:10:28 +03:00
xmatthias e22da45474 update documentation with forcesell at the end of the backtest period 2018-06-13 07:00:39 +02:00
xmatthias 6357812743 fix backtest report able 2018-06-13 06:57:49 +02:00
xmatthias 6e68c3b230 fix backtesting.md formatting 2018-06-13 06:52:17 +02:00
xmatthias 0f117d480e improve backtesting-tests
* assert length of result specifically
* add assert for "open_at_end"
2018-06-13 06:42:24 +02:00
xmatthias 8d8e6dcffc Add test for extracted backtest_results test 2018-06-13 06:31:42 +02:00
xmatthias e3ced7c15e extract export from backtest function 2018-06-12 22:29:30 +02:00
xmatthias 182f4c603b fix plot-script datadir not working 2018-06-12 21:43:14 +02:00
xmatthias 1f6b9c332b fix default datadir not working in plot-script 2018-06-12 21:38:14 +02:00
xmatthias bfde33c945 Use timestamp() instead of strftime
this will avoid a bug shifting epoch time by 1 hour:
https://stackoverflow.com/questions/11743019/convert-python-datetime-to-epoch-with-strftime
2018-06-12 21:12:55 +02:00
Matthias bd6ed3ada4 Merge pull request #906 from freqtrade/pyup-scheduled-update-2018-06-12
Scheduled daily dependency update on tuesday
2018-06-12 14:45:24 +02:00
pyup-bot aa6e276cf9 Update ccxt from 1.14.172 to 1.14.177 2018-06-12 14:22:06 +02:00
Gérald LONLAS 3694499a6a Merge pull request #905 from freqtrade/issue_template
update issue template to include ccxt version
2018-06-11 22:38:58 -07:00
xmatthias 06b71d713c update issue template to include ccxt version 2018-06-12 07:00:58 +02:00
Michael Egger 7141060a2d Merge pull request #903 from freqtrade/fix/downloadscript_noavailable_pair
fix downloadscript crash if a pair is not available
2018-06-12 02:56:19 +02:00
Michael Egger 59a4dffc56 Merge pull request #901 from freqtrade/fix/backtest_abort_no_data
Check if no backtest data is found and fail gracefully
2018-06-12 02:54:58 +02:00
Anton 708320318c Check minimal amount 2018-06-12 01:05:43 +03:00
xmatthias 40746c3fcb fix downloadscript crash if a pair is not available 2018-06-11 21:10:57 +02:00
xmatthias a0f735d4f2 reduce test-noise 2018-06-11 21:02:24 +02:00
xmatthias 335d1fbbbc Check if no backtest data is found and fail gracefully 2018-06-11 19:50:43 +02:00
Anton 90025d0ac4 Fix check 2018-06-11 16:38:10 +03:00
Anton ce663f6af5 Merge with develop 2018-06-11 16:25:05 +03:00
Anton 3676015184 Fix check 2018-06-11 16:21:57 +03:00
Samuel Husso 3aff67605e Merge pull request #900 from freqtrade/pyup-scheduled-update-2018-06-11
Scheduled daily dependency update on monday
2018-06-11 15:31:32 +03:00
Samuel Husso 7801688c6e Merge pull request #899 from freqtrade/precommithook
Add note about flake8 pre-commit hooks
2018-06-11 15:24:22 +03:00
pyup-bot 17f3b217de Update ccxt from 1.14.169 to 1.14.172 2018-06-11 14:22:07 +02:00
Janne Sinivirta d02af07d35 Add not about flake8 pre-commit hooks 2018-06-11 14:55:39 +03:00
Janne Sinivirta c46e50864b Merge pull request #886 from freqtrade/feature/reload-conf
Reload bot config without restarting
2018-06-11 10:47:00 +03:00
Michael Egger 6c361c190b Merge pull request #897 from freqtrade/fix_backtest_tests
fix backtest tests
2018-06-10 23:13:46 +02:00
xmatthias 12e455cbf5 add buy/sell index to backtest result 2018-06-10 20:52:42 +02:00
xmatthias a9f3744f1b fix backtest test 2018-06-10 19:46:52 +02:00
Janne Sinivirta 53e1b8c0d5 Merge pull request #895 from freqtrade/pyup-scheduled-update-2018-06-10
Scheduled daily dependency update on sunday
2018-06-10 16:39:07 +03:00
pyup-bot 2ba363684d Update ccxt from 1.14.165 to 1.14.169 2018-06-10 14:22:07 +02:00
xmatthias 9cc087c788 update hyperopt tests to support new structure 2018-06-10 13:56:23 +02:00
xmatthias 4710210cff fix hyperopt to use new backtesting result tuple 2018-06-10 13:56:10 +02:00
xmatthias 27ee8f7360 make flake happy 2018-06-10 13:55:48 +02:00
xmatthias 1cd7ac55a8 Added "left open trades" report 2018-06-10 13:45:16 +02:00
xmatthias b81588307f Add "open_at_end" parameter 2018-06-10 13:37:53 +02:00
xmatthias 31025216f9 fix type of open/close timestmap 2018-06-10 13:32:07 +02:00
xmatthias aff1ede46b Fix last backtesting test 2018-06-10 13:25:52 +02:00
xmatthias 322a528c12 fix bug with backtestResult 2018-06-10 13:25:16 +02:00
xmatthias 17c0ceec04 adjust tests for backtestresult type 2018-06-10 13:22:24 +02:00
xmatthias c9476fade8 adjust tests for forcesell 2018-06-10 13:20:41 +02:00
xmatthias 7b5a2946e5 adjust for forcesell backtesting 2018-06-10 13:19:32 +02:00
xmatthias 9c57d3aa8b add BacktestresultTuple 2018-06-10 13:15:46 +02:00
xmatthias c1b2e06eda simplify return from _get_sell_trade_entry 2018-06-10 09:07:04 +02:00
xmatthias 3094acc7fb update comment 2018-06-10 08:58:28 +02:00
xmatthias 24a875ed46 remove experimental parameters - they are read by analyze.py anyway 2018-06-09 21:44:57 +02:00
xmatthias 5623ea3ac6 Add forcesell at end of backtest period 2018-06-09 21:44:20 +02:00
Matthias 655155bbab Merge pull request #890 from freqtrade/coveralls-single-execution
avoid running coveralls 4 times
2018-06-09 17:59:04 +02:00
Janne Sinivirta 28e8840456 avoid running coveralls 4 times 2018-06-09 18:52:57 +03:00
Janne Sinivirta 8c73fd6e59 Merge pull request #887 from freqtrade/pyup-scheduled-update-2018-06-09
Scheduled daily dependency update on saturday
2018-06-09 16:02:48 +03:00
pyup-bot eb58e7cb82 Update ccxt from 1.14.160 to 1.14.165 2018-06-09 14:22:07 +02:00
Janne Sinivirta 8db3dfa8c6 Merge pull request #880 from freqtrade/fix/636
Fixes issue 636
2018-06-09 08:59:12 +03:00
Janne Sinivirta efd69b2cd5 Merge pull request #883 from freqtrade/fstrings-in-use
fstrings in use
2018-06-09 08:53:54 +03:00
Samuel Husso 38c32f0e10 flake8 fix 2018-06-09 08:40:32 +03:00
Samuel Husso 62b4efb881 freqtradebot: fstrings in use 2018-06-09 08:27:39 +03:00
Samuel Husso b5c200f6c4 Fiat_converter: fstrings into use 2018-06-09 08:27:39 +03:00
Samuel Husso 18e3090379 Exchange: f-strings into use 2018-06-09 08:27:39 +03:00
Samuel Husso 1e1be6bc3f arguments,configuration: fstring in use 2018-06-09 08:24:45 +03:00
Gerald Lonlas f0456bb802 Update the README structure 2018-06-08 20:15:52 -07:00
gcarq 61da7f63b2 Merge branch 'develop' of freqtrade into feature/reload-conf 2018-06-09 04:30:23 +02:00
gcarq 0b5d21f32a implement bot reconfiguration and expose it to telegram 2018-06-09 04:29:48 +02:00
gcarq 74db82d759 main: don't touch freqbot state in cleanup()
cleanup() should be only called after the main loop has been exited.
At that point the state shouldn't be modified.
2018-06-09 01:19:42 +02:00
gcarq 5851cc70a7 Merge branch 'develop' of freqtrade into fix/636 2018-06-09 00:37:46 +02:00
Michael Egger faeda0e70c Merge pull request #878 from freqtrade/fix_timeframe_issue
fix windows-specific init issue with named tuple
2018-06-08 22:44:06 +02:00
Michael Egger 73c5f0ec90 Merge pull request #872 from freqtrade/feature/improve-error-handling
improve error handling
2018-06-08 22:43:37 +02:00
Michael Egger 66f6e71e7e Merge pull request #827 from freqtrade/fix/pylint_and_coverage
Increase code coverage and improve Pylint
2018-06-08 22:32:04 +02:00
xmatthias cc4b2eef13 mypy - ignore tests folder 2018-06-08 19:58:01 +02:00
xmatthias 8effc5f929 fix windows-specific init issue with named tuple 2018-06-08 19:46:07 +02:00
Samuel Husso 5f93c5e789 Merge pull request #876 from freqtrade/pyup-scheduled-update-2018-06-08
Scheduled daily dependency update on friday
2018-06-08 18:14:43 +03:00
Samuel Husso 980172a55a Merge pull request #865 from freqtrade/partial_candle_removal
Partial candle removal
2018-06-08 18:10:21 +03:00
pyup-bot 760e878dd8 Update ccxt from 1.14.155 to 1.14.160 2018-06-08 14:22:07 +02:00
Samuel Husso 4dbc7abd0f Merge pull request #875 from freqtrade/feat/windows_doc
update windows install documentation
2018-06-08 12:58:26 +03:00
Janne Sinivirta 867faf1c30 Merge pull request #873 from freqtrade/feature/strat_repo_ref
add reference to strategy repository
2018-06-08 12:53:40 +03:00
Matthias 43d19790ae update windows install documentation 2018-06-08 11:23:00 +02:00
Matthias 0bc86e72b3 Add slack reference, fix spelling 2018-06-08 10:57:52 +02:00
Gerald Lonlas 5ca84acb6d Fix Flake8 2018-06-07 23:12:03 -07:00
Samuel Husso c4af66e312 Merge pull request #874 from freqtrade/local-talib
store ta-lib locally in a zip for Travis
2018-06-08 08:51:39 +03:00
Janne Sinivirta c37792dbc4 store ta-lib locally in a zip for Travis 2018-06-08 08:15:04 +03:00
Gerald Lonlas 50852136ef Increase FreqtradeBot.get_real_amount() coverage 2018-06-07 22:13:50 -07:00
Gerald Lonlas 20082f52a2 Increase code coverage for FreqtradeBot.process_maybe_execute_sell() 2018-06-07 22:13:50 -07:00
Gerald Lonlas 5ec3eb76eb Cover a edge case of CryptoToFiatConverter::_find_price() 2018-06-07 22:13:50 -07:00
Gerald Lonlas dfbc94c05b Add missing test for CryptoToFiatConverter::convert_amount() 2018-06-07 22:13:50 -07:00
Gerald Lonlas 81ce7d720d Add missing unit test for Arguments::testdata_dl_options() 2018-06-07 22:13:50 -07:00
Gerald Lonlas 1db0f2bd55 Increase pylint to 10 for freqtrade/arguments.py 2018-06-07 22:13:50 -07:00
xmatthias 9292eb664a add reference to strategy repository
fix markdown to have markdownlint not complain that much
2018-06-08 06:44:59 +02:00
Matthias 8f91eeb195 Merge pull request #870 from freqtrade/feature/increase-main-coverage
add and fix tests for main.py
2018-06-08 06:35:36 +02:00
gcarq 10e12ec1b9 fix flake8 warning 2018-06-08 02:37:12 +02:00
gcarq 61b2373dd1 flush db connection after forcesell 2018-06-08 02:35:10 +02:00
gcarq 7f881cce85 add additional None check for trade.open_order_id 2018-06-08 02:34:44 +02:00
gcarq bea9a3304e use correct return code on error 2018-06-08 02:01:46 +02:00
gcarq 95d6c9c678 adapt tests 2018-06-08 02:01:38 +02:00
gcarq a2a1a517da fix flake8 warning 2018-06-08 02:01:18 +02:00
gcarq 27f83b511f raise OperationalException if config is missing 2018-06-08 02:00:42 +02:00
Anton b1b87731b1 Support case when _get_trade_stake_amount returns None 2018-06-08 00:54:46 +03:00
Anton b4138f29c8 Merge with develop 2018-06-08 00:29:44 +03:00
gcarq dd3a53fb5f fix tests for main.py 2018-06-07 22:28:21 +02:00
Matthias d23bcc435a Merge pull request #864 from freqtrade/feature/overhaul-db-handling
Allow custom sqlite database path
2018-06-07 22:18:10 +02:00
Michael Egger 45eb1b4f0a Merge pull request #869 from freqtrade/feature/profit_rpc
fix /profit percentage calculation
2018-06-07 21:41:32 +02:00
gcarq d41f71bc34 handle sqlalchemy NoSuchModuleError 2018-06-07 21:35:57 +02:00
xmatthias f5fe9a4b1c fix rpc tests (add a test with multiple trades
without this, sum/percentage cannot be properly tested.
2018-06-07 20:52:03 +02:00
xmatthias 0e699b87af don't sum percentage, but use mean instead (aligned to backtesting) 2018-06-07 20:43:28 +02:00
gcarq 3f5efef6e5 tests: add proper asserts 2018-06-07 20:41:52 +02:00
gcarq d4f8704a4c arguments: implement tests for db_url 2018-06-07 20:30:13 +02:00
gcarq 526cb1ea20 fix db-url handling if passed via CLI args 2018-06-07 20:15:31 +02:00
Janne Sinivirta f5b47fbd86 flake8 fixes 2018-06-07 20:23:09 +03:00
Janne Sinivirta 3cee04fb8c bot should not repaint: do not include last partial candle in analysis 2018-06-07 20:23:09 +03:00
gcarq ac602ed5a9 persistence: adapt checks to detect in-memory db 2018-06-07 19:10:26 +02:00
Samuel Husso ad510b8b5f Merge pull request #855 from freqtrade/fix-look-ahead
Avoid look-ahead in backtesting
2018-06-07 20:00:46 +03:00
Samuel Husso 3436af3931 Merge pull request #868 from creslinux/patch-1
plotting.md update.
2018-06-07 19:32:12 +03:00
gcarq 01675f50bf adapt scripts/plot_dataframe to use freqtrade db_url 2018-06-07 18:06:27 +02:00
gcarq 17742df591 Merge branch 'develop' of freqtrade into feature/overhaul-db-handling 2018-06-07 17:33:37 +02:00
gcarq 5b1ff6675f define constants.DEFAULT_DB_DRYRUN_URL and fix StaticPool conditions 2018-06-07 17:29:43 +02:00
creslin 7bcac064c0 Update plotting.md
typo fixed.
2018-06-07 15:18:19 +00:00
Michael Egger 867145cd09 Merge pull request #859 from freqtrade/readd_ticker_caching
Re-add ticker caching for rpc operations
2018-06-07 17:15:59 +02:00
creslin 959a03a6b0 plotting.md update.
include an example or plotting a strategy buy/sell output.
2018-06-07 15:13:55 +00:00
Janne Sinivirta b4ae5a36a8 use .copy() to avoid Pandas mistake. drop first row because of shifting 2018-06-07 17:29:40 +03:00
Janne Sinivirta 7f8e0ba25f use buy/sell signal from previous candle, not current to avoid seeing to the future 2018-06-07 17:28:40 +03:00
Michael Egger c75b70463b Merge pull request #852 from freqtrade/timeframe_class
Refactor Timeframe fake-type into NamedTuple
2018-06-07 16:19:44 +02:00
Janne Sinivirta f9788afbfb Merge pull request #867 from freqtrade/pyup-scheduled-update-2018-06-07
Scheduled daily dependency update on thursday
2018-06-07 17:04:39 +03:00
pyup-bot 7b0a5644a3 Update pytest from 3.6.0 to 3.6.1 2018-06-07 14:22:10 +02:00
pyup-bot 34b5203760 Update numpy from 1.14.3 to 1.14.4 2018-06-07 14:22:08 +02:00
pyup-bot a2fd70417c Update ccxt from 1.14.121 to 1.14.155 2018-06-07 14:22:07 +02:00
gcarq c3d0980763 test_persistence: fix reference before assignment 2018-06-07 06:06:21 +02:00
gcarq 4ee5271de7 fix failing dynamic-whitelist test 2018-06-07 05:50:07 +02:00
gcarq f6ef466876 adapt docs 2018-06-07 05:47:14 +02:00
gcarq 00b646158c update docs 2018-06-07 05:36:39 +02:00
gcarq c8a43bad67 add db_url to full example config 2018-06-07 05:28:05 +02:00
gcarq a29ac44d64 adapt tests 2018-06-07 05:27:55 +02:00
gcarq e2aa78c11b remove obsolete param 2018-06-07 05:27:27 +02:00
gcarq 58a6f21705 remove dry_run_db and replace it with db_url in config 2018-06-07 05:26:39 +02:00
gcarq 8583e89550 persistence: simplify init and pass db_url via config dict 2018-06-07 05:25:53 +02:00
Gérald LONLAS e8ab754646 Merge pull request #863 from freqtrade/fix/pyup-pin-networkx
exclude networkx from pyup
2018-06-06 18:43:54 -07:00
Michael Egger 5c1ee52815 Merge pull request #861 from freqtrade/pyup-config
Config file for pyup.io
2018-06-07 01:19:21 +02:00
gcarq 02671a7e10 pin networkx with pyup ignore filter 2018-06-07 01:12:46 +02:00
pyup-bot 2ba5e2053a create pyup.io config file 2018-06-07 00:55:09 +02:00
xmatthias 7714490530 Test keyerror exception 2018-06-06 21:24:57 +02:00
xmatthias 4a17671f45 improve log message 2018-06-06 20:30:42 +02:00
xmatthias a901f21bcd test ticker caching 2018-06-06 20:24:47 +02:00
xmatthias e690003621 reinstate caching for get_ticker 2018-06-06 20:18:16 +02:00
Matthias fb49d706d0 Merge pull request #851 from jblestang/update_doc_process_throttle
Update doc process throttle
2018-06-06 00:11:44 +02:00
xmatthias cac6e0d715 Add docstring to TimeRange class 2018-06-06 00:10:18 +02:00
xmatthias f37c5b70ba Fix tests - read optional argument 2018-06-05 23:53:49 +02:00
xmatthias 270ccbb0da fix args test 2018-06-05 23:41:50 +02:00
xmatthias 7a34578b4d refactor timerange to named tuple 2018-06-05 23:34:26 +02:00
Anton 12d8a8b1a3 Fix review comments 2018-06-06 00:14:28 +03:00
Janne Sinivirta 7d3eefa97a Merge pull request #838 from freqtrade/fix/plot-scripts
Fix/Improve plot scripts
2018-06-05 15:32:04 +03:00
Jean-Baptiste LE STANG 608fc170d9 fix doc 2018-06-05 13:51:30 +02:00
Jean-Baptiste LE STANG 456d0a050f update doc for process_throttle_secs 2018-06-05 13:49:59 +02:00
Janne Sinivirta 399dd7df95 Merge pull request #849 from freqtrade/readme/fix-links
Docs: point links to freqtrade org
2018-06-05 13:45:54 +03:00
Samuel Husso 7cc36eee0f Docs: point links to freqtrade org 2018-06-05 13:27:24 +03:00
Gerald Lonlas 5024cd52af Update docstring for generate_graph() 2018-06-04 23:49:16 -07:00
Gerald Lonlas c29c13dfd7 Fix a typo in Arguments() comment 2018-06-04 22:42:24 -07:00
Gerald Lonlas 947462e134 Add back 'import os' in Arguments() 2018-06-04 21:29:53 -07:00
Gerald Lonlas 3778bcda24 Ok! you won Flake8 2018-06-04 21:18:03 -07:00
Gerald Lonlas 1b071b1f4a Add example on how to start the script 2018-06-04 21:18:03 -07:00
Gerald Lonlas 8edcef6d32 Add two params to select what indicators to display 2018-06-04 21:18:03 -07:00
Gerald Lonlas 662436acd2 Fix typo in Argument() 2018-06-04 21:18:03 -07:00
Gerald Lonlas e16fb45d84 Fix typo, remove Bittrex mention 2018-06-04 21:17:20 -07:00
Gerald Lonlas 1c75bfdddd Add more indicators 2018-06-04 21:17:20 -07:00
Gerald Lonlas 64504e6777 Add support of --refresh-pairs-cached param 2018-06-04 21:17:20 -07:00
Gerald Lonlas af76d5f0e0 Breakdown the script in functions the improve maintainability 2018-06-04 21:17:20 -07:00
Gerald Lonlas 5683f9e10e Remove hardcoded backtest-result.json in Plot scripts 2018-06-04 21:17:20 -07:00
Matthias 15fb81da92 Merge pull request #844 from creslinux/Constants_usdt
To be able to start with USDT in fiat_display_currency in config.json
2018-06-04 21:56:34 +02:00
creslin e52ec14588 Update configuration.md
typo, form to from.
2018-06-04 22:19:25 +03:00
creslinux b13658b319 Updated configuration doc with new fiat values accepted. 2018-06-04 22:17:10 +03:00
creslinux a44978a068 Per steer from project core member, add other valid coinmarketcap
listed crypto base currencies that are valid during conversion lookup

Here is the test of USDT working:
https://api.coinmarketcap.com/v2/ticker/1027/?convert=USDT&limit=10

CMK page lists: "BTC", "ETH" "XRP", "LTC", and "BCH" as valid.
2018-06-04 21:48:15 +03:00
Matthias bee2541bd8 Merge pull request #843 from freqtrade/more_timeframes
Add support for more timeframes
2018-06-04 16:23:19 +02:00
creslinux 7c8bf95f8f To be able to start bot with USDT in fiat_display_currency in config.json
There are use case that build the base pair to consider price of whitelist pairs.
On Binance this is USDT not USD.
2018-06-04 16:45:47 +03:00
Janne Sinivirta 7df77b1b28 match timeframes to arguments 2018-06-04 16:35:34 +03:00
Matthias b995e04daa Merge pull request #841 from freqtrade/choose_tickers_to_download
Choose tickers to download
2018-06-04 14:13:35 +02:00
Janne Sinivirta 0f3dc821f2 add missing timeframes to allowed values 2018-06-04 15:08:45 +03:00
Janne Sinivirta 5ff405b0b0 allow defining of timeframes to download 2018-06-04 15:08:45 +03:00
Samuel Husso 86ae9d25f0 Merge pull request #840 from freqtrade/improve_downloader
Improve ticker downloader
2018-06-04 14:51:02 +03:00
Janne Sinivirta 3321e4cafd travis should run hyperopt and backtesting using tests/testdata tickers 2018-06-04 14:27:42 +03:00
Janne Sinivirta 639b6bc4f6 set and create default datadir based on used exchange 2018-06-04 14:27:42 +03:00
Janne Sinivirta af1ba1e191 split ugly ternary to regular if 2018-06-04 12:58:35 +03:00
Janne Sinivirta 5c7899ae98 flake8 fix 2018-06-04 12:45:23 +03:00
Janne Sinivirta d4b431a335 update documentation about download_backtesting_data.py script 2018-06-04 12:37:06 +03:00
Janne Sinivirta 6891054b84 use folder user_data/data/exchangename by default and pick pairs.json from that folder by default 2018-06-04 12:37:06 +03:00
Janne Sinivirta e10279b7b4 show default exchange in download_backtest_data.py 2018-06-04 11:50:33 +03:00
Janne Sinivirta a0c79bd727 make --pairs-file required 2018-06-04 11:47:27 +03:00
Janne Sinivirta 4b8f382cfd Merge pull request #839 from freqtrade/fix/incorrect_folder_name_userdata
Fix folder names in custom datadir documentation
2018-06-04 11:24:09 +03:00
Janne Sinivirta eeda93a359 Fix folder names in custom datadir documentation 2018-06-04 10:04:26 +03:00
Gérald LONLAS 7b79ca3e8f Merge pull request #837 from xmatthias/fix_doc_links
Fix links to point to new repository in owner github account
2018-06-03 18:59:57 -07:00
Anton 3030bf9778 Fix types 2018-06-04 01:52:54 +03:00
Anton 87f750da35 Merge with develop 2018-06-04 01:50:10 +03:00
Anton daa9c0c026 Fix review comments 2018-06-04 01:48:26 +03:00
xmatthias 5ef2654eb4 replace references to old url
replace garq with freqtrade
2018-06-03 23:07:00 +02:00
xmatthias 26120ff675 remove unnecessary .gitkeep 2018-06-03 23:06:37 +02:00
Gérald LONLAS e453dab4a3 Merge pull request #831 from xmatthias/backtest_export_filename
allow export of backtesting-results to different files
2018-06-03 13:12:38 -07:00
xmatthias 482d063638 update documentation for --export-filename 2018-06-03 19:41:34 +02:00
Janne Sinivirta 2f3b0cd422 Merge pull request #835 from gcarq/pyup-update-ccxt-1.14.120-to-1.14.121
Update ccxt to 1.14.121
2018-06-03 20:40:22 +03:00
xmatthias e3227a741c add --export-filename for backtesting 2018-06-03 19:36:53 +02:00
pyup-bot 4eb8295955 Update ccxt from 1.14.120 to 1.14.121 2018-06-03 19:27:08 +02:00
Samuel Husso bdb25bbcbc Merge pull request #834 from gcarq/feature/__main__
Add __main__.py to improve how to launch the bot
2018-06-03 19:28:23 +03:00
Gerald Lonlas 43696eff5c Add __main__.py to improve how to launch the bot 2018-06-03 08:57:13 -07:00
Michael Egger c6b93f8fe5 Merge pull request #833 from gcarq/fix/backtesting_doc
Update Backtesting/Hyperopt usage documentation
2018-06-03 17:43:41 +02:00
Gerald Lonlas d3d62e90d3 Update Backtesting/Hyperopt usage documentation 2018-06-03 08:36:01 -07:00
Janne Sinivirta 20815771ab Merge pull request #817 from gcarq/feature/gdax
Enable Backtesting with GDAX and allow trading with EUR/USD
2018-06-03 17:49:20 +03:00
Janne Sinivirta b6754601ef Merge pull request #832 from xmatthias/contrib_document
update contributing document to include mypy
2018-06-03 17:43:59 +03:00
xmatthias 0f352a4b5c update contributing document to include mypy 2018-06-03 15:14:51 +02:00
Samuel Husso 7d6b11cb10 Merge pull request #830 from xmatthias/refactor_fiat_list
Refactor fiat-list to constants
2018-06-03 15:57:23 +03:00
xmatthias 3a158faa30 Refactor fiat-list to constants 2018-06-03 13:47:36 +02:00
Matthias fff7ec1dab Merge pull request #808 from xmatthias/mypy_typecheck
add mypy typechecking
2018-06-03 10:43:55 +02:00
xmatthias 50fc5f91ca Merge branch 'develop' into mypy_typecheck 2018-06-03 10:35:56 +02:00
Samuel Husso ec7c11513e Merge pull request #829 from gcarq/pyup-update-ccxt-1.14.119-to-1.14.120
Update ccxt to 1.14.120
2018-06-03 11:31:50 +03:00
pyup-bot cfb06ceb58 Update ccxt from 1.14.119 to 1.14.120 2018-06-03 10:12:07 +02:00
Gerald Lonlas e8a59f4c20 Add a test to check the behavior when converting two FIAT 2018-06-03 00:13:48 -07:00
Gerald Lonlas 638d98735f Allow fiat_convert to use same symbol for Crypto and FIAT 2018-06-03 00:13:48 -07:00
Gerald Lonlas c9e49ed7b4 Sort ticker_history
CCXT does not sort the ticker history from exchanges.
Bittrex and Binance are sorted ASC (oldest first, newest last) when
GDAX is sorted DESC (newest first, oldest last).

Because of that the get_ticker_history() fall in a very long loop
when the tickers are sorted DESC. Means it downloads more than
needed.

This commit enable exhanges like GDAX and unify the ticker_history
list across all exchanges.
2018-06-03 00:13:48 -07:00
Gerald Lonlas acbfe91f13 Allow EUR / USD as stake_currency
It will enable to trade with FIAT on exhanges like GDAX or Kraken.
2018-06-03 00:13:48 -07:00
Janne Sinivirta 7edafbb772 Merge pull request #823 from creslinux/timerange_unixtime_argument
Timerange unixtime argument
2018-06-03 07:22:41 +03:00
Janne Sinivirta a657e3d24a Merge pull request #826 from gcarq/fix/hyperopt-stake_currency
Fix stake_currency returned by Hyperopt  …
2018-06-03 07:19:24 +03:00
Janne Sinivirta 2cd8782a88 Merge pull request #825 from gcarq/fix/hyperopt-in-progress
Fix the in-progress dot that does not show up during a Hyperopt run
2018-06-03 07:16:39 +03:00
Gerald Lonlas fe8ff1b929 Fix stake_currency return by Hyperopt
Hyperopt had BTC hard coded in the result. This commit  will display
the real stake_currency used.

If you used `"stake_currency": "USDT",` in your config file.
Before this commit you saw a message like:
"2 trades. Avg profit  0.13%. Total profit  0.00002651 BTC (0.0027Σ%). Avg duration 142.5 mins."

Now with the commit, we fix the wrong BTC currency:
"2 trades. Avg profit  0.13%. Total profit  0.00002651 USDT (0.0027Σ%). Avg duration 142.5 mins."
2018-06-02 14:07:31 -07:00
Gerald Lonlas 127cf5d619 Backtesting: Add the Interval required when data is missing
Change the message:
"No data for pair ETH/BTC, use --refresh-pairs-cached to download the data"
for:
"No data for pair: "ETH/BTC", Interval: 5m. Use --refresh-pairs-cached to download the data"

The message structure is unified with the download message:
"Download the pair: "ETH/BTC", Interval: 5m"
2018-06-02 13:55:05 -07:00
Gérald LONLAS 5e99df1759 Merge pull request #824 from xmatthias/rymdluo-patch-1
Make backtesting report markdown shareable (resubmit)
2018-06-02 13:05:11 -07:00
creslinux 94e586c049 Added unit test to check posix time arguments passed to timerange
Here is the pass report:
freqtrade_new creslin$ pytest freqtrade/tests/test_arguments.py
==================================================================== test session starts =====================================================================
platform darwin -- Python 3.6.5, pytest-3.6.0, py-1.5.3, pluggy-0.6.0
rootdir: /Users/creslin/PycharmProjects/freqtrade_new, inifile:
plugins: mock-1.10.0, cov-2.5.1
collected 19 items

freqtrade/tests/test_arguments.py ...................                                                                                                  [100%]

================================================================= 19 passed in 2.37 seconds ==================================================================
2018-06-02 22:46:54 +03:00
Gerald Lonlas dc65753a64 Fix the in-progress dot that does not show up during a Hyperopt run 2018-06-02 12:35:07 -07:00
creslin 43ba02afc6 Per feed back, kept the stype as date.
Use a tuple to keep as epoch int or process via arrow to timestamp.

I'll look at the test file also.
2018-06-02 21:59:18 +03:00
xmatthias 9537f17dd4 Fix test 2018-06-02 20:06:29 +02:00
Raymond Luo 2791d543ea Make backtesting report markdown shareable
Small tweak to make the backtesting report markdown ready and much easier to share reports on many markdown publishing tools and editors that already support Markdown Extra with just a copy and paste

Example:
![Example](https://i.imgur.com/HXlNkfm.png)
2018-06-02 19:52:16 +02:00
creslin 9dbe5fdb85 Update back testing document to include example using Posix timestamps
as timerange

e.g
--timerange=1527595200-1527618600
2018-06-02 19:49:23 +03:00
creslin 6ca375a397 Extend timerange to accept unix timestamps.
This gives greater granularity over backtest, parsing tickerfiles.

Example runs using date and unix time below.

/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/bin/python3.6 /Users/creslin/PycharmProjects/freqtrade/scripts/report_correlation.py --timerange=20180528-20180529
2018-06-02 18:44:58,829 - freqtrade.configuration - INFO - Log level set to INFO
2018-06-02 18:44:58,830 - freqtrade.configuration - INFO - Using max_open_trades: 200 ...
2018-06-02 18:44:58,831 - freqtrade.configuration - INFO - Parameter --timerange detected: 20180528-20180529 ...
2018-06-02 18:44:58,831 - freqtrade.configuration - INFO - Parameter --datadir detected: freqtrade/tests/testdata ...
   BasePair      Pair  Correlation  BTC % Change  Pair % USD Ch  Pair % BTC Ch  Gain % on BTC        Start         Stop  BTC Volume
1  BTC_USDT   ETC_USD        0.965        -2.942         -4.070         -1.163      -1.128585  05-28 00:00  05-29 00:00      335.19
0  BTC_USDT   SNT_USD        0.943        -2.942         -5.857         -3.004      -2.915585  05-28 00:00  05-29 00:00      496.01
3  BTC_USDT  DASH_USD        0.902        -2.942         -9.034         -6.277      -6.092432  05-28 00:00  05-29 00:00      751.41
2  BTC_USDT   MTH_USD        0.954        -2.942         -9.290         -6.541      -6.348708  05-28 00:00  05-29 00:00       23.00
4  BTC_USDT   TRX_USD        0.951        -2.942        -13.647        -11.029     -10.704957  05-28 00:00  05-29 00:00    14544.57

Process finished with exit code 0

/usr/local/Cellar/python3/3.6.2/Frameworks/Python.framework/Versions/3.6/bin/python3.6 /Users/creslin/PycharmProjects/freqtrade/scripts/report_correlation.py --timerange=1527595200-1527618600
2018-06-02 18:47:40,382 - freqtrade.configuration - INFO - Log level set to INFO
2018-06-02 18:47:40,382 - freqtrade.configuration - INFO - Using max_open_trades: 200 ...
2018-06-02 18:47:40,383 - freqtrade.configuration - INFO - Parameter --timerange detected: 1527595200-1527618600 ...
2018-06-02 18:47:40,383 - freqtrade.configuration - INFO - Parameter --datadir detected: freqtrade/tests/testdata ...
   BasePair      Pair  Correlation  BTC % Change  Pair % USD Ch  Pair % BTC Ch  Gain % on BTC        Start         Stop  BTC Volume
0  BTC_USDT   SNT_USD        0.680           NaN            NaN            NaN            NaN  05-29 12:00  05-29 18:30    68866.30
1  BTC_USDT   ETC_USD        0.857           NaN            NaN            NaN            NaN  05-29 12:00  05-29 18:30   227514.17
2  BTC_USDT   MTH_USD        0.790           NaN            NaN            NaN            NaN  05-29 12:00  05-29 18:30    12103.96
3  BTC_USDT  DASH_USD        0.862           NaN            NaN            NaN            NaN  05-29 12:00  05-29 18:30    72982.78
4  BTC_USDT   TRX_USD        0.178           NaN            NaN            NaN            NaN  05-29 12:00  05-29 18:30  1258316.95

Process finished with exit code 0
2018-06-02 19:45:08 +03:00
Matthias 81bb128cf7 Merge pull request #822 from gcarq/fix/misleading_log
change misleading logging for datadir
2018-06-02 14:50:27 +02:00
xmatthias a8bf5092e8 add ignore explanation 2018-06-02 14:18:57 +02:00
xmatthias f88729f0e8 add ignore comment 2018-06-02 14:14:28 +02:00
xmatthias 3447e4bb97 comment on ignore hint 2018-06-02 14:13:17 +02:00
xmatthias 884395415f remove type:ignore 2018-06-02 14:10:15 +02:00
xmatthias 0007002c80 fix test failure 2018-06-02 14:07:54 +02:00
xmatthias 0a595190a3 fix last typechecks 2018-06-02 13:59:35 +02:00
xmatthias 32300f6d5f don't initialize with None where it's not necessary 2018-06-02 13:55:06 +02:00
xmatthias d9e951447f remove _init function in backtesting (and according test) 2018-06-02 13:54:22 +02:00
xmatthias 6fc21e30e5 remove unused import 2018-06-02 13:52:55 +02:00
xmatthias 6106822d10 typing 2018-06-02 13:44:41 +02:00
xmatthias 4a322abd4d Typecheck improvements 2018-06-02 13:44:05 +02:00
Janne Sinivirta 52309cc292 Merge pull request #819 from gcarq/pyup-update-ccxt-1.14.96-to-1.14.119
Update ccxt to 1.14.119
2018-06-02 11:57:58 +03:00
Janne Sinivirta b5c41ca0fc Merge pull request #820 from gcarq/fix/backtesting_hint
Fix wrong hint '--update-pairs-cached' from Backtesting/Hyperopt
2018-06-02 11:39:09 +03:00
Janne Sinivirta a82a31341b change misleading logging for datadir 2018-06-02 11:32:05 +03:00
Gérald LONLAS 0980e7e82d Merge pull request #766 from pan-long/forcesell-amount
Sell filled amount or an open limit buy order in forcesell.
2018-06-01 19:51:38 -07:00
Gérald LONLAS 41efe99770 Merge pull request #786 from gcarq/fix/setup_script
Update setup.sh
2018-06-01 19:48:29 -07:00
Gerald Lonlas 792dd556a1 Fix wrong hint '--update-pairs-cached' from Backtesting/Hyperopt 2018-06-01 19:46:53 -07:00
pyup-bot b731a65c75 Update ccxt from 1.14.96 to 1.14.119 2018-06-02 04:27:04 +02:00
xmatthias e28973c50a fix flake8 2018-05-31 22:17:46 +02:00
xmatthias 633620a5e9 exclude .mypy_cache 2018-05-31 22:15:18 +02:00
xmatthias 41a47df93f setup travis to check mypy 2018-05-31 22:09:31 +02:00
xmatthias 3fb1dd02f1 add typehints and type: ignores 2018-05-31 22:00:46 +02:00
xmatthias cf34b84cf1 add attributes with typehints 2018-05-31 21:59:22 +02:00
xmatthias f4f821e88e add typehints 2018-05-31 21:44:18 +02:00
xmatthias c0cef7250d typing - avoid variable reuse with differen ttype 2018-05-31 21:22:46 +02:00
xmatthias 2976a50c58 fix typing 2018-05-31 21:10:15 +02:00
xmatthias 69006b8fe8 flake8 2018-05-31 21:08:26 +02:00
xmatthias 4eb55acdbc fix typing 2018-05-31 21:04:10 +02:00
xmatthias 1352f135d0 typing 2018-05-31 20:55:45 +02:00
xmatthias 0d251cbfdd rpc type hints 2018-05-31 20:55:26 +02:00
xmatthias 4733aad7ff mypy_typing 2018-05-31 20:54:37 +02:00
xmatthias 48516e6e1e Add typehint 2018-05-31 20:41:05 +02:00
xmatthias 45909af7e0 type anotation fixes 2018-05-30 22:38:09 +02:00
xmatthias 88755fcded fix typing 2018-05-30 22:09:20 +02:00
xmatthias 0d6dffdc7e fix typehinting 2018-05-30 22:09:03 +02:00
xmatthias 9aa468adda fix for typehint 2018-05-30 22:01:29 +02:00
Janne Sinivirta 52386d8153 Merge pull request #793 from gcarq/pyup-update-ccxt-1.14.73-to-1.14.96
Update ccxt to 1.14.96
2018-05-30 21:40:32 +03:00
pyup-bot b7e0466d7c Update ccxt from 1.14.73 to 1.14.96 2018-05-30 18:42:00 +02:00
Samuel Husso f91de3c10e Merge pull request #788 from gcarq/fix/doc_configuration
Update Readme and documentation
2018-05-30 08:53:57 +03:00
Gerald Lonlas 4329c15a9b Doc: Add Buzz/trendy word 2018-05-29 22:38:48 -07:00
Gerald Lonlas 963d2a8368 Doc: update bot usage 2018-05-29 22:24:13 -07:00
Gerald Lonlas d9eddfb1ee Doc: Update the exchanges supported 2018-05-29 22:21:29 -07:00
Gerald Lonlas f59f534c64 Setup.sh: fix Python3.6 when broken on macOS 2018-05-29 20:49:37 -07:00
Gerald Lonlas 5a4eb2cbf2 Setup.sh: make message format consistent 2018-05-29 20:48:34 -07:00
Samuel Husso c471ccb2db Merge pull request #734 from arudov/fix/pair-downloads
Do not download pairs if --refresh-pairs-cached isn't set
2018-05-29 08:05:10 +03:00
Samuel Husso 656be523bc Merge pull request #779 from gcarq/pyup-update-sqlalchemy-1.2.7-to-1.2.8
Update sqlalchemy to 1.2.8
2018-05-29 08:03:58 +03:00
pyup-bot 9cd7749867 Update sqlalchemy from 1.2.7 to 1.2.8 2018-05-28 22:14:50 +02:00
Samuel Husso 1845e5d7ca Merge pull request #772 from gcarq/pyup-update-ccxt-1.14.62-to-1.14.73
Update ccxt to 1.14.73
2018-05-27 10:23:42 +03:00
Samuel Husso 9639a3805d Merge pull request #771 from creslinux/develop
Correct instructions in backtesting.md
2018-05-27 10:23:29 +03:00
Samuel Husso bc88fbf948 Merge pull request #767 from xmatthias/ccxt_loglevel
set ccxt loglevel to info
2018-05-27 10:22:20 +03:00
pyup-bot 94c1a6f2a6 Update ccxt from 1.14.62 to 1.14.73 2018-05-26 23:41:52 +02:00
creslin 280e8b3208 Update backtesting.md - correct instructions
Correct instructions for calling a custom strategy file
To paraphrase the change:

Prior - to call a custom strategy -s the strategy file name within users_data/strategies/ directory
After - to call a custom strategy -s the class name within the strategy within users_data/strategies/ directory
2018-05-26 20:14:33 +03:00
creslin 607c895065 Update backtesting.md: how to call a custom strat
Corrected instructions, to paraphrase the PR 
prior - to call a custom strategy -s the custom strategy file name in user_data/strategies 
after - to call a custom strategy -s the class name within the custom strategy file name in user_data/strategies
2018-05-26 20:09:20 +03:00
Pan Long a98fcee4f9 Sell filled amount or an open limit buy order in forcesell.
Currently forcesell only cancels an open limit buy order and doesn't sell the filled amount.

After this change, forcesell will also update trade's amount to filled amount and sell the filled amount.
2018-05-26 09:55:31 +08:00
xmatthias 1ba5c5d9c6 set ccxt loglevel to info 2018-05-25 21:23:15 +02:00
Anton 3427c7eb54 Use constants 2018-05-25 17:04:08 +03:00
Anton cf5d691950 Clean the tests 2018-05-25 00:46:08 +03:00
Janne Sinivirta 4e0b095f2b Merge pull request #756 from gcarq/pyup-update-ccxt-1.14.27-to-1.14.62
Update ccxt to 1.14.62
2018-05-24 10:59:40 +03:00
Janne Sinivirta 0837f3f9f3 Merge pull request #733 from xmatthias/fix_fiat_init
Fix fiat initialization
2018-05-24 10:54:31 +03:00
pyup-bot bad5d57d71 Update ccxt from 1.14.27 to 1.14.62 2018-05-24 08:26:46 +02:00
Samuel Husso 620c7e8312 Merge pull request #748 from gcarq/pyup-update-pytest-3.5.1-to-3.6.0
Update pytest to 3.6.0
2018-05-24 09:01:31 +03:00
pyup-bot af0b1e806f Update pytest from 3.5.1 to 3.6.0 2018-05-23 15:06:26 +02:00
Samuel Husso cf522d1df2 Merge pull request #747 from creslinux/patch-1
OSX docker start cmd updated
2018-05-23 16:06:18 +03:00
creslin 318c973461 Update to installation.md
Added link to Docker issue  on OSX with greater detail of the problem and work-around.
2018-05-23 15:20:16 +03:00
creslin 34e78a7400 OSX docker start cmd updated
New versions of Docker will not start in OSX using the cmd in these instructions as /etc/localtime cannot be mounted. 
The change provides an alternate command that does work. 
`docker run --rm -e TZ=`ls -la /etc/localtime | cut -d/ -f8-9` -v `pwd`/config.json:/freqtrade/config.json -it freqtrade`

More info is in this thread: 
https://github.com/docker/for-mac/issues/2396
2018-05-23 13:17:35 +03:00
Anton 9be98cd8f7 Add ability to set unlimited stake_amount 2018-05-23 13:15:03 +03:00
Samuel Husso e267b84510 Merge pull request #741 from pan-long/setup-defaults
Auto apply default values in setup.
2018-05-23 10:24:22 +03:00
Pan Long c7ef69f4eb Auto apply default values in setup.
Before this commit, during setup, even a default value is displayed for some config, if user doesn't enter anything, an empty value is applied.

After this commit, if user doesn't enter anything for a config with default value, the default value will be applied.
2018-05-22 22:09:52 +08:00
Anton 8c22cfce37 Fix tests; fix codestyle 2018-05-21 23:15:01 +03:00
Anton e1cb0dbf28 Do not try to redownload pair data if --refresh-pairs-cached is not set 2018-05-21 22:31:08 +03:00
xmatthias e2efd7c6ec add test to verify network exception is cought on init of coinmarketcap 2018-05-21 20:03:25 +02:00
xmatthias 56e697acf5 Fix error initializing coinmarketcap 2018-05-21 20:01:41 +02:00
Michael Egger 13d6297b9f Merge pull request #711 from gcarq/pyup-update-ccxt-1.14.24-to-1.14.27
Update ccxt to 1.14.27
2018-05-20 10:31:27 +02:00
pyup-bot 65c069dd9f Update ccxt from 1.14.24 to 1.14.27 2018-05-20 06:41:38 +02:00
Samuel Husso b0536dba0b Merge pull request #709 from gcarq/pyup-update-ccxt-1.14.10-to-1.14.24
Update ccxt to 1.14.24
2018-05-19 09:15:02 +03:00
peterkorodi 0c051b1b7a Make plot_dataframe able to show trades stored in database. (#692)
* Show trades stored in db on the graph
2018-05-19 09:14:42 +03:00
pyup-bot 16eb793081 Update ccxt from 1.14.10 to 1.14.24 2018-05-19 06:56:37 +02:00
Samuel Husso 1cc132afe2 Merge pull request #695 from gcarq/pyup-update-ccxt-1.13.148-to-1.14.10
Update ccxt to 1.14.10
2018-05-17 08:23:32 +03:00
Samuel Husso d985405fe7 Merge pull request #683 from xmatthias/fix_get_real_amount
Fix get real amount
2018-05-17 08:22:34 +03:00
pyup-bot e88fabe1d6 Update ccxt from 1.13.148 to 1.14.10 2018-05-17 00:26:32 +02:00
Samuel Husso 7f1f1ec1ad Merge pull request #688 from gcarq/pyup-update-pandas-0.22.0-to-0.23.0
Update pandas to 0.23.0
2018-05-16 08:37:38 +03:00
pyup-bot 8094f84efe Update pandas from 0.22.0 to 0.23.0 2018-05-16 05:16:24 +02:00
Matthias Voppichler ef78f2f03a Add test for invalid order_fee dict 2018-05-15 20:13:43 +02:00
Matthias Voppichler a1fa688da0 Add tests for the new scenario 2018-05-15 19:49:47 +02:00
Matthias Voppichler 263bf918b1 Fix bug pointed out in #679 2018-05-15 19:49:28 +02:00
Samuel Husso 58a2af8d80 Merge pull request #678 from arudov/fix/get-balance
Fixed bot crash while requesting the current balance
2018-05-15 18:10:02 +03:00
Samuel Husso 594b541f34 Merge pull request #680 from gcarq/pyup-update-ccxt-1.13.147-to-1.13.148
Update ccxt to 1.13.148
2018-05-15 18:07:16 +03:00
pyup-bot cc3e4e9aa7 Update ccxt from 1.13.147 to 1.13.148 2018-05-15 16:41:31 +02:00
Janne Sinivirta d74a0f0526 Merge pull request #677 from gcarq/pyup-update-coinmarketcap-5.0.1-to-5.0.3
Update coinmarketcap to 5.0.3
2018-05-15 17:39:37 +03:00
Anton d112d90e8e Make telegram message beautiful 2018-05-15 13:37:34 +03:00
Michael Egger 2383a83c2d Merge pull request #675 from gcarq/pyup-update-ccxt-1.13.142-to-1.13.147
Update ccxt to 1.13.147
2018-05-15 12:36:27 +02:00
pyup-bot c2245362da Update coinmarketcap from 5.0.1 to 5.0.3 2018-05-15 08:41:22 +02:00
pyup-bot c96f912043 Update ccxt from 1.13.142 to 1.13.147 2018-05-15 01:11:29 +02:00
Anton f175f48418 Fix get balance functionality 2018-05-15 00:31:56 +03:00
Janne Sinivirta 6cc8017943 Merge pull request #670 from gcarq/flakify-scripts
Scripts: fix syntax errors and flake8ify
2018-05-14 08:42:11 +03:00
Samuel Husso e0bd45efab Scripts: fix syntax errors and flake8ify 2018-05-14 08:08:40 +03:00
Samuel Husso f80864b5bc Merge pull request #668 from gcarq/pyup-update-ccxt-1.13.138-to-1.13.142
Update ccxt to 1.13.142
2018-05-14 07:14:18 +03:00
pyup-bot 9a09e6b815 Update ccxt from 1.13.138 to 1.13.142 2018-05-14 03:26:26 +02:00
Michael Egger 91f90920c2 Merge pull request #665 from xmatthias/fix_fiat_convert
Fix fiat convert
2018-05-13 22:37:01 +02:00
Matthias Voppichler 8549201502 add test for new fiat_convert logic 2018-05-13 20:46:02 +02:00
Samuel Husso 0665a23b0f Merge pull request #663 from gcarq/pyup-update-ccxt-1.13.136-to-1.13.138
Update ccxt to 1.13.138
2018-05-13 21:27:01 +03:00
Matthias Voppichler b1c53ec656 refactor "patch_coinmarketcap" to conftest"
add patch_coinmarketcap to get_patched_freqtradebot
2018-05-13 20:04:40 +02:00
Matthias Voppichler 790f35a5c8 fix test which resets singleton without reinstating it 2018-05-13 20:03:54 +02:00
Matthias Voppichler 3246c60472 Fix coinmarketcap ticker 2018-05-13 20:00:38 +02:00
Matthias Voppichler 57fc9df5f3 Fix typo 2018-05-13 19:54:19 +02:00
Matthias Voppichler 144be37a9a Convert ID to string 2018-05-13 19:53:23 +02:00
Matthias Voppichler 9b8f90dc9f log error in find_price 2018-05-13 19:50:04 +02:00
Matthias Voppichler d07491ceb2 Dynamically load cryptomap 2018-05-13 19:46:08 +02:00
pyup-bot 14c140d242 Update ccxt from 1.13.136 to 1.13.138 2018-05-13 16:26:25 +02:00
Michael Egger 263d34ae82 Merge pull request #660 from xmatthias/fix_hyperopt_testfluke
Fix testfluke in hyperopt
2018-05-13 14:51:27 +02:00
Matthias Voppichler 8f17b11610 Fix testfluke in hyperopt 2018-05-13 13:38:29 +02:00
Samuel Husso 177962fa05 Merge pull request #657 from gcarq/pyup-update-ccxt-1.13.133-to-1.13.136
Update ccxt to 1.13.136
2018-05-13 10:23:34 +03:00
pyup-bot d51ac94662 Update ccxt from 1.13.133 to 1.13.136 2018-05-13 05:41:24 +02:00
Samuel Husso 40dfe4b3a9 Merge pull request #655 from xmatthias/dev_reduce_verbosity
Reduce verbosity of get_ticker_history
2018-05-12 22:20:08 +03:00
Matthias Voppichler 8b098859f4 Reduce verbosity of get_ticker_history 2018-05-12 20:15:59 +02:00
Samuel Husso 72a2c37769 Merge pull request #654 from gcarq/pyup-update-cachetools-2.0.1-to-2.1.0
Update cachetools to 2.1.0
2018-05-12 20:42:15 +03:00
pyup-bot bc25007fef Update cachetools from 2.0.1 to 2.1.0 2018-05-12 18:45:18 +02:00
Michael Egger 1e119013c8 Merge pull request #653 from gcarq/pyup-update-ccxt-1.11.149-to-1.13.133
Update ccxt to 1.13.133
2018-05-12 14:40:34 +02:00
pyup-bot 189873f9d4 Update ccxt from 1.11.149 to 1.13.133 2018-05-12 14:04:16 +02:00
Michael Egger 5b25ed99ac Merge pull request #652 from gcarq/feat/objectify-ccxt
CCXT into use
2018-05-12 14:04:06 +02:00
Michael Egger edd840ac35 Merge pull request #640 from xmatthias/ccxt-obj-slippage
[cxxt][2/2] Add columns for slippage detection
2018-05-12 13:56:15 +02:00
Matthias Voppichler 58425993da Adapt tests to verify pair-conversion and exchange conversion 2018-05-12 13:39:29 +02:00
Matthias Voppichler e3ae1c6c2f Convert exchange-name to new format 2018-05-12 13:39:16 +02:00
Matthias Voppichler 40c581e5a8 Convert pair-format to new format 2018-05-12 13:37:42 +02:00
Matthias Voppichler 631081a2b2 Add additional tests 2018-05-12 10:37:17 +02:00
Matthias Voppichler 8e3ff8235f add explaining comments 2018-05-12 10:31:24 +02:00
Matthias Voppichler ada98abfee fix flake 2018-05-12 10:30:30 +02:00
Matthias Voppichler 49266fc4b8 Add migration test 2018-05-12 10:29:26 +02:00
Matthias Voppichler f5ff6ceead Rename instead of drop/create 2018-05-12 10:29:10 +02:00
Matthias Voppichler 81ee6f8265 Update sql docs to new schema 2018-05-12 10:19:52 +02:00
Matthias Voppichler ab4e2bd5a9 Fix migrate script 2018-05-12 10:04:41 +02:00
Samuel Husso 01b6a0eb53 Freqtrade: ccxt release shall be called 0.17.0 2018-05-12 09:57:10 +03:00
Samuel Husso e1322b75a9 Freqtrade 0.16.1 release
Note. This is the last release that uses our own bittrex implementation
      for trading. After this ccxt library will be taken into use which
      will offer the needed exchanges (bittrex/binance)
2018-05-12 09:50:01 +03:00
Samuel Husso 4ce927d455 merge develop to master for 0.16.1 release (pre-work for ccxt into use) 2018-05-12 09:48:40 +03:00
Samuel Husso 20ebd744c3 Freqtrade 0.16.0 release 2018-05-12 09:43:22 +03:00
Samuel Husso b55822ad30 telegram: document proxy usage without code changes per gcarq's
comment in #609
2018-05-09 09:22:01 +03:00
Samuel Husso 7552c912a2 config.json.example: add ticker_interval 2018-05-09 09:15:09 +03:00
Samuel Husso 89180adb35 Merge pull request #646 from gcarq/pyup-update-coinmarketcap-4.2.1-to-5.0.1
Update coinmarketcap to 5.0.1
2018-05-09 08:28:29 +03:00
pyup-bot 6b008d2237 Update coinmarketcap from 4.2.1 to 5.0.1 2018-05-08 15:41:10 +02:00
Michael Egger 33ce904f41 Merge pull request #643 from xmatthias/adjust_dockerignore
exclude unnecessary files from Docker image
2018-05-07 17:20:49 +02:00
Michael Egger 1dbdb880e6 Merge pull request #637 from arudov/fix/dl-testdata-period2
Time-range download of backtesting data
2018-05-07 17:19:54 +02:00
Michael Egger ed34c4f199 Merge pull request #641 from gcarq/pyup-update-scipy-1.0.1-to-1.1.0
Update scipy to 1.1.0
2018-05-07 17:01:09 +02:00
Matthias Voppichler 394ef35a45 Add unnecessary files to .dockerignore
these files are not needed to run the bot - therefore should not be
added to the docker container
2018-05-06 20:23:20 +02:00
Matthias Voppichler ccf1c894b4 Inital try mirate 2018-05-06 09:09:53 +02:00
pyup-bot 490cbde652 Update scipy from 1.0.1 to 1.1.0 2018-05-05 21:31:05 +02:00
Matthias Voppichler d3fb2e4516 Add open_rate_requested and close_rate_requested for slippage detection 2018-05-05 12:57:07 +02:00
Samuel Husso 2c49231fcd Merge pull request #638 from gcarq/pyup-update-python-telegram-bot-10.0.2-to-10.1.0
Update python-telegram-bot to 10.1.0
2018-05-05 09:10:50 +03:00
pyup-bot 3d4019d8b7 Update python-telegram-bot from 10.0.2 to 10.1.0 2018-05-05 00:14:03 +02:00
Anton 932b65da27 Fix test_optimize.py 2018-05-04 13:59:50 +03:00
Anton 2bfce64e6a Fix conflicts 2018-05-04 13:38:51 +03:00
Gert Wohlgemuth 6d2afdb146 added support for showing the exposed real value on the count table (#634) 2018-05-03 11:18:35 +02:00
gcarq 43fd9b37df fix 'max_open_trades must be greater than 0' regression 2018-05-03 10:48:25 +02:00
Anton ceeb98dda9 Fix conflicts 2018-05-03 11:16:29 +03:00
gcarq a5c1547251 user_data: change ticker_interval to new format 2018-05-02 22:56:29 +02:00
gcarq 306885e174 Merge branch 'develop' into feat/objectify-ccxt 2018-05-02 22:49:55 +02:00
Michael Egger 90a107393a Merge pull request #622 from gcarq/fix/dl-testdata
fix download testdata
2018-05-02 22:06:43 +02:00
Michael Egger c72d4665a1 Merge pull request #619 from gcarq/feature/catch-exchange-errors
granular exception handling and retrying mechanism for ccxt
2018-05-02 20:13:16 +02:00
gcarq a76ed88496 Merge branch 'feat/objectify-ccxt' into feature/catch-exchange-errors 2018-05-02 20:03:13 +02:00
Samuel Husso bddf009a2b Merge pull request #630 from gcarq/pyup-update-pytest-mock-1.9.0-to-1.10.0
Update pytest-mock to 1.10.0
2018-05-02 07:50:36 +03:00
pyup-bot bc13b7901f Update pytest-mock from 1.9.0 to 1.10.0 2018-05-01 20:12:57 +02:00
Anton 24ab1b5be5 Fix review comments, documenation update 2018-05-01 00:27:05 +03:00
Samuel Husso 842b0c2270 Exchange: fix missing comma and typehinting per PR comments 2018-04-29 18:55:43 +03:00
Samuel Husso 743a1f1604 Merge pull request #626 from gcarq/pyup-update-numpy-1.14.2-to-1.14.3
Update numpy to 1.14.3
2018-04-28 20:33:24 +03:00
pyup-bot cec58323d4 Update numpy from 1.14.2 to 1.14.3 2018-04-28 19:19:50 +02:00
Anton a127e1db07 Fix case with empty dict 2018-04-28 01:40:48 +03:00
Anton 2267a420a4 Fix codestyle 2018-04-28 00:30:42 +03:00
Anton 82ea56c8fd Fix review comments. Add support of datetime timeganges 2018-04-28 00:16:34 +03:00
Michael Egger ecaf6b763c Merge pull request #623 from xmatthias/cxxt_obj_sellfix
[cxxt][1/2] fix fee calculation in binance
2018-04-26 19:58:24 +02:00
Matthias Voppichler 0987af910e remove indicator name from comment 2018-04-25 20:03:32 +02:00
Matthias Voppichler 2e1124af1a remove unnecessary .keys() 2018-04-25 14:00:25 +02:00
Anton 2fe7812e20 Fix codestyle 2018-04-25 10:32:58 +03:00
Matthias Voppichler 8bd9ed1543 fix flake8 2018-04-25 09:13:56 +02:00
Matthias Voppichler 72c17e29c0 Add test for "no trades found" case 2018-04-25 09:08:02 +02:00
Matthias Voppichler 483415cd65 Add fee entry to DRY_ORDER dict as defined by ccxt 2018-04-25 09:03:32 +02:00
Matthias Voppichler 98669a3d62 remove duplicate log entry, fix key-error 2018-04-25 09:01:21 +02:00
Matthias Voppichler 9c2115c917 refactor get_real_amount 2018-04-25 08:52:08 +02:00
Matthias Voppichler f6ecd8e514 Add pytest fixture for real_amount test 2018-04-25 08:51:31 +02:00
Samuel Husso 9cbd0df644 Merge pull request #624 from gcarq/pyup-update-pytest-3.5.0-to-3.5.1
Update pytest to 3.5.1
2018-04-25 07:59:27 +03:00
pyup-bot 6adab0cf6b Update pytest from 3.5.0 to 3.5.1 2018-04-25 04:54:46 +02:00
Anton 6675120324 Add time range support to download_backtest_data 2018-04-25 02:11:07 +03:00
Matthias Voppichler ab6589d573 Fix comment and improve log message 2018-04-24 19:43:08 +02:00
Matthias Voppichler 9e94778fd7 simplify check for presence of list 2018-04-24 19:42:41 +02:00
Matthias Voppichler 2968347062 fix flake8 2018-04-23 20:32:46 +02:00
Matthias Voppichler 9450b76414 improve style of import in test 2018-04-23 20:08:58 +02:00
Matthias Voppichler d2608cbf13 improve check when not to run 2018-04-23 20:06:00 +02:00
Matthias Voppichler f580fbb91d remove maybe_update_amount and tests 2018-04-23 20:03:10 +02:00
gcarq 9b0fbbdc14 cancel_order: pass all positional arguments 2018-04-23 16:58:52 +02:00
gcarq aa213a3640 cancel_order: handle InvalidOrder exception 2018-04-23 16:58:32 +02:00
gcarq baeeaa777d get_balance: handle case if currency is not in response 2018-04-23 16:57:18 +02:00
gcarq 20af4bae7c retrier: raise initial exception instead of OperationalException 2018-04-23 16:56:35 +02:00
gcarq 5baab91bb5 catch TemporaryError for buy/sell in _process() 2018-04-22 20:28:39 +02:00
gcarq 4c49229b77 catch DependencyExceptions while selling 2018-04-22 20:27:34 +02:00
Matthias Voppichler 93a7c46977 optimize to only do network calls if necessary 2018-04-22 19:37:24 +02:00
gcarq bc2bd7fe1e add retrier decorator to all exchange functions except buy/sell 2018-04-22 17:28:49 +02:00
Matthias Voppichler a70958da41 test modify-logic 2018-04-22 11:05:23 +02:00
Samuel Husso 9f1544978d tests: use only coins that most likely are going to be in bittrex 2018-04-22 11:29:21 +03:00
Samuel Husso aa104f86e8 Merge pull request #621 from xmatthias/update_docker_image
update Docker image to python-3.6.5-slim-stretch
2018-04-22 11:06:06 +03:00
Matthias Voppichler f838ba2a9b remove fee column from bot 2018-04-22 10:04:30 +02:00
Samuel Husso 53e76a89ac convert_backtestdata: flake8 fixes 2018-04-22 11:00:51 +03:00
Samuel Husso de8db9293c exchange: extract ccxt init to its own function (so that we can init ccxt from the scripts) 2018-04-22 10:57:48 +03:00
Samuel Husso fded8e5117 move download_backtest_data to scripts 2018-04-22 10:56:49 +03:00
Matthias Voppichler 710c7daec5 update Docker image to python-3.6.5-slim 2018-04-22 09:21:09 +02:00
Matthias Voppichler be95d699d2 only update if open_fee is set 2018-04-22 09:13:02 +02:00
gcarq c43ceb2045 add config*.json to .gitignore 2018-04-22 00:35:04 +02:00
gcarq 9ab4953472 fix backtesting testsuite 2018-04-22 00:21:03 +02:00
gcarq bbe3bc4423 catch ccxt.ExchangeError and retry 2018-04-22 00:20:15 +02:00
Matthias acb1b50924 [ccxt] fix unsupported fiat failures (#620)
* prepare to support FIAT/Crypto trading

* Don't fail fiat-convert for unsupported stake currencies

* remove commented code

* Add BNB to cryptomap

* Fix test-failure

* related to random execution as fee was not properly mocked if this is
one of the first tests
2018-04-21 23:20:12 +02:00
Matthias Voppichler a140748b5a Merge branch 'feat/objectify-ccxt' into cxxt_obj_sellfix 2018-04-21 22:39:22 +02:00
Matthias Voppichler 573b6b8e15 Remove unused line 2018-04-21 22:35:17 +02:00
Matthias 23e989d31f Fix tests run in random order (#599)
* allow tests to run in random mode

* Fix random test mode for fiat-convert

* allow random test execution in persistence

* fix pep8 styling

* use "usefixtures" to prevent pylint "unused parameter" message

* add pytest-random-order to travis
2018-04-21 21:21:50 +02:00
Matthias Voppichler 990f8a996b log in case of error 2018-04-21 21:01:53 +02:00
gcarq f4077a51c1 log hyperopt progress to stdout instead to the logger 2018-04-21 20:52:01 +02:00
gcarq 403f59ef45 use native python logger 2018-04-21 20:47:06 +02:00
Samuel Husso 001d7443da Merge pull request #618 from gcarq/feature/add-get_fee-mocks
add mocks for exchange.get_fee
2018-04-21 21:26:22 +03:00
Samuel Husso 4eb66aa9ce Merge pull request #617 from gcarq/feature/ccxt-enable-ratelimit
let ccxt handle rate limits internally
2018-04-21 21:25:19 +03:00
Matthias Voppichler ce90ee4ac2 have backtesting use fee_open and fee_close 2018-04-21 20:05:49 +02:00
Matthias Voppichler 06d230279c Fix tests 2018-04-21 20:05:39 +02:00
Matthias Voppichler 47748bc6f7 adjust tests for fee_open and fee_close 2018-04-21 19:55:48 +02:00
Matthias Voppichler a620aa8352 add columns fee_open and fee_close, update value 2018-04-21 19:47:08 +02:00
gcarq 09fb4ea584 add mocks for exchange.get_fee 2018-04-21 19:39:18 +02:00
gcarq 3997b6038d let cctx handle rate limits 2018-04-21 19:11:29 +02:00
Luis Felipe Díaz Chica 954c6e8c15 Write log when trying to sell opened trades (#608) 2018-04-21 18:44:57 +02:00
Samuel Husso 6d327658ea docs: Add note about using telegram proxy (#611) 2018-04-21 18:24:53 +02:00
Matthias Voppichler 7f4c70827a Test get_amount_lots 2018-04-21 13:33:29 +02:00
Matthias Voppichler f69e8458f4 Add tests for update_real_amount 2018-04-21 13:33:29 +02:00
Matthias Voppichler 02f0f22621 fix comment 2018-04-21 13:33:29 +02:00
Matthias Voppichler 1d43dc229b refactor tests of get_real_amount 2018-04-21 13:33:29 +02:00
Matthias Voppichler c7d1a767f7 add get_trades_for_order 2018-04-21 13:33:29 +02:00
Matthias Voppichler 11d8f7d522 add get_real_amount and tests 2018-04-21 13:33:29 +02:00
gcarq 1332ab397f fix reference before assignment 2018-04-21 10:19:12 +03:00
Samuel Husso 27003c447d Merge pull request #612 from gcarq/pyup-update-sqlalchemy-1.2.6-to-1.2.7
Update sqlalchemy to 1.2.7
2018-04-21 10:05:31 +03:00
pyup-bot bb07ad38d3 Update sqlalchemy from 1.2.6 to 1.2.7 2018-04-20 23:35:34 +02:00
Samuel Husso 78bafee39d download_backtest: fix imports and travis 2018-04-19 09:44:45 +03:00
Samuel Husso 66866ff260 fix travis 2018-04-19 09:06:56 +03:00
Samuel Husso 1dcd7e747e partial fix for download testdate 2018-04-19 09:01:34 +03:00
Samuel Husso 42c0d7c7c3 Merge pull request #603 from enenn/ccxt-objectify-pr3_1
[3/3] Add support for multiple exchanges with ccxt (objectified version)
2018-04-18 15:23:33 +03:00
Samuel Husso 49f2c24698 Merge pull request #605 from pan-long/fix-typo-setup
Fix a typo in setup.sh
2018-04-18 15:09:41 +03:00
Pan Long 0fab7f0880 Fix a typo in setup.sh 2018-04-18 19:11:37 +08:00
Samuel Husso 81020b3612 Merge pull request #604 from gcarq/pyup-update-python-telegram-bot-10.0.1-to-10.0.2
Update python-telegram-bot to 10.0.2
2018-04-17 10:46:03 +03:00
pyup-bot 4b78bedddd Update python-telegram-bot from 10.0.1 to 10.0.2 2018-04-17 09:27:27 +02:00
enenn 488210915a Flak8 fixes... 2018-04-15 13:11:17 +02:00
enenn f1d406b1e6 Fix possible race condition during testing
Order would sometimes fail to sell during tests,
probably because time between current time and creation was 0
2018-04-15 12:50:47 +02:00
enenn 89ed2e0127 Get mocked exhange buy return value from existing fixture 2018-04-15 12:48:02 +02:00
enenn 53b1f8d3a4 Add a 4th pair to testing dynamic whitelist generation 2018-04-15 12:20:49 +02:00
enenn cc5991d269 Fixturize fee MagicMock object in tests 2018-04-15 12:09:12 +02:00
Michael Egger b8184e4fdd Merge pull request #602 from xmatthias/obj_ccxt_test_formatms
Add test for format_ms_time
2018-04-13 00:44:25 +02:00
Matthias Voppichler 37dee02e1c Add comment and extract magic number to variable 2018-04-12 19:32:14 +02:00
enenn 2765a065a7 Use UNITTEST/BTC pair instead of ETH/BTC pair for load_data tests 2018-04-12 19:21:40 +02:00
Matthias Voppichler bb7b2cdfd5 Disable dynamic whitelist
Revert regression introduced in refactoring for objectify

(cherry picked from commit 5bd7954)
2018-04-12 18:35:35 +02:00
enenn 94287d66a8 Flake8 fixes 2018-04-12 18:16:27 +02:00
enenn 1cfa0a3c0e Add exchange name to default hyperopt config 2018-04-12 18:16:26 +02:00
enenn 1678518cd4 Add dry_run=True to config during backtesting 2018-04-12 18:16:26 +02:00
enenn 838bd5824e Mock validate_pairs 2018-04-12 18:16:26 +02:00
enenn a650072fe0 Edit signal handler tests to work on windows as well 2018-04-12 18:16:26 +02:00
enenn 6115fb08c0 Remove get_fee_maker/taker and add argument to get_fee instead 2018-04-12 18:16:25 +02:00
enenn 91b2092d55 Remove ticker_history_api and ticker_history_without_bv from conftest.py 2018-04-12 18:16:25 +02:00
enenn cba8745164 Update exchange validate_pairs and related tests 2018-04-12 18:16:19 +02:00
enenn c3d00a8825 Change ticker format to ccxt in backtesting and optimize tests 2018-04-12 18:14:33 +02:00
enenn 261522446e Change to ccxt ticker format in test_analyze.py 2018-04-12 18:07:45 +02:00
enenn a86104d0fe Update backtesting and hyperopt tests to use default_config and mock validate_pairs
Use default_config from conftest.py instead of user supplied config in user_data/hyperopt_conf
Mock validate pairs so tests don't fail if pairs don't exist/are removed from exchanges
2018-04-12 18:07:45 +02:00
enenn 4ac2afacfa Use global backtest instance for backtesting tests 2018-04-12 18:07:45 +02:00
enenn 07c655cf41 Use os.path.join for file paths 2018-04-12 18:07:45 +02:00
enenn a9ba0981c7 Use exchange id for Trade and exchange name for RPC 2018-04-12 18:07:44 +02:00
enenn 7a074f21bd Remove duplicate result pytest fixture 2018-04-12 18:07:44 +02:00
enenn fef8a4c978 Update tests related to whitelist 2018-04-12 18:07:44 +02:00
enenn 0c8ecf2b1f Add 'get_tickers' function to exchange and use it for dynamic whitelists 2018-04-12 18:07:44 +02:00
enenn 5fc8250ee4 Add 'exchange_has' function to check if exchange supports specific API call
Catch ccxt.NotSupported exception instead of checking beforehand
2018-04-12 18:07:44 +02:00
enenn e42403fecc Change date to timestamp conversion method in backtesting 2018-04-12 18:07:44 +02:00
enenn 12a84cc30b Mock fee during testing as 0.0025
Ensures profit calculations does not vary if exchange fees change, which can cause tests to fail
2018-04-12 18:07:44 +02:00
enenn 0ae5b75f33 Update order structure to ccxt generic structure instead of bittrex specific 2018-04-12 18:07:43 +02:00
enenn 4810d87044 Change buy/sell return value in tests 2018-04-12 18:07:43 +02:00
enenn 0b71f7186c Replace 'get_wallet_health' and 'get_markets_summaries'
Both are now covered by 'get_markets'
2018-04-12 18:07:43 +02:00
Samuel Husso eac3c4b72c Merge pull request #600 from enenn/ccxt-obecjtify-pr2_1
[2/3] Add support for multiple exchanges with ccxt (objectified version)
2018-04-12 07:36:18 +03:00
Matthias Voppichler d03f58417b Fix timezone dependency in test 2018-04-11 20:19:13 +02:00
Matthias Voppichler 7123985325 Add test for format_ms_time 2018-04-10 20:10:20 +02:00
Samuel Husso ce142496b1 Merge pull request #601 from gcarq/pyup-update-pytest-mock-1.8.0-to-1.9.0
Update pytest-mock to 1.9.0
2018-04-10 07:47:31 +03:00
pyup-bot 53690c5ece Update pytest-mock from 1.8.0 to 1.9.0 2018-04-10 05:57:16 +02:00
enenn 7eb5138276 Update 8m historical unittest data.
8m.json.gz should be a copy of 1m.json, 8m.json should be empty
2018-04-09 20:25:26 +02:00
enenn d50445108e Fix issue where datetime string was converted to timestamp with timezone dependent offset 2018-04-08 13:12:55 +02:00
enenn 65c5a0b308 Remove comment from donwload_backtest_data.py 2018-04-08 13:11:36 +02:00
enenn bfe1eaadcf Adapt convert_backtestdata.py to new format
Also fix timezone issue and integer overflow
2018-04-08 13:11:12 +02:00
enenn ce3603f84f Change ticker_interval from 5 to 5m in default strategy 2018-04-07 21:31:52 +02:00
Matthias a26cdceb4b Fix tests run in random order (#599)
* allow tests to run in random mode

* Fix random test mode for fiat-convert

* allow random test execution in persistence

* fix pep8 styling

* use "usefixtures" to prevent pylint "unused parameter" message

* add pytest-random-order to travis
2018-04-07 20:06:53 +02:00
enenn 21468d72d3 Fix pair order in test_rpc.py 2018-04-07 20:01:06 +02:00
enenn 4f4cb3698e Revert editing health in conftest.py 2018-04-07 17:05:44 +02:00
enenn 21c5282eb1 Change backtest data from bittrex format to ccxt format 2018-04-07 16:58:26 +02:00
enenn db46ad6502 Change ticker interval from minutes as integer to string (1m, 5m, 1h,...) 2018-04-07 16:57:47 +02:00
enenn 616006caf8 Replace 'ETH/BTC' with 'UNITTEST/BTC' to fix adx not generating if ETH/BTC ticker history is too short 2018-04-07 16:55:18 +02:00
enenn cbc0b81d2e Rename ticker history files from "BTC_XXX-1.json" to "XXX_BTC-1m.json" 2018-04-07 16:52:09 +02:00
enenn c1c6ed6ed7 Replace 'BTC_XXX' with 'XXX/BTC' for pairs and 'XXX_BTC' for files 2018-04-07 16:51:50 +02:00
Samuel Husso 248ff3349b Merge pull request #598 from gcarq/pyup-update-pytest-mock-1.7.1-to-1.8.0
Update pytest-mock to 1.8.0
2018-04-07 07:51:17 +03:00
pyup-bot 55dc699d45 Update pytest-mock from 1.7.1 to 1.8.0 2018-04-07 06:42:10 +02:00
enenn 1f75636e56 [1/3] Add support for multiple exchanges with ccxt (objectified version) (#585)
* remove obsolete helper functions and make _state a public member.

* remove function assertions

* revert worker() changes

* Update pytest from 3.4.2 to 3.5.0

* Adapt exchange functions to ccxt API
Remove get_market_summaries and get_wallet_health, add exception handling

* Add NetworkException

* Change pair format in constants.py

* Add tests for exchange functions that comply with ccxt

* Remove bittrex tests

* Remove Bittrex and Interface classes

* Add retrier decorator

* Remove cache from get_ticker

* Remove unused and duplicate imports

* Add keyword arguments for get_fee

* Implement 'get_pair_detail_url'

* Change get_ticker_history format to ccxt format

* Fix exchange urls dict, don't need to initialize exchanges

* Add "Using Exchange ..." logging line
2018-04-06 10:57:08 +03:00
Samuel Husso f3847a3a9a Merge pull request #597 from xmatthias/obj_ccxt_fix_nullref
use local config-object for check_exchange (fixes Nonetype Attribute error when starting the bot)
2018-04-05 08:05:38 +03:00
Matthias Voppichler 0203a48f3e use local config-object for check_exchange
fix AttributeError: 'NoneType' object has no attribute 'get' when
starting the bot.
2018-04-04 22:05:17 +02:00
Michael Egger 9019f6492f define constants on module level (#596) 2018-04-02 16:42:53 +02:00
Michael Egger 5420bb9f6d Merge pull request #594 from xmatthias/obj_ccxt_conv
Conversion script for Ticker history data
2018-03-31 17:58:00 +02:00
Matthias Voppichler 4ac591b076 rename logging to freqtrade 2018-03-31 17:30:11 +02:00
Matthias Voppichler 18f8686cdb fix returncode for convert_file 2018-03-31 17:29:52 +02:00
Matthias Voppichler 2f40e23dcc don't check negated if both trees are handled 2018-03-31 17:28:54 +02:00
Matthias Voppichler 8a83e050d0 use path to handle filenames 2018-03-31 17:24:25 +02:00
Samuel Husso 9cb5591007 Merge pull request #592 from xmatthias/develop_fix_dyn_wl
Disable dynamic whitelist if not specified
2018-03-31 12:14:06 +03:00
Samuel Husso eac89c244d Merge pull request #593 from gcarq/pyup-update-sqlalchemy-1.2.5-to-1.2.6
Update sqlalchemy to 1.2.6
2018-03-31 00:59:49 +03:00
Matthias Voppichler a972b8768d Improve errorhandling for json files which are not ticker data 2018-03-30 23:34:22 +02:00
Matthias Voppichler a4906c477e Add handling for gzip files 2018-03-30 23:30:23 +02:00
pyup-bot 84bbe7728d Update sqlalchemy from 1.2.5 to 1.2.6 2018-03-30 22:52:47 +02:00
Gerald Lonlas 7cafd1f17e Update exchange unit tests 2018-03-30 13:52:25 -07:00
Matthias Voppichler 5bd79546ab Disable dynamic whitelist
Revert regression introduced in refactoring for objectify
2018-03-30 22:38:09 +02:00
Gerald Lonlas 3d2c6a22a3 Fix test_validate_pairs() 2018-03-30 13:31:13 -07:00
Gerald Lonlas 052404ffbd Check if the exchange is supported 2018-03-30 13:14:35 -07:00
Gerald Lonlas 96b2210c0f Change deprecated logger.warn by warning 2018-03-30 12:11:06 -07:00
Janne Sinivirta 2efc0113fe Merge pull request #591 from gcarq/feature/remove-duplicate-ticks
Aggregate ticks in parse_ticker_dataframe
2018-03-30 10:55:51 +03:00
gcarq 24aa6a1679 adapt test_download_backtesting_testdata 2018-03-29 20:17:11 +02:00
gcarq 3775fdf9c7 change column order assertions 2018-03-29 20:16:46 +02:00
gcarq fee8d0a2e1 refactor get_timeframe 2018-03-29 20:16:25 +02:00
gcarq 702402e1fe simplify download_backtesting_testdata 2018-03-29 20:15:32 +02:00
gcarq 4f2d3dbb41 parse_ticker_dataframe: use as_index=False to keep date column 2018-03-29 20:14:43 +02:00
gcarq 02aacdd0c8 parse_ticker_dataframe: group dataframe by date 2018-03-29 17:12:49 +02:00
Janne Sinivirta 131dfaf263 Merge pull request #588 from gcarq/feature/enhance-strategy-resolving-2
Add --strategy-path parameter and simplify StrategyResolver
2018-03-28 10:54:24 +03:00
gcarq 004e0bb9a3 bot-usage.md: add strategy-path 2018-03-27 18:46:42 +02:00
gcarq 06276e1d24 bot-optimization.md: add strategy-path 2018-03-27 18:39:49 +02:00
gcarq ba5cbcbb3f configuration.md: add strategy and strategy_path 2018-03-27 18:38:43 +02:00
gcarq 872bbadded add test_load_custom_strategy() 2018-03-27 18:29:51 +02:00
gcarq 6a12591248 change strategy override condition 2018-03-27 18:20:15 +02:00
gcarq e7399b5046 add strategy and strategy_path to config_full.json.example 2018-03-27 18:16:21 +02:00
gcarq df57c32076 only override strategy if other than DEFAULT 2018-03-27 18:15:49 +02:00
gcarq f78044da6d fix method docs 2018-03-27 16:32:58 +02:00
gcarq 157f7da8ce remove obsolete assertions 2018-03-27 16:32:58 +02:00
gcarq a356edb117 implement '--strategy-path' argument 2018-03-27 16:32:58 +02:00
gcarq 5fb6fa38aa apply __slots__ to resolver and reintroduce type conversations 2018-03-27 16:32:58 +02:00
gcarq 99e890bc99 simplify resolver constructor 2018-03-27 16:32:58 +02:00
gcarq 280886104c strategy: remove unneeded population methods in resolver 2018-03-27 16:32:58 +02:00
Janne Sinivirta 1cec06f808 Merge pull request #578 from gcarq/feature/enhance-strategy-resolving
enhance strategy resolving
2018-03-27 12:44:33 +03:00
Janne Sinivirta 85a81b18a3 Merge pull request #586 from xmatthias/obj_backtest_pr2
fix backtest --export format
2018-03-27 12:43:52 +03:00
Matthias Voppichler 756bd63e1d whitespace fix 2018-03-26 23:16:41 +02:00
Matthias Voppichler a182cab27f fix backtest --export format
reverts regression introduced in c623564
2018-03-26 20:28:51 +02:00
Matthias Voppichler 9d2b7c1fc0 Add convert script 2018-03-26 20:18:14 +02:00
gcarq b254ff9b41 Merge 'develop' into feature/enhance-strategy-resolving 2018-03-26 16:23:25 +02:00
Samuel Husso 0a32d38ad9 exchange: fix get_ticker_history test 2018-03-26 09:24:50 +03:00
Samuel Husso 3069a422e9 Conftest: use coins that we know are in bittrex, added a new conf for ccxt unittest 2018-03-26 09:24:22 +03:00
Samuel Husso 1b4c1980c2 exchange: capitalize class name 2018-03-26 09:23:42 +03:00
Samuel Husso aba09b8107 Merge pull request #576 from xmatthias/obj-ccxt-ticker
objectify ccxt fix backtesting and some tests
2018-03-26 08:28:40 +03:00
Janne Sinivirta 586f49cafd Merge pull request #584 from gcarq/feature/fix-loglevel
Drop Logger class and ensure parent logger detection
2018-03-26 06:49:44 +03:00
gcarq 611bb52d1f log hyperopt progress to stdout instead to the logger 2018-03-25 22:57:40 +02:00
gcarq f374a062e1 remove freqtrade/logger.py 2018-03-25 21:43:00 +02:00
gcarq fa7f74b4bc use native python logger 2018-03-25 21:43:00 +02:00
gcarq 3f8d7dae39 make name a required argument and add fallback to getEffectiveLevel 2018-03-25 21:42:03 +02:00
gcarq 7edbae893d docs: fix typos 2018-03-25 16:42:20 +02:00
gcarq 7fe0ec5407 adapt docs/bot-usage to reflect changes 2018-03-25 16:39:31 +02:00
gcarq 6b47c39103 remove invalid mock 2018-03-25 15:12:39 +02:00
gcarq bd2a6467fe adapt argument description and metavar 2018-03-25 15:12:39 +02:00
gcarq 4fac61387f adapt docs/bot-optimization 2018-03-25 15:12:39 +02:00
gcarq 3cee94226f fix flake8 warnings 2018-03-25 15:12:39 +02:00
gcarq a38c2121cc adapt tests 2018-03-25 15:12:39 +02:00
gcarq b4d2a3f495 refactor StrategyResolver to work with class names 2018-03-25 15:12:39 +02:00
gcarq 6e5c14a95b fix mutable default argument 2018-03-25 15:12:39 +02:00
gcarq ca9c5edd39 rename Strategy into StrategyResolver 2018-03-25 15:12:39 +02:00
Matthias Voppichler f51ef1a791 refactor format_ms_time to misc.py 2018-03-25 13:38:50 +02:00
Matthias Voppichler 016232a8e9 Revert OHLVC dataformat to ccxt format
* Also fixes backtesting - but data must be refreshed for now as no
conversation is happening yet
2018-03-25 13:32:46 +02:00
Matthias Voppichler dbb0a6261f don't raise exceptions from get_ticker_history 2018-03-25 13:03:21 +02:00
Matthias Voppichler b07ee26e08 Revert testing exchange to bittrex 2018-03-25 12:57:59 +02:00
Samuel Husso a2c3df3ac5 Merge pull request #577 from gcarq/feature/fix-reference-before-assignment
fix reference before assignment error during shutdown
2018-03-25 10:15:43 +03:00
Matthias Voppichler ae803474f9 switch rpc_telgram to new style and make it pass 2018-03-24 20:59:25 +01:00
Matthias Voppichler 0a068db285 Switch rpc_test to new currency style 2018-03-24 20:59:09 +01:00
Samuel Husso d393aa0f87 Merge pull request #575 from gcarq/pyup-update-scipy-1.0.0-to-1.0.1
Update scipy to 1.0.1
2018-03-24 21:58:15 +02:00
gcarq 3f4261ad1e use correct return_code if an error occured 2018-03-24 20:56:27 +01:00
gcarq 4c97ee45dd return None if subcommand has been executed 2018-03-24 20:55:10 +01:00
gcarq 9d443b8bd8 fix reference before assignment 2018-03-24 20:54:46 +01:00
Matthias Voppichler 32222ae6ef Fix tests in acl_pair 2018-03-24 20:42:51 +01:00
pyup-bot 71025fd374 Update scipy from 1.0.0 to 1.0.1 2018-03-24 20:40:57 +01:00
Matthias Voppichler 82a2144296 change format of health fixture and get_market_summaries fixture 2018-03-24 20:36:33 +01:00
Matthias Voppichler 22ef860312 Change freqbottest currencies 2018-03-24 20:32:15 +01:00
Matthias Voppichler a6587b209f freqtradebot_tests - change currency to new format 2018-03-24 20:11:42 +01:00
Matthias Voppichler 4dc1d7538e switch currencies to new format 2018-03-24 20:07:04 +01:00
Matthias Voppichler 609c1eee55 fix persistance tests 2018-03-24 20:03:31 +01:00
Matthias Voppichler ab6e32f6bb have backtest and dry-mode working
partially revert d20e3f79be - Changing the
OHLVC format should not be done at this time
2018-03-24 19:51:40 +01:00
Matthias Voppichler 85af68d807 ccxt - make backtesting work 2018-03-24 19:45:23 +01:00
Samuel Husso 0893431fde Merge pull request #572 from gcarq/pyup-update-pytest-3.4.2-to-3.5.0
Update pytest to 3.5.0
2018-03-23 07:07:06 +02:00
pyup-bot e5abc15c53 Update pytest from 3.4.2 to 3.5.0 2018-03-23 05:30:54 +01:00
Janne Sinivirta 8d65452631 Merge pull request #569 from gcarq/feature/state-public-attr
Make state a public property on FreqtradeBot
2018-03-22 15:46:18 +02:00
Samuel Husso eb4ac73b78 remove last bittrex references so that bot is runnable 2018-03-22 08:29:52 +02:00
gcarq b8f322d8f6 revert worker() changes 2018-03-21 19:27:30 +01:00
Samuel Husso d20e3f79be analyze to use the ccxt OHLCV format
setup: remove bittrex and add requirement to ccxt

freqtradebot: update market summaries to ccxt format
2018-03-21 19:57:58 +02:00
gcarq 9df5e09a82 remove function assertions 2018-03-21 18:50:18 +01:00
gcarq 9559f50eec remove obsolete helper functions and make _state a public member. 2018-03-21 18:50:18 +01:00
Samuel Husso 40a0689183 exhcange now uses ccxt in dry_run, update config 2018-03-21 19:40:16 +02:00
Samuel Husso 14d16d573c Remove bittrex related interface code and tests 2018-03-21 19:31:15 +02:00
Samuel Husso 556533f160 requirements add ccxt, remove bittrex 2018-03-21 19:02:04 +02:00
Janne Sinivirta 62a3366fbf Merge pull request #537 from gcarq/feature/objectify
Switch from procedural code to object + Code coverage 99.09%
2018-03-21 08:59:28 +02:00
Janne Sinivirta 04c6474dd0 Merge pull request #563 from gcarq/feature/typehints
Set correct typehints and minor code cleanups
2018-03-21 08:53:38 +02:00
gcarq 3553686e50 plot_dataframe: set missing typehints 2018-03-20 19:50:04 +01:00
gcarq bc554faffb plot_profit: add missing typehints and fix mutable argument issue 2018-03-20 19:50:04 +01:00
gcarq a5c62b5c10 rpc/rpc.py: fix indentation 2018-03-20 19:50:04 +01:00
gcarq f6df7df9bf modify args typehints 2018-03-20 19:50:04 +01:00
gcarq 33ddc540cf don't shadow built-in name tuple 2018-03-20 19:50:04 +01:00
gcarq 7078bc00bd rpc: apply correct typehints; remove redundant parentheses 2018-03-20 19:50:04 +01:00
gcarq d2aea7bdc1 optimize imports 2018-03-20 19:50:04 +01:00
gcarq d8689e5045 set correct typehint; remove unused argument 2018-03-20 19:48:03 +01:00
gcarq 5327533188 optimize: set correct typehints 2018-03-20 19:48:03 +01:00
gcarq 5532cedcdd get_signal: remove redundant parentheses 2018-03-20 19:48:03 +01:00
gcarq ed71340a90 arguments: apply missing typehints 2018-03-20 19:48:03 +01:00
gcarq 1074415d30 remove invalid typehint from ctor 2018-03-20 19:48:03 +01:00
gcarq 90be78b283 CryptoFiat: inherit from object explicitly 2018-03-20 19:48:03 +01:00
gcarq 2de63133ae indicator_helpers: apply correct typehints 2018-03-20 19:48:03 +01:00
gcarq 31e2aa0f38 misc: apply missing typehints 2018-03-20 19:48:03 +01:00
gcarq cae7be4447 add fee param to function doc 2018-03-20 19:48:03 +01:00
gcarq a6a38735b1 backtesting: only respect max_open_trades with realistic_simulation 2018-03-20 19:38:33 +01:00
gcarq 93931eb32b fix typo in _generate_text_table 2018-03-19 23:05:12 +01:00
gcarq 967bf417df Merge branch 'develop' into feature/objectify 2018-03-19 19:10:19 +01:00
Matthias b67257db35 replace pymarketcap with coinmarketcap (#562)
* replace pymarketcap with coinmarketcap

* fix tests to use coinmarketcap instead of pymarketcap

* use arraypos 0

* update setup.py from pymarketcap to coinmarketcap

* Add test to check for unsupported Crypto currency
2018-03-19 18:40:40 +01:00
Matthias 94caf82ab2 Fix test_dataframe when ran standalone (#546)
* Fix dataframe test when ran standalone

* Fix standalone tests in hyperopt and optimize tests
2018-03-19 18:30:14 +01:00
gcarq eb8503c547 README: add codeclimate badge 2018-03-18 18:59:13 +01:00
Samuel Husso 89e8286cbc Merge pull request #565 from gcarq/feature/adapt-bin-wrapper
adapt bin/freqtrade to pass required parameters
2018-03-18 09:41:45 +02:00
gcarq ebe1d3647f .gitignore: add .pytest_cache/ 2018-03-18 02:04:30 +01:00
gcarq 5ed6f70010 call set_loggers() and pass sys.argv to main 2018-03-18 01:55:43 +01:00
Matthias a99c8c4046 replace pymarketcap with coinmarketcap (#562)
* replace pymarketcap with coinmarketcap

* fix tests to use coinmarketcap instead of pymarketcap

* use arraypos 0

* update setup.py from pymarketcap to coinmarketcap

* Add test to check for unsupported Crypto currency
2018-03-18 00:42:24 +01:00
Michael Egger fd44c0e59e allow max_open_trades to be zero (#561) 2018-03-17 10:40:50 +01:00
Gérald LONLAS e6732e01e1 Use ticker_interval defined in Strategy() instead of a mix between strategy and config file (#540) 2018-03-15 23:48:22 +01:00
Matthias e907c48438 Fix test_dataframe when ran standalone (#546)
* Fix dataframe test when ran standalone

* Fix standalone tests in hyperopt and optimize tests
2018-03-15 23:37:34 +01:00
Matthias 480d3876b8 Align calling of freqtrade in backtesting and plotting docu (#554) 2018-03-15 23:34:13 +01:00
Samuel Husso ab93a61066 Merge pull request #550 from gcarq/pyup-update-numpy-1.14.1-to-1.14.2
Update numpy to 1.14.2
2018-03-13 10:52:36 +02:00
pyup-bot 5f68a445cf Update numpy from 1.14.1 to 1.14.2 2018-03-12 19:53:35 +01:00
Samuel Husso de454924a3 Merge pull request #549 from gcarq/pyup-update-ta-lib-0.4.16-to-0.4.17
Update ta-lib to 0.4.17
2018-03-12 19:15:33 +02:00
pyup-bot 4be75d862f Update ta-lib from 0.4.16 to 0.4.17 2018-03-12 16:24:35 +01:00
Samuel Husso 61d5e265f5 Merge pull request #548 from ElanHasson/patch-2
Should be Telegram, not Instagram
2018-03-12 08:30:34 +02:00
Elan Hasson e172bc134b Should be Telegram, not Instagram 2018-03-11 16:07:57 -04:00
Samuel Husso 0dbc0ffb6b Merge pull request #543 from xmatthias/docker-readme
Update documentation for docker
2018-03-10 12:31:47 +02:00
Matthias Voppichler 215dea0411 Fix wrong whitespace character 2018-03-10 09:53:38 +01:00
Matthias Voppichler 4cfa3be69e add /etc/localtime to container to syncronize time 2018-03-09 20:51:28 +01:00
Samuel Husso d081f6afe7 Merge pull request #542 from xmatthias/update_dockerfile
Update dockerfile to python:3.6.4-slim-stretch
2018-03-09 09:23:11 +02:00
Matthias Voppichler adf6244eda Update dockerfile to python:3.6.4-slim-stretch 2018-03-08 19:25:42 +01:00
Janne Sinivirta 1bdbe09b6b Merge pull request #538 from gcarq/pyup-update-python-telegram-bot-9.0.0-to-10.0.1
Update python-telegram-bot to 10.0.1
2018-03-08 11:24:20 +02:00
Gérald LONLAS a10cd23990 Merge branch 'develop' into pyup-update-python-telegram-bot-9.0.0-to-10.0.1 2018-03-07 19:40:19 -08:00
Janne Sinivirta 7c393080ff Merge pull request #541 from gcarq/pyup-update-sqlalchemy-1.2.4-to-1.2.5
Update sqlalchemy to 1.2.5
2018-03-07 08:34:14 +02:00
pyup-bot d1dbefa376 Update sqlalchemy from 1.2.4 to 1.2.5 2018-03-06 20:50:25 +01:00
Gerald Lonlas c94f55807b Merge branch 'develop' into feature/objectify 2018-03-06 03:33:00 -08:00
Samuel Husso f8e81dde9e Merge pull request #539 from gcarq/pyup-update-pytest-3.4.1-to-3.4.2
Update pytest to 3.4.2
2018-03-06 09:51:21 +02:00
Gerald Lonlas 173b640b34 Increase Hyperopt() code coverage 2018-03-05 22:36:15 -08:00
Gerald Lonlas 0bb7cc8ab5 Hyperopt: fix 'Ran out of input' error 2018-03-05 20:49:45 -08:00
Gerald Lonlas a8fd7a69ab Increase Configuration._load_config_file() code coverage 2018-03-05 19:57:45 -08:00
pyup-bot b986ed5613 Update pytest from 3.4.1 to 3.4.2 2018-03-06 04:37:20 +01:00
pyup-bot 96ad74cd51 Update python-telegram-bot from 9.0.0 to 10.0.1 2018-03-05 12:55:22 +01:00
Gerald Lonlas ea7b25766b Increase Hyperopt() code coverage 2018-03-05 00:35:42 -08:00
Gerald Lonlas 1d43e04725 Increase FreqtradeBot() code coverage 2018-03-05 00:11:13 -08:00
Gerald Lonlas ba664c4341 Increase Configuration._load_hyperopt_config() code coverage 2018-03-04 23:12:34 -08:00
Gerald Lonlas aa22585d40 Add unit test for misc.common_datearray() 2018-03-04 23:05:44 -08:00
Gerald Lonlas cf78da5fae Plot_profit.py: Fix Flake8 warnings 2018-03-04 20:24:01 -08:00
Gerald Lonlas 152c4483c8 Configuration() sends a msg to user when config file not found 2018-03-04 20:22:40 -08:00
Gerald Lonlas 45341bb246 Plot_profit.py: fix it and make it works with the new object model 2018-03-04 20:21:49 -08:00
Gerald Lonlas 9ae2491b1e Plot_dataframe.py: make it works with the new object model 2018-03-04 18:12:43 -08:00
Gerald Lonlas d685646446 Arguments(): Change private methods to public 2018-03-04 17:51:57 -08:00
Gerald Lonlas de468c6fc8 Fix wrong realistic_simulation implementation in Hyperopt 2018-03-04 02:31:25 -08:00
Gerald Lonlas 6f3949bb6d Merge commit 'b799445b1a690a3773cb4b0ab73947c382285f95' into feature/objectify 2018-03-04 02:07:08 -08:00
Gerald Lonlas 25d0e5f942 Merge commit '4dca84817eb1b62047a9e4d282254392ea978e44' into feature/objectify 2018-03-04 02:06:40 -08:00
Gerald Lonlas 4abb7e22ac Merge commit 'cd28693726d4034e0332076803930ee0b6a0ae1d' into feature/objectify 2018-03-04 01:34:35 -08:00
Gerald Lonlas f8781bc193 Merge commit '293dc4da8025461c67d191981604e3c4da7137bf' into feature/objectify 2018-03-04 01:34:23 -08:00
Gerald Lonlas d7e9d8c6cc Merge commit 'df13a6f3338e94b2f49e62f776e0fe94b2e08d6b' into feature/objectify 2018-03-04 01:34:07 -08:00
Gerald Lonlas 6fcc173489 Merge commit '35c51c73f713bfdb81bd84721f3dceab0c19e819' into feature/objectify 2018-03-04 01:33:39 -08:00
Gerald Lonlas bb1e38f584 Merge commit '8eed9c08a6cffdd7c6b43fa3db2c3e08d1657f43' into feature/objectify 2018-03-04 01:01:19 -08:00
Gerald Lonlas c52e688979 Fix unit tests in test_arguments.py and test_configuration.py 2018-03-04 00:58:20 -08:00
Gerald Lonlas 2001c20426 Merge commit '028700d86f130d5c3cbfef4e422dc701340f58c9' into feature/objectify 2018-03-04 00:53:27 -08:00
Gerald Lonlas 5a6f6c7138 Merge commit 'd13d6736b92ebfed1e172b60c77029e6c10b29a6' into feature/objectify 2018-03-04 00:51:49 -08:00
Gerald Lonlas 722ed48d9d Merge commit 'e3d222912dfd775b7456a44d6d6055430711f251' into feature/objectify 2018-03-04 00:51:22 -08:00
Gerald Lonlas 38510d4b03 Merge commit '1134c81aad049d4357c8f299ffc801218f3d9574' into feature/objectify 2018-03-03 17:26:06 -08:00
Gerald Lonlas 96a343fb29 Merge commit '53b1f7ac4d0449d54711d1f406d1c0a79dc5d8ee' into feature/objectify 2018-03-03 14:59:01 -08:00
Gerald Lonlas 84759073d9 Refactor Configuration() to apply common configurations all the time and to remove show_info 2018-03-03 13:43:14 -08:00
Gerald Lonlas 0632cf0f44 Merge commit 'aa7aeb046ef72412cadd094666efc8e4c503ef2d' into feature/objectify 2018-03-02 23:28:36 -08:00
Gerald Lonlas bbb1a31fda Merge commit 'c5400b6c37c7de64a86c9db39a4d0fa9169b35f6' into feature/objectify 2018-03-03 10:01:06 +08:00
Gerald Lonlas 6158de3729 Merge commit '192521523f3894d40a8d1d77308504912618e375' into feature/objectify 2018-03-03 09:41:08 +08:00
Gerald Lonlas 3ba365ceb2 Merge commit 'fecd9f830ec4e8e9d5d1f3a70310d42bbe3f274a' into feature/objectify 2018-03-03 09:39:27 +08:00
Gerald Lonlas 5b314e2f7a Port commit "Remove Strategy fallback to default strategy (#490)"
Hash: d24cd89304
2018-03-03 09:33:54 +08:00
Gerald Lonlas 390501bac0 Make Pylint Happy chapter 1 2018-03-03 09:33:54 +08:00
Gerald Lonlas d274f13480 Remove Memory profiler in Backtesting 2018-03-03 09:33:54 +08:00
Gerald Lonlas 6148f98980 Fix Telegram unit test when using an internet connection 2018-03-03 09:33:54 +08:00
Gerald Lonlas 8bd0f4d0d7 Remove ugly pprints 2018-03-03 09:33:54 +08:00
Gerald Lonlas bc8ca491cd Minor updates 2018-03-03 09:33:54 +08:00
Gerald Lonlas 6ef7b7d93d Complete Backtesting and Hyperopt unit tests 2018-03-03 09:33:54 +08:00
Gerald Lonlas f4ec073099 Move RPC and Telegram to classes 2018-03-03 09:33:54 +08:00
Gerald Lonlas 766ec5ad0f Update unit tests to be compatible with this refactoring
Updated:
- test_acl_pair to be compatible with FreqtradeBot() class
- test_default_strategy.py to be compatible with Analyze() class
2018-03-03 09:33:54 +08:00
Gerald Lonlas 383fb6d20e Add a class Arguments to manage cli arguments passed to the bot 2018-03-03 09:33:54 +08:00
Gerald Lonlas 1d251d6151 Move Backtesting to a class and add unit tests 2018-03-03 09:33:54 +08:00
Gerald Lonlas db67b10605 Remove Singleton from Strategy() 2018-03-03 09:33:54 +08:00
Gerald Lonlas 4da033c7a2 Refactor main.py
- Update, clean, and improve code coverage on main.py
- Move bot trading logic into Freqtradebot() class
- Move unit tests to test_freqtradebot, add more coverage tests
2018-03-03 09:33:54 +08:00
Gerald Lonlas a8b8ab20b7 Move Analyze to a class 2018-03-03 09:33:54 +08:00
Gerald Lonlas e025dc0dba Keep in misc file only tool functions 2018-03-03 09:33:54 +08:00
Gerald Lonlas 89e3729955 Add a Configuration class that generate the Bot config from Arguments 2018-03-03 09:33:54 +08:00
Gerald Lonlas 3b9e828fa4 Add a class Logger to manage the logging messages
This class will evolve later to support color logging. For now
it is used to not repeat the logging configuration everywhere.
2018-03-03 09:33:54 +08:00
Gerald Lonlas cf753d5c40 Add a Enum class State that contains Bot running states 2018-03-03 09:33:54 +08:00
Gerald Lonlas 314ab0a84f Add a Constants class that contains Bot constants 2018-03-03 09:33:54 +08:00
Samuel Husso b799445b1a Merge pull request #531 from gcarq/pyup-update-pytest-mock-1.7.0-to-1.7.1
Update pytest-mock to 1.7.1
2018-03-01 14:18:10 +02:00
pyup-bot 69eddbbc76 Update pytest-mock from 1.7.0 to 1.7.1 2018-03-01 12:56:17 +01:00
Samuel Husso 4dca84817e Merge pull request #526 from gcarq/improve_log_messages
Improve log messages
2018-02-26 08:48:09 +02:00
Janne Sinivirta bf54692efb use log_has helper in tests 2018-02-24 22:18:19 +02:00
Janne Sinivirta 76c5cdc6e3 more minor tweaks to log messages 2018-02-24 20:30:16 +02:00
Janne Sinivirta 3e89b9685d remove unnecessary detail from log message 2018-02-24 19:28:51 +02:00
Janne Sinivirta 646d1f7316 better log message for outdated history 2018-02-24 19:25:08 +02:00
Janne Sinivirta 67ad9e9351 simplify some error message statements 2018-02-24 19:19:43 +02:00
Janne Sinivirta 160af91f9a improving log messages 2018-02-24 18:58:57 +02:00
Janne Sinivirta 5e73f3431c log how old the last received tick is 2018-02-24 16:59:20 +02:00
Samuel Husso cd28693726 Merge pull request #525 from gcarq/pyup-update-sqlalchemy-1.2.3-to-1.2.4
Update sqlalchemy to 1.2.4
2018-02-23 07:52:47 +02:00
pyup-bot ebad2b7542 Update sqlalchemy from 1.2.3 to 1.2.4 2018-02-22 23:17:07 +01:00
Samuel Husso 293dc4da80 Merge pull request #523 from gcarq/pyup-update-numpy-1.14.0-to-1.14.1
Update numpy to 1.14.1
2018-02-21 09:09:20 +02:00
Samuel Husso df13a6f333 Merge pull request #524 from gcarq/pyup-update-pytest-3.4.0-to-3.4.1
Update pytest to 3.4.1
2018-02-21 09:08:46 +02:00
pyup-bot e58cafed6f Update pytest from 3.4.0 to 3.4.1 2018-02-21 02:43:34 +01:00
pyup-bot 072f0b07d4 Update numpy from 1.14.0 to 1.14.1 2018-02-21 02:43:31 +01:00
Samuel Husso 35c51c73f7 Merge pull request #518 from gcarq/cleaning_up_backtesting
Cleaning up backtesting/hyperopt
2018-02-18 10:18:00 +02:00
Janne Sinivirta fac122891f remove stoploss parameter from backtest, it is loaded from strategy 2018-02-17 11:14:03 +02:00
Samuel Husso 8eed9c08a6 Merge pull request #519 from gcarq/pyup-update-pytest-mock-1.6.3-to-1.7.0
Update pytest-mock to 1.7.0
2018-02-17 10:12:28 +02:00
Samuel Husso 1911143a75 Merge pull request #520 from gcarq/pyup-update-sqlalchemy-1.2.2-to-1.2.3
Update sqlalchemy to 1.2.3
2018-02-17 10:11:32 +02:00
pyup-bot 19616eba35 Update sqlalchemy from 1.2.2 to 1.2.3 2018-02-17 01:16:22 +01:00
pyup-bot e0153d8203 Update pytest-mock from 1.6.3 to 1.7.0 2018-02-16 22:58:22 +01:00
Janne Sinivirta d1bdbcd273 Fix wrong duration calculation in hyperopting 2018-02-16 22:08:20 +02:00
Janne Sinivirta bf72b5bc37 make args available for optimizer and use them instead of guessing from params 2018-02-16 14:00:12 +02:00
Janne Sinivirta ec8bf82695 combine shared backtest/hyperopt flags 2018-02-15 15:23:49 +02:00
Janne Sinivirta f64c8cc9ce realistic should be False by default and enabled with a --realistic-simulation flag 2018-02-15 13:11:17 +02:00
Samuel Husso 028700d86f Merge pull request #517 from gcarq/fix-backslash-again
Correctly join paths in ticker loading
2018-02-15 10:38:37 +02:00
Samuel Husso d13d6736b9 Merge pull request #515 from gcarq/indicator_helpers
Random indicator helpers
2018-02-15 10:12:37 +02:00
Janne Sinivirta a1ba57186b correctly join paths and debug log the found results 2018-02-15 08:59:02 +02:00
Janne Sinivirta 459611516c enable stochastic and fisherRSI in default strategy 2018-02-14 13:02:31 +02:00
Janne Sinivirta 340ab0214b add generic fishers inverse transformation with smoothing 2018-02-14 10:17:43 +02:00
Janne Sinivirta 178d1ed423 add ehlers super smoother 2018-02-14 10:16:53 +02:00
Janne Sinivirta cf013140a6 add went_up and went_down helpers 2018-02-13 11:37:59 +02:00
Samuel Husso e3d222912d Merge pull request #511 from gcarq/hyperopt_selectable_spaces
Allow selecting Hyperopt search space
2018-02-12 08:28:24 +02:00
Gérald LONLAS 1134c81aad Merge pull request #513 from gcarq/arrays_for_backtesting
Make backtesting 5x faster
2018-02-11 21:02:43 -08:00
Janne Sinivirta 3e07d41fa9 remove mention of sell space 2018-02-12 07:01:51 +02:00
Janne Sinivirta b1230b27b7 adjust unit test to match new --spaces format 2018-02-11 19:22:13 +02:00
Janne Sinivirta 1eecf28a8b adjust documentation to match changes to --spaces flag 2018-02-11 19:18:11 +02:00
Janne Sinivirta fe28addb51 specify allowed values for --spaces flag 2018-02-11 19:17:04 +02:00
Janne Sinivirta 9bcdc8e14b remove unnecessary condition 2018-02-11 15:25:30 +02:00
Janne Sinivirta 2ce03ab1b5 make Strategy store roi and stoploss values as numbers to avoid later casting 2018-02-11 15:25:30 +02:00
Janne Sinivirta 5190cd507e start with simpler condition 2018-02-11 14:37:12 +02:00
Janne Sinivirta 2dd2f31431 remove repeated condition 2018-02-11 14:31:37 +02:00
Janne Sinivirta dc105d5eae better names for row variables 2018-02-11 14:24:19 +02:00
Janne Sinivirta c62356438a loop over arrays instead of dataframes 2018-02-11 14:18:57 +02:00
Janne Sinivirta d74543ac32 document the new --spaces flag for hyperopt 2018-02-10 11:04:16 +02:00
Janne Sinivirta 55a1f604d6 small corrections and typo fixes to hyperopt documentation 2018-02-10 11:03:56 +02:00
Janne Sinivirta f14d6249e0 allow selecting hyperopt searchspace 2018-02-09 20:59:06 +02:00
kryofly 12a19e400f tests: more backtesting testing (#496)
* tests: more backtesting testing

* tests: hyperopt

* tests: document kludge

* tests: improve test_dataframe_correct_length

* tests: remove remarks
2018-02-08 21:49:43 +02:00
Samuel Husso 53b1f7ac4d Merge pull request #509 from gcarq/cleanup_plot_scripts
Cleanup plot scripts
2018-02-08 13:50:34 +02:00
Janne Sinivirta 6f80aff3e2 cleanup plot scripts 2018-02-08 13:32:34 +02:00
Gérald LONLAS aa7aeb046e Merge pull request #508 from gcarq/faster_backtesting
Faster backtesting
2018-02-06 22:59:45 -08:00
Janne Sinivirta bf46f2e50d short circuit check for roi threshold 2018-02-06 21:37:11 +02:00
Janne Sinivirta 4760dd699d remove surprisingly slow logging line 2018-02-06 21:37:11 +02:00
Janne Sinivirta 22c48d5cef use faster time diff 2018-02-06 21:37:11 +02:00
Janne Sinivirta 0454b4c8d5 remove unnecessary Decimal construction 2018-02-06 21:37:11 +02:00
Janne Sinivirta 5c02f0983d let Strategy hold a sorted roi map 2018-02-06 21:37:11 +02:00
Janne Sinivirta a28ffcbcf7 remove slow unnecessary table scan 2018-02-06 21:21:47 +02:00
Samuel Husso c5400b6c37 Merge pull request #507 from gcarq/date_indexing_for_backtesting
Date indexing for backtesting
2018-02-06 12:20:33 +02:00
Janne Sinivirta a071571eac switch to faster short circuiting condition 2018-02-06 12:13:12 +02:00
Janne Sinivirta 5cf2dd79f2 don't reset index if not needed 2018-02-06 11:34:01 +02:00
Janne Sinivirta cf7c6d2e9c switch to properly using dates as indexes, makes date based searching and slicing a lot faster 2018-02-06 11:34:00 +02:00
Janne Sinivirta 8c7b29734e use date info to calculate trade durations 2018-02-06 11:34:00 +02:00
macd2 192521523f add an option to control vertical spacing (#506) 2018-02-05 08:05:12 +02:00
Gérald LONLAS 2765ee5a85 Merge pull request #504 from gcarq/improve_argparse
Use substitution in argparse help texts
2018-02-04 13:36:01 -08:00
Samuel Husso 585c2e31c6 Merge pull request #502 from gcarq/marker_for_buy
Change buy and sell markers in plot_dataframe
2018-02-04 16:31:41 +02:00
Janne Sinivirta fecd9f830e use substitution in argparse 2018-02-04 15:48:41 +02:00
Janne Sinivirta 6efd744497 change buy and sell markers in plot_dataframe 2018-02-04 14:09:36 +02:00
Samuel Husso 2b6a62faa1 Merge pull request #501 from gcarq/pyup-update-pymarketcap-3.3.155-to-3.3.158
Update pymarketcap to 3.3.158
2018-02-04 12:10:01 +02:00
Gérald LONLAS 4b62f84cc7 Merge pull request #500 from gcarq/fix/setup.sh
Fix config generation on setup.sh
2018-02-03 19:04:07 -08:00
pyup-bot 3fb3d30365 Update pymarketcap from 3.3.155 to 3.3.158 2018-02-03 23:38:59 +01:00
Gerald Lonlas 2c16ba18a4 Fix config generation on setup.sh 2018-02-03 12:55:15 -08:00
pyup.io bot f45c64d61b Update pymarketcap from 3.3.154 to 3.3.155 (#498) 2018-02-03 21:32:16 +02:00
mijgame 7bf88333dd Fix typos (#497)
* Update config_full.json.example

Typo

* Update config.json.example
2018-02-03 21:31:55 +02:00
Gérald LONLAS e1a033672f Merge pull request #493 from macd2/patch-3
typo fix
2018-02-02 09:34:45 -08:00
macd2 4dbc4cb652 typo fix 2018-02-02 11:23:10 +01:00
Gérald LONLAS d24cd89304 Remove Strategy fallback to default strategy (#490)
* Remove Strategy fallback to default strategy
2018-02-02 11:01:09 +02:00
Samuel Husso 0f041b424d Merge pull request #491 from gcarq/pyup-update-pymarketcap-3.3.153-to-3.3.154
Update pymarketcap to 3.3.154
2018-02-01 20:35:40 +02:00
pyup-bot 7688f18a25 Update pymarketcap from 3.3.153 to 3.3.154 2018-02-01 18:08:57 +01:00
Samuel Husso d5435a9962 Merge pull request #487 from gcarq/pyup-update-pytest-3.3.2-to-3.4.0
Update pytest to 3.4.0
2018-02-01 08:21:45 +02:00
kryofly 9f6aedea47 telegram refactor 1/ (#389)
* telegram refactor 1/

move out freqcode from telegram

* telegram refactor 2/

move out rpc_trade_status

* telegram refactor 3/

move out rpc_daily_profit

* telegram refactor /4

move out rpc_trade_statistics

* 5/

* rpc refactor 6/

* rpc refactor 7/

* rpc refactor 8/

* rpc refactor 9/

* rpc refactor 10/

cleanups
two tests are broken

* fiat

* rpc: Add back fiat singleton usage

* test: rpc_trade_statistics

Test that rpc_trade_statistics can handle trades that lacks
trade.open_rate (it is set to None)

* test: rpc_forcesell

Also some cleanups

* test: telegram.py::init

* test: telegram test_cleanup and test_status

* test rcp cleanup
2018-02-01 08:05:23 +02:00
Janne Sinivirta 45975c9677 set capturing level 2018-01-31 19:37:38 +02:00
Janne Sinivirta 0a42a0e814 Merge pull request #479 from gcarq/fix/issue-478
Fix Backtesting / Hyperopt ticker_interval download
2018-01-31 17:15:47 +02:00
Janne Sinivirta 5855f0cdfc Merge pull request #486 from jbweb/develop
Fix typos
2018-01-31 16:48:12 +02:00
Janne Sinivirta 5b71d5f3a1 Merge pull request #488 from jblestang/fixing_bug_in_backtesting_causing_to_much_sells
Fixing bug in backtesting preventing sell events to be executed
2018-01-31 16:42:02 +02:00
Janne Sinivirta 613ad4c5d6 Merge pull request #481 from jblestang/fix_buy_sell_order
Fixing buy and sell order
2018-01-31 16:37:55 +02:00
Jordy Bulten e6d6918ed8 Fixed typos in setup script 2018-01-31 09:46:20 +01:00
Jean-Baptiste LE STANG 07b7828f39 Fixing bug in backtesting causing to much sells 2018-01-31 07:59:45 +01:00
pyup-bot 8ba08af539 Update pytest from 3.3.2 to 3.4.0 2018-01-31 03:42:52 +01:00
Jordy 3aa77360f0 Update config_full.json.example
Typo fix
2018-01-30 21:46:40 +01:00
Jordy c9f97149e1 Update config.json.example
Typo fix
2018-01-30 21:46:07 +01:00
Gérald LONLAS 529e4d0131 Merge pull request #484 from baudbox/develop
Adding 1.6 comment into telegram pre-requirements
2018-01-30 08:17:01 -08:00
baudbox dc322f0423 Fixed typo 2018-01-30 15:29:18 +01:00
baudbox 6adeb97b19 Adding 1.6 comment 2018-01-30 15:00:05 +01:00
Jean-Baptiste LE STANG d53d4b808b Fixing buy and sell order 2018-01-30 09:38:24 +01:00
Gerald Lonlas d313eb812d Forgot one args.ticker_interval 2018-01-29 23:07:54 -08:00
Gerald Lonlas cac2f2b58b Wrong assert condition 2018-01-29 23:04:28 -08:00
Gerald Lonlas 321e3ede30 Fix hyperopt ticker interval download 2018-01-29 22:53:28 -08:00
Gerald Lonlas 524290d678 Fix backtesting ticker interval download 2018-01-29 22:51:29 -08:00
Janne Sinivirta 5f86c389b0 Merge pull request #476 from gcarq/feat/update-testdata
update backtesting data for the latest market craze
2018-01-30 07:38:11 +02:00
Samuel Husso 990a609afd test_analyze: update dataframe magic len check so that test pass 2018-01-30 07:26:00 +02:00
Samuel Husso 271e11e065 update backtesting data for the latest market craze 2018-01-30 07:01:44 +02:00
Samuel Husso 9df2ccbceb Merge pull request #467 from gcarq/feature/setup_script
Add setup.sh script to install and update the bot
2018-01-30 06:33:54 +02:00
Gérald LONLAS ac006e0d52 Merge pull request #469 from jblestang/refactoring_sell_eval_conditions
Refactoring the sell conditions evaluation to share the function with…
2018-01-29 18:45:20 -08:00
Gérald LONLAS 0bf56f249a Merge pull request #473 from ElanHasson/patch-1
Fixed typo. Update bot-usage.md
2018-01-29 13:08:37 -08:00
Elan Hasson b6c6f42d40 Update bot-usage.md
Fixed typo.
2018-01-29 10:08:50 -05:00
Jean-Baptiste LE STANG 0d04da3158 Removing unecessary buy condition when sell_profit_only 2018-01-29 13:33:49 +01:00
Jean-Baptiste LE STANG 94172091ae Refactoring the sell conditions evaluation to share the function with backtesting 2018-01-29 10:10:19 +01:00
Samuel Husso e6c215104f Merge pull request #468 from gcarq/fix/ignore-freqtrade-plot
Ignore freqtrade-plot.html
2018-01-29 09:50:18 +02:00
Gerald Lonlas 7a3eb40697 Ignore freqtrade-plot.html 2018-01-28 23:41:22 -08:00
Gerald Lonlas 7321836bfb Indent functions code 2018-01-28 23:35:13 -08:00
Gerald Lonlas 96c54716d7 Add --plot parameter for installing plotting dependencies 2018-01-28 23:24:41 -08:00
Gerald Lonlas f69adc1894 Add setup.sh script to install and update the bot 2018-01-28 23:18:15 -08:00
Janne Sinivirta 21b142df40 Merge pull request #453 from ermakus/fix_usdt_balance
Fix usdt balance
2018-01-29 08:48:38 +02:00
Janne Sinivirta a5155b3b20 Merge pull request #465 from gcarq/fix/increase_test_coverage
Fix/increase test coverage
2018-01-29 08:47:26 +02:00
Anton Ermak 807c067701 More test coverage 2018-01-29 10:55:42 +07:00
Gérald LONLAS b8af493b56 Merge pull request #459 from rybolov/develop
Read .gzip files in testdata/
2018-01-28 19:27:36 -08:00
Michael Smith e438422a22 test_optimize.py:
Added spaces for flake8 compliance.
2018-01-29 11:21:01 +08:00
Gérald LONLAS 91ed349e11 Merge pull request #466 from gcarq/fix/doc
Update doc: add --upgrade pip
2018-01-28 18:44:43 -08:00
Michael Smith b8f2341998 BTC_UNITEST-8.json:
Added to test gzip loading before .json file.
2018-01-29 10:25:24 +08:00
Michael Smith 4799e1ed44 tests/optimize/test_optimize.py:
Added test for gzip ticker file.
BTC_UNITEST-8.json.gz:
Added to test gzip loading.
2018-01-29 10:22:55 +08:00
Michael Smith e3b295cecc tests/optimize/test_optimize.py:
Added test for gzip ticker file.
BTC_UNITEST-8.json.gz:
Added to test gzip loading.
2018-01-29 10:22:34 +08:00
Gerald Lonlas 2a37034787 Update doc: add --upgrade pip 2018-01-28 18:01:02 -08:00
Gérald LONLAS aae8044150 Merge pull request #456 from jblestang/fix_old_dataframe_detection_for_longer_tickers
Fixing wrong 'old dataframe detection mechanism' for long tickers
2018-01-28 17:40:03 -08:00
Gerald Lonlas 20af5049af Thanks Flake8 2018-01-28 16:34:38 -08:00
Gerald Lonlas 3e777a9d87 Add unit test in misc.py to cover datesarray_to_datetimearray() 2018-01-28 16:25:15 -08:00
Gerald Lonlas 36fa5b827d Add unit test on rpc_telegram.py 2018-01-28 16:18:10 -08:00
Gerald Lonlas 7ab2498544 Increase test coverage on optimize.py 2018-01-28 15:33:57 -08:00
Gerald Lonlas df453803ce Increase test coverage on rpc_telegram.py 2018-01-28 15:29:26 -08:00
Gerald Lonlas fd9c62d1c4 Increase test coverage on strategy.py 2018-01-28 15:16:22 -08:00
Gerald Lonlas 25ab08f422 Fix Flake8 warning 2018-01-28 15:03:54 -08:00
Gérald LONLAS a0dea5a51f Merge pull request #458 from seansan/patch-7
Backtest with **With a (custom) strategy file**
2018-01-28 14:59:28 -08:00
Gérald LONLAS cec8ef3599 Merge pull request #463 from mijgame/patch-1
Update telegram-usage.md
2018-01-28 14:51:52 -08:00
Gerald Lonlas d85b56a2bd Add unit test for test_file_dump_json() 2018-01-28 14:38:30 -08:00
Gerald Lonlas 2bccaa31c9 Increase pylint score on misc.py 2018-01-28 14:28:28 -08:00
Gerald Lonlas 45a34be2ac Add more unittest for trim_tickerlist() method 2018-01-28 14:20:20 -08:00
Gerald Lonlas 9f8539f13e Remove unused code on Strategy interface 2018-01-28 13:21:25 -08:00
mijgame 33c6ef28f8 Update telegram-usage.md
Typo
2018-01-28 19:33:24 +01:00
seansan fe730a3db0 Update backtesting.md 2018-01-28 15:20:38 +01:00
Michael Smith f66958c34f optimize/__init__.py:
Added support for gzip ticker data files if they exist.
2018-01-28 21:57:25 +08:00
Michael Smith b44adaa5ab Added support in /optimize for gzip ticker data files if they exist. 2018-01-28 21:52:27 +08:00
seansan 3a905e3d59 BAcktest with **With a (custom) strategy file** 2018-01-28 14:51:45 +01:00
Jean-Baptiste LE STANG cf4d25d547 Fixing wrong 'old dataframe detection mechanism' for long tickers( > 30 minutes) 2018-01-28 14:40:02 +01:00
Samuel Husso 3b11459a38 Merge pull request #454 from gcarq/replace_matplotlib
Replace matplotlib with Plotly
2018-01-28 12:59:10 +02:00
Janne Sinivirta 02079771ef update documentation 2018-01-28 12:53:52 +02:00
Janne Sinivirta 7d29df3783 replace matplotlib with Plotly in requirements.txt 2018-01-28 11:56:52 +02:00
Janne Sinivirta 9b8cb05037 convert plot_profit to use Plotly instead of matplotlib 2018-01-28 11:51:26 +02:00
Anton Ermak 3593626a8e Merge branch 'fix_usdt_balance' of git+ssh://github.com/ermakus/freqtrade into fix_usdt_balance 2018-01-28 16:16:13 +07:00
Anton Ermak 45239724c6 Skip convert if balance is zero 2018-01-28 16:15:23 +07:00
Janne Sinivirta bb470d0aea Merge pull request #451 from gcarq/pyup-update-python-bittrex-0.2.2-to-0.3.0
Update python-bittrex to 0.3.0
2018-01-28 11:14:57 +02:00
Janne Sinivirta ffb60fe8b9 replace matplotlib with Plotly in plot_dataframe.py 2018-01-28 11:12:14 +02:00
Samuel Husso 40a78970e1 flake: remove requests as we dont use it 2018-01-28 11:09:03 +02:00
Anton Ermak 81ed7627bf Unit test 2018-01-28 16:08:43 +07:00
Samuel Husso 8be94c4af4 remove custom timeout as the latest bittrex package version implemented it 2018-01-28 11:03:19 +02:00
Janne Sinivirta 9090715ae5 Merge branch 'develop' of github.com:gcarq/freqtrade into develop 2018-01-28 10:46:33 +02:00
Janne Sinivirta a6a479f7aa balances to min roi hyperopt settings 2018-01-28 10:46:22 +02:00
Janne Sinivirta dde0695909 Merge pull request #452 from gcarq/fix/pylint
Fix/pylint
2018-01-28 10:39:56 +02:00
Gerald Lonlas d824816880 Increase pylint score on test files 2018-01-28 00:28:41 -08:00
Gerald Lonlas 776dd4a0d5 Increase pylint score on strategy 2018-01-27 21:26:57 -08:00
pyup-bot f33bc93639 Update python-bittrex from 0.2.2 to 0.3.0 2018-01-28 04:38:46 +01:00
Gerald Lonlas 67c6c380e1 Increase pylint score for fiat_convert 2018-01-27 18:23:08 -08:00
Janne Sinivirta 022fedb5d2 Merge pull request #416 from kryofly/plot_profit
Plot profit
2018-01-27 14:02:48 +02:00
Samuel Husso 50402a7805 Merge pull request #449 from gcarq/lower_hyperopt_precision
Lower precision for most search space variables
2018-01-27 10:05:14 +02:00
Janne Sinivirta 67ddb2e7f8 lower precision for most search space variables 2018-01-27 09:51:06 +02:00
Anton Ermak 432735773a Unit test 2018-01-27 13:04:06 +07:00
Samuel Husso 781b9b6dd4 Merge pull request #446 from gcarq/pylint_fixes
Pylint fixes
2018-01-26 19:21:03 +02:00
Samuel Husso c85f498bc7 Merge pull request #445 from gcarq/pyup-update-pymarketcap-3.3.152-to-3.3.153
Update pymarketcap to 3.3.153
2018-01-26 19:15:21 +02:00
Janne Sinivirta 67995a2f49 remove unnecessary else statements 2018-01-26 19:02:26 +02:00
Janne Sinivirta 1eebbebed1 fix assert order 2018-01-26 19:02:25 +02:00
Janne Sinivirta a5690e707d remove unused parameters 2018-01-26 19:02:25 +02:00
Janne Sinivirta 0ff56c6e8d use uppercase constant 2018-01-26 18:54:15 +02:00
pyup-bot b547893fbf Update pymarketcap from 3.3.152 to 3.3.153 2018-01-26 17:53:44 +01:00
Janne Sinivirta e14007ced4 sort imports 2018-01-26 18:52:39 +02:00
Janne Sinivirta 42919e8864 give type hint for _CONF 2018-01-26 18:49:14 +02:00
Janne Sinivirta 5505845c6f remove unused method parameter 2018-01-26 18:48:53 +02:00
Janne Sinivirta 95ab7c84bc remove unnecessary else 2018-01-26 18:41:41 +02:00
Janne Sinivirta f33923c784 fix typings for hyperopt code 2018-01-26 18:32:45 +02:00
Janne Sinivirta a7a7c37121 add day counter to timeframe 2018-01-26 18:32:45 +02:00
Samuel Husso e08003b336 Merge pull request #443 from gcarq/pyup-update-pymarketcap-3.3.150-to-3.3.152
Update pymarketcap to 3.3.152
2018-01-26 17:34:03 +02:00
pyup-bot 29c84bf622 Update pymarketcap from 3.3.150 to 3.3.152 2018-01-26 16:23:43 +01:00
Janne Sinivirta b7e297ebda remove unused loop variable 2018-01-26 11:50:00 +02:00
kryofly fe2f779c47 Merge branch 'develop' into plot_profit 2018-01-26 10:07:48 +01:00
Janne Sinivirta 90aae6c3a8 Merge pull request #439 from gcarq/fix/test_clean_dry_run_db
Fix test_clean_dry_run_db failing test
2018-01-26 08:24:25 +02:00
Gerald Lonlas 0baffd94a4 Fix test_clean_dry_run_db failing test 2018-01-25 21:05:10 -08:00
Janne Sinivirta 4fe6ae0bae fix search space for min ROI 2018-01-25 22:32:46 +02:00
Samuel Husso 477acdd635 Merge pull request #437 from nalepae/patch-1
[DOC] Correct typos about telegram.
2018-01-25 20:14:08 +02:00
Manu NALEPA 3da12014b8 [DOC] Correct typos about telegram. 2018-01-25 18:26:01 +01:00
Samuel Husso 58d07eeb87 Merge pull request #436 from gcarq/roi_hyperopt
ROI table Hyperopting
2018-01-25 13:34:37 +02:00
Janne Sinivirta 42087c9bfe let hyperopt optimize ROI table 2018-01-25 11:12:00 +02:00
Janne Sinivirta 5007165908 add search space for ROI table 2018-01-25 09:34:26 +02:00
Janne Sinivirta 0b24fb50c0 Merge pull request #433 from gcarq/pyup-update-sqlalchemy-1.2.1-to-1.2.2
Update sqlalchemy to 1.2.2
2018-01-25 09:31:39 +02:00
Janne Sinivirta 7dc63c06e7 Merge pull request #356 from kryofly/test_coverage
Test coverage
2018-01-25 09:31:06 +02:00
pyup-bot 5819ba9a9c Update sqlalchemy from 1.2.1 to 1.2.2 2018-01-25 04:16:43 +01:00
Janne Sinivirta 4e9e97ddbb Merge pull request #432 from kryofly/stratback
tests: run backtest single
2018-01-24 12:34:54 +02:00
kryofly 30ca078cec test: use pytest fixture 2018-01-24 11:05:27 +01:00
kryofly a14d9d35c7 tests: run backtest single 2018-01-24 10:32:52 +01:00
Samuel Husso c968b904de Merge pull request #429 from gcarq/fix/issue-385
Fix dry_run db issue when open_order_id already exist
2018-01-24 07:25:26 +02:00
Samuel Husso ba65e12c33 Merge pull request #431 from gcarq/pyup-update-pymarketcap-3.3.148-to-3.3.150
Update pymarketcap to 3.3.150
2018-01-24 07:06:28 +02:00
pyup-bot c83ac5271d Update pymarketcap from 3.3.148 to 3.3.150 2018-01-23 20:38:41 +01:00
Gérald LONLAS 38101d433b Merge pull request #430 from gcarq/include_indicators_in_hyperopt
Separate strategy and hyperopt
2018-01-23 08:15:26 -08:00
Janne Sinivirta 30abebfe65 remove hyperopt things from test_strategy 2018-01-23 17:01:13 +02:00
Janne Sinivirta c400d15ed1 rip out hyperopt things from strategy, add indicator populating to hyperopt 2018-01-23 16:56:12 +02:00
Janne Sinivirta a6cbc1ba16 Merge pull request #400 from gcarq/feature/custom_strategy
Allow custom strategy files
2018-01-23 15:25:18 +02:00
Samuel Husso b11fe2f814 Merge pull request #424 from gcarq/feat/telegram-sell-msg
Feat/telegram sell msg
2018-01-23 10:59:05 +02:00
Samuel Husso c593e909aa Merge pull request #428 from gcarq/fix/issue-397
Remove useless USDT_BTC filename conversion
2018-01-23 09:53:10 +02:00
Gerald Lonlas f4298a7323 Fix dry_run db issue when open_order_id exist 2018-01-22 23:23:29 -08:00
Samuel Husso 93bd63cfbe get rid of / replacements, minor edit to outgoing msg 2018-01-23 08:55:22 +02:00
Gerald Lonlas e220ad5389 Remove useless USDT_BTC filename conversion 2018-01-22 21:40:07 -08:00
Gerald Lonlas 5c499d16a5 Make plot_profit.py flake8 compliant 2018-01-22 21:20:17 -08:00
Gerald Lonlas 6d8252e2b6 Add support of custom strategy in plot_profit.py 2018-01-22 21:17:54 -08:00
Gerald Lonlas fcb29c6da5 Make plot_dataframe.py flake8 compliant 2018-01-22 21:12:48 -08:00
Gerald Lonlas 00f1c57279 Add support of custom strategy into plot_dataframe.py 2018-01-22 21:09:40 -08:00
Gerald Lonlas 41aa8f18fb Add ticker_interval support in strategy class 2018-01-22 20:51:39 -08:00
Gerald Lonlas 5eb7aa07a1 Update bot version to 0.16.0
This commit is major core upgrade and introduce breaking change.
2018-01-22 20:51:39 -08:00
Gerald Lonlas 1792aebaf6 Fix doc feedbacks 2018-01-22 20:51:39 -08:00
Gerald Lonlas eac6e05392 Fix error when config does not have stoploss 2018-01-22 20:51:39 -08:00
Gerald Lonlas 04010548f8 Update hyperopt params in test_strategy.py 2018-01-22 20:51:39 -08:00
Gerald Lonlas 3e8088d99c Avoid hyperopt to fail if a guard was removed from SPACE but still defined in populate_buy_trend() 2018-01-22 20:51:39 -08:00
Gerald Lonlas 1c7da95fed Move hyperopt_trials.pickle to user_data/ 2018-01-22 20:51:39 -08:00
Gerald Lonlas baae374899 Move hyperopt_conf.py into user_data/ 2018-01-22 20:51:39 -08:00
Gerald Lonlas a5853681e3 Update documentation 2018-01-22 20:51:39 -08:00
Gerald Lonlas be75522507 Fix flake8 2018-01-22 20:51:39 -08:00
Gerald Lonlas dfd61bbf1d Implement More triggers and guards from PR#394 2018-01-22 20:51:39 -08:00
Gerald Lonlas c46d78b4b9 Decouple strategy from analyse.py 2018-01-22 20:51:39 -08:00
Janne Sinivirta f7e979f3ba Merge pull request #423 from gcarq/feature/Crypto2Fiat_Singleton
Convert CryptoToFiatConverter into a Singleton
2018-01-22 16:24:19 +02:00
Janne Sinivirta fd8e7c2623 Merge pull request #426 from gcarq/fix/ticker_interval_as_int
ticker_interval as int (instead of string)
2018-01-22 11:34:20 +02:00
Samuel Husso 757a46ab12 ticker_interval as int (instead of string) 2018-01-22 10:39:26 +02:00
Samuel Husso bce6a7be61 rebase develop and update tests 2018-01-22 09:39:11 +02:00
Samuel Husso 6abbf45042 Update tests to reflect new selling msg 2018-01-22 09:36:56 +02:00
Samuel Husso ddd62277c2 add total amount of trades to /status 2018-01-22 09:36:56 +02:00
Samuel Husso bd356f3eb4 when selling, show more information about the trade in the message 2018-01-22 09:36:56 +02:00
kryofly aec481b6b3 tests: 100% cov bittrex.py 2018-01-22 08:30:00 +01:00
Gerald Lonlas 28b1ecb109 Convert CryptoToFiatConverter into a Singleton
Result in a speed up of the unittest from 60s to 4s

Because it cost time to load Pymarketcap() every time we create
a CryptoToFiatConverter, it worth it to change it into a
Singleton.
2018-01-21 16:41:59 -08:00
Samuel Husso 408f120612 Merge pull request #417 from jblestang/fix_bv_key_not_present_in_ticker_data_clean
Fixing the 'BV' key being missing for USDT
2018-01-21 19:03:33 +02:00
Jean-Baptiste LE STANG c0d3ac5534 With a better unit test thanks @glonlas 2018-01-21 15:02:41 +01:00
Jean-Baptiste LE STANG 960d088deb Fixing the 'BV' key being missing for USDT 2018-01-21 15:02:41 +01:00
kryofly 19ef682250 Merge branch 'develop' into plot_profit 2018-01-21 14:13:08 +01:00
kryofly 6171be4f46 Use dates on plot profit/dataframe
* plot_dataframe also support --timerange
* Both default to tkinter as matplotlib plotting backend
2018-01-21 13:44:30 +01:00
Janne Sinivirta f6df701b84 Merge pull request #415 from gcarq/fix/wrong_refactoring
Remove optimize.load_data() that is called twice
2018-01-21 07:42:25 +02:00
Gerald Lonlas ad2a5f1717 Remove optimize.load_data() that is called twice 2018-01-20 15:35:13 -08:00
Gérald LONLAS 3b6b2aa5fe Merge pull request #414 from gcarq/fix/issue-413
Fix the issue get_signal() missing 1 required positional argument: Interval
2018-01-20 15:12:14 -08:00
Gerald Lonlas 998081785e Fix the issue get_signal() missing 1 required positional argument: Interval 2018-01-20 15:05:01 -08:00
kryofly e94e6292e9 Merge branch 'develop' into test_coverage 2018-01-20 22:01:03 +01:00
Gérald LONLAS d2371b5bac Merge pull request #391 from jblestang/support_multiple_ticker
Support multiple tickers
2018-01-20 11:02:42 -08:00
kryofly f40d9dbb05 plot_profit uses --timerange flag 2018-01-20 19:49:04 +01:00
Jean-Baptiste LE STANG f1efaffe81 with fXXXXX8 2018-01-20 19:30:47 +01:00
Jean-Baptiste LE STANG 36797cda30 Merge branch 'develop' into support_multiple_ticker 2018-01-20 19:25:47 +01:00
Samuel Husso 52d881e3f9 Merge pull request #411 from jblestang/fixing_crappy_ticker_data_handling
fixing handling of data fetched from Bittrex server with bad content in the ticker
2018-01-20 18:07:30 +02:00
Jean-Baptiste LE STANG 081d3932b6 Fixing bug report #406 + unit test 2018-01-20 14:44:13 +01:00
Janne Sinivirta a7e561b55f Merge pull request #369 from kryofly/plot_profit
Plot profit from exported backtesting results
2018-01-20 11:54:46 +02:00
kryofly cf266a67ad Merge branch 'develop' into test_coverage 2018-01-20 10:06:53 +01:00
kryofly 8bbe8a7f95 Merge branch 'develop' into plot_profit 2018-01-20 08:33:28 +01:00
Janne Sinivirta a3f84d9f21 Merge pull request #409 from gcarq/feature/add_num_trade_daily
Add number of trades in /daily command
2018-01-20 08:23:50 +02:00
Gerald Lonlas fb110ccfd2 Add number of trades in /daily command 2018-01-19 22:14:31 -08:00
Janne Sinivirta 99de17da82 Merge pull request #361 from kryofly/backtest-export
Backtest export
2018-01-20 07:45:38 +02:00
kryofly e3088647fc Merge branch 'develop' into test_coverage 2018-01-19 08:40:40 +01:00
kryofly 9d75b63a6e Merge branch 'develop' into plot_profit 2018-01-19 07:26:04 +01:00
kryofly 4a9e1cb345 Merge branch 'develop' into backtest-export 2018-01-19 07:02:38 +01:00
Gérald LONLAS a4b8db38ca Merge pull request #404 from gcarq/fix/doc
Fix markdown mistakes in backtesting doc
2018-01-18 21:28:54 -08:00
Gerald Lonlas ddc1b7cd49 Update bot commands in README.md 2018-01-18 21:15:20 -08:00
Gerald Lonlas 861e065d08 Fix markdown mistakes in backtesting doc 2018-01-18 21:07:55 -08:00
Gérald LONLAS 14d16f2574 Merge pull request #357 from kryofly/timeperiod
Timeperiod
2018-01-18 20:26:44 -08:00
Gérald LONLAS 57757d22f9 Merge pull request #403 from gcarq/pyup-update-arrow-0.12.0-to-0.12.1
Update arrow to 0.12.1
2018-01-18 20:25:13 -08:00
Gérald LONLAS 98f808326f Merge pull request #395 from jblestang/fix_signal_overlaps
Fix signal overlaps
2018-01-18 19:47:55 -08:00
pyup-bot 9a48e3b867 Update arrow from 0.12.0 to 0.12.1 2018-01-19 01:33:33 +01:00
Janne Sinivirta 6cafa9120c Merge pull request #392 from stephendade/timeoutfix3
Order timeouts - added exception catching and rpc messaging
2018-01-18 10:18:48 +02:00
Janne Sinivirta 4658b554ce Merge pull request #399 from gcarq/pyup-update-ta-lib-0.4.15-to-0.4.16
Update ta-lib to 0.4.16
2018-01-18 07:19:34 +02:00
Janne Sinivirta 4a3144ae43 Merge pull request #398 from kryofly/test_speedup
tests: speed up backtests
2018-01-18 07:19:14 +02:00
pyup-bot fb34fe8c9a Update ta-lib from 0.4.15 to 0.4.16 2018-01-17 23:08:30 +01:00
Jean-Baptiste LE STANG c9e1fd3fc4 Merge branch 'develop' into support_multiple_ticker 2018-01-17 21:29:36 +01:00
kryofly 423b251467 tests: speed up backtests 2018-01-17 18:19:39 +01:00
Jean-Baptiste LE STANG f48b493620 Merge branch 'support_multiple_ticker' of https://github.com/jblestang/freqtrade into support_multiple_ticker 2018-01-17 13:52:36 +01:00
Jean-Baptiste LE STANG 5e75f1d8cd Fixing the documentation 2018-01-17 13:52:14 +01:00
toto b34621fadf fixing default ticker_interval 2018-01-17 13:52:14 +01:00
Jean-Baptiste LE STANG 42a135fbd9 fix typo in API Bittrex 2018-01-17 13:52:14 +01:00
Jean-Baptiste LE STANG 8e5de365a5 Ticker in the conf is now an enum string 2018-01-17 13:52:14 +01:00
Jean-Baptiste LE STANG 658d16c2cd really fixing this stuff ... 2018-01-17 13:52:14 +01:00
Jean-Baptiste LE STANG 3a4ff4c76c fixing a duplicated unit test without config 2018-01-17 13:52:14 +01:00
Jean-Baptiste LE STANG 7b292d5ca3 backtesting takes its ticker_interval from the config file, else from the command line options 2018-01-17 13:52:14 +01:00
Jean-Baptiste LE STANG 2509ce030d Refreshing pair of only selected ticker_interval 2018-01-17 13:52:14 +01:00
Jean-Baptiste LE STANG 15189c28ed fixing pep8 compliance 2018-01-17 13:52:14 +01:00
Jean-Baptiste LE STANG a0df566b2b fix unitest file for 30 minutes ticker 2018-01-17 13:52:14 +01:00
Jean-Baptiste LE STANG e2e2005567 Adding 30 minutes, 1 hour, 1 day tickers 2018-01-17 13:52:14 +01:00
Samuel Husso a799b7d56d Merge pull request #394 from gcarq/more_triggers
More triggers and guards
2018-01-17 14:14:33 +02:00
Jean-Baptiste LE STANG 0d709847ee Fixing the doc and and the default value of sell_profit_only to False 2018-01-17 11:31:26 +01:00
Jean-Baptiste LE STANG 58bcb9dfc8 Fixing the documentation 2018-01-17 11:24:45 +01:00
Stephen Dade 04be438b35 Better exception handling for check_handle_timedout 2018-01-17 19:51:27 +11:00
toto fa3b96eb4a fixing default ticker_interval 2018-01-16 21:37:37 +01:00
toto 5723039637 fXXXXXXk8 2018-01-16 21:21:43 +01:00
toto 6dd48fb820 Adding unitest 2018-01-16 21:18:43 +01:00
toto 12ffbf5047 - get_signal to return both SELL and BUY signal
- _process modified so that we do not sell if we would buy afterwards
- execute_sell modified so that that min_roi_reached is not executed if we would buy afterwards

Veuillez saisir le message de validation pour vos modifications. Les lignes
2018-01-16 20:22:15 +01:00
Janne Sinivirta c670ccfd37 add trigger +DI crossed above -DI 2018-01-16 18:52:06 +02:00
Janne Sinivirta 8896b39231 add heikenashi reversal bullish trigger to hyperopt 2018-01-16 18:52:06 +02:00
Janne Sinivirta ce963aae58 add macd < 0 guard to hyperopt 2018-01-16 18:52:06 +02:00
Janne Sinivirta dc01807b3c switch ema5 trigger to ema3 cross trigger 2018-01-16 18:52:06 +02:00
Janne Sinivirta fadac5fe4a remove too aggressive trigger 2018-01-16 18:52:06 +02:00
Janne Sinivirta 99260735ae remove broken bbands trigger from hyperopt. add two working bbands triggers 2018-01-16 18:52:06 +02:00
Janne Sinivirta 3e1a70bbb2 enable correct bollinger bands 2018-01-16 18:52:06 +02:00
Janne Sinivirta fd3568d48f Merge pull request #393 from gcarq/balancing_hyperopt_2
Balancing hyperopt objective
2018-01-16 18:21:50 +02:00
Janne Sinivirta 501be8a3bc adjust the hyperopt objective function to emphasize profit and allow more variation in trade counts 2018-01-16 16:36:50 +02:00
Janne Sinivirta 38fe7ec7cd adjust default target values for hyperopt 2018-01-16 16:35:48 +02:00
Stephen Dade 01e10014bb Order timeouts - added exception catching and rpc messaging 2018-01-16 22:21:05 +11:00
kryofly 0e58ab7e01 more advanced use of --timerange 2018-01-16 00:15:49 +01:00
Jean-Baptiste LE STANG bcabb90f5a fix typo in API Bittrex 2018-01-15 22:36:38 +01:00
Jean-Baptiste LE STANG 86b11a9365 Ticker in the conf is now an enum string 2018-01-15 22:27:12 +01:00
kryofly 71bb348698 rename --timeperiod to --timerange 2018-01-15 21:49:06 +01:00
Samuel Husso 5a82d99482 Merge pull request #388 from gcarq/pyup-update-sqlalchemy-1.2.0-to-1.2.1
Update sqlalchemy to 1.2.1
2018-01-15 19:23:42 +02:00
pyup-bot 50462fdb00 Update sqlalchemy from 1.2.0 to 1.2.1 2018-01-15 16:32:27 +01:00
Samuel Husso 354dcaac58 Merge pull request #386 from ermakus/show_estimated_btc_fiat_balance
Show estimated BTC and fiat balance
2018-01-15 09:13:42 +02:00
Anton Ermak 5db04b15e7 Balance Estimated BTC - fix test 2018-01-15 12:08:56 +07:00
Anton Ermak dd9ab5264d Estimated BTC and fiat value for balance 2018-01-15 12:08:42 +07:00
Gérald LONLAS 5a50b88f52 Merge pull request #374 from robmoggach/develop
New Installation Docs
2018-01-14 19:59:40 -08:00
Gérald LONLAS dce554af53 Merge branch 'develop' into develop 2018-01-14 18:10:55 -08:00
Gérald LONLAS 130867a6c2 Merge branch 'develop' into develop 2018-01-14 18:03:28 -08:00
Rob Moggach b5cd9dab26 change cat to cp 2018-01-14 12:25:30 -05:00
Janne Sinivirta ec7bfba8df add comment about checking the new total profit logging 2018-01-14 13:11:19 +02:00
Janne Sinivirta f1e176d35c log total profit in percentages also 2018-01-14 13:10:25 +02:00
Janne Sinivirta 92241baade log the loss value 2018-01-14 13:09:39 +02:00
kryofly f61012097c Merge branch 'develop' into timeperiod 2018-01-14 10:23:54 +01:00
Samuel Husso fe26ff763e Merge pull request #381 from gcarq/doc_update
Documentation update
2018-01-14 09:46:11 +02:00
Samuel Husso 6aa812aa0c Merge pull request #379 from kryofly/testdata-download2
support download for multiple testdata sets
2018-01-14 09:42:53 +02:00
Gerald Lonlas 344843d802 Update doc: 'cp' becomes 'cp -n', and add more FAQ questions 2018-01-13 23:02:00 -08:00
kryofly 3277e491f1 support download for multiple testdata sets 2018-01-13 17:40:59 +01:00
Janne Sinivirta 80e7f37f50 Merge pull request #376 from jblestang/fix_ticker_with_null_value
Fixing the ticker analysis with null values
2018-01-13 15:36:20 +02:00
Janne Sinivirta 61c4624f5f Merge pull request #377 from gcarq/pyup-update-pymarketcap-3.3.147-to-3.3.148
Update pymarketcap to 3.3.148
2018-01-13 15:34:31 +02:00
kryofly fc2e8b321f test for bittrex to reach 100% cov again 2018-01-13 14:29:16 +01:00
pyup-bot e5b27baa59 Update pymarketcap from 3.3.147 to 3.3.148 2018-01-13 13:38:23 +01:00
kryofly a62a5f814a main returns integer instead of sys.exit 2018-01-13 13:16:40 +01:00
kryofly 53447e7ef5 test cleanup 2018-01-13 12:52:02 +01:00
Jean-Baptiste LE STANG f7a44d1cec Fixing the ticker analysis with null value 2018-01-13 09:50:02 +01:00
Jean-Baptiste LE STANG c34a61dd55 really fixing this stuff ... 2018-01-13 09:21:49 +01:00
Jean-Baptiste LE STANG e834a4e4f5 fixing a duplicated unit test without config 2018-01-13 09:09:12 +01:00
Jean-Baptiste LE STANG 0328caffe4 backtesting takes its ticker_interval from the config file, else from the command line options 2018-01-13 08:55:45 +01:00
Jean-Baptiste LE STANG 260bb2f558 Refreshing pair of only selected ticker_interval 2018-01-13 08:32:44 +01:00
Gérald LONLAS 70f2aed0a7 Merge pull request #375 from gcarq/update_version
Update freqtrade version
2018-01-12 23:21:06 -08:00
Jean-Baptiste LE STANG 46dc9985fc fixing pep8 compliance 2018-01-13 08:19:39 +01:00
Gerald Lonlas 3087ca0823 Update freqtrade version 2018-01-12 22:56:39 -08:00
Janne Sinivirta 372dc5b49a Merge pull request #368 from gcarq/pyup-update-pymarketcap-3.3.145-to-3.3.147
Update pymarketcap to 3.3.147
2018-01-13 07:33:16 +02:00
Janne Sinivirta 030aedc7d4 Merge pull request #362 from gcarq/pyup-update-ta-lib-0.4.14-to-0.4.15
Update ta-lib to 0.4.15
2018-01-13 07:33:04 +02:00
Rob Moggach 25e021d4b4 installation docs update 2018-01-12 21:32:09 -08:00
Rob Moggach d48d2d08df cleaned up installation docs 2018-01-12 18:36:12 -08:00
kryofly 524899ccbf plot profit: export format change 2018-01-12 22:23:43 +01:00
kryofly d4008374f6 backtest export: include enter,exit dates 2018-01-12 22:12:00 +01:00
kryofly 48432abff1 remove two-letter options 2018-01-12 19:48:52 +01:00
kryofly 167483f777 plot profit: filter multiple pairs, misc fixes 2018-01-12 19:18:31 +01:00
Jean-Baptiste LE STANG 4eca4abb21 fix unitest file for 30 minutes ticker 2018-01-12 17:06:26 +01:00
Jean-Baptiste LE STANG e99286f871 Adding 30 minutes, 1 hour, 1 day tickers 2018-01-12 17:02:35 +01:00
kryofly d8d46890b3 script: plot profit 2018-01-12 11:56:04 +01:00
kryofly 98cf986934 misc options parsing split up 2018-01-12 11:55:58 +01:00
kryofly 829da096e2 plotting docs 2018-01-12 11:49:50 +01:00
pyup-bot a26cb4bc6b Update pymarketcap from 3.3.145 to 3.3.147 2018-01-12 11:08:23 +01:00
Gérald LONLAS 1fe86656e1 Merge pull request #364 from gcarq/fix/issue-363
Fix plot_dataframe.py
2018-01-11 21:26:10 -08:00
Gerald Lonlas 39c6e5263a Fix plot_dataframe.py 2018-01-11 21:09:04 -08:00
pyup-bot 46a1a2de10 Update ta-lib from 0.4.14 to 0.4.15 2018-01-11 20:53:26 +01:00
kryofly 05f5a1b0ee Merge branch 'develop' into test_coverage 2018-01-11 19:49:33 +01:00
kryofly 153e11f045 Merge branch 'develop' into timeperiod 2018-01-11 19:45:47 +01:00
kryofly 4781a23809 Merge branch 'develop' into backtest-export 2018-01-11 19:40:42 +01:00
kryofly ed47ee4e29 backtest export json2 2018-01-11 19:14:11 +01:00
kryofly 27769f0301 uncomplex backtest 2018-01-11 17:45:41 +01:00
kryofly feb5da0c35 file_dump_json 2018-01-11 15:49:04 +01:00
Samuel Husso 3a902289f1 testdata path to use os.path.join (#360) 2018-01-11 12:58:06 +01:00
Samuel Husso 3ac3ead2cf Merge pull request #358 from ermakus/set_requests_default_timeout
Set timeout for bittrex only
2018-01-11 08:51:21 +02:00
Anton Ermak 0d0737d1f6 Resolve conflict 2018-01-11 13:36:56 +07:00
Samuel Husso 27fcf62011 Merge pull request #354 from gcarq/linter-fixes
Linter fixes
2018-01-11 08:32:48 +02:00
Anton Ermak bb91fdbaf9 oops, print removed 2018-01-11 13:26:49 +07:00
Anton Ermak 11cbb9188b Set timeout for bittrex only 2018-01-11 12:24:05 +07:00
Janne Sinivirta c11102cf4a another run of autopep8 2018-01-11 07:08:56 +02:00
Janne Sinivirta 02fcbbb6d2 few flake8 fixes 2018-01-11 07:08:56 +02:00
Janne Sinivirta 0d6051e6f9 formatting 2018-01-11 07:08:56 +02:00
Janne Sinivirta 6a433282dc fix literal comparison 2018-01-11 07:08:56 +02:00
Janne Sinivirta 8fb404b0f8 ignore talib.abstract in pylint 2018-01-11 07:08:56 +02:00
Janne Sinivirta 64530c6196 remove unused variables 2018-01-11 07:08:56 +02:00
Janne Sinivirta 86db6c9084 sort imports 2018-01-11 07:08:56 +02:00
Janne Sinivirta 0abc30401c linter fixes and cleanups 2018-01-11 06:50:36 +02:00
Janne Sinivirta 1b6b0ad9d2 autopep8 2018-01-11 06:50:36 +02:00
Janne Sinivirta 7cdbd550c8 Merge pull request #351 from gcarq/feat/hyperopt-resume
resume hyperopt run
2018-01-11 06:47:05 +02:00
kryofly 94883202b8 docs: --timeperiod argument 2018-01-11 00:14:36 +01:00
kryofly b0f3fd7ffb timeperiod argument to backtesting and hyperopt 2018-01-10 23:48:59 +01:00
kryofly feca87345f refactor 2018-01-10 23:00:40 +01:00
kryofly f848a5c87d tests optimize load_data 2018-01-10 13:43:03 +01:00
kryofly 0cb57bee0e small refactor of check_handle_timedout 2018-01-10 13:43:00 +01:00
kryofly f8cc08e2a1 small refactor splitting the _process() 2018-01-10 13:42:59 +01:00
kryofly ad2328bbd8 tests for exchange 2018-01-10 13:42:58 +01:00
kryofly d5ca77da97 tests for analyze 2018-01-10 13:42:55 +01:00
Samuel Husso 69f68c428e Merge pull request #355 from ermakus/set_requests_default_timeout
Set requests default timeout
2018-01-10 14:22:39 +02:00
Anton Ermak abcdbcfd39 Set requests default timeout 2018-01-10 17:37:49 +07:00
Samuel Husso e67c652988 use os.path.join, fix docstrings 2018-01-10 11:50:00 +02:00
Gérald LONLAS ddc711ec93 Merge pull request #353 from kryofly/test_exchange_bittrex
test: increase coverage of exchange.bittrex
2018-01-09 17:26:38 -08:00
kryofly b9bf5c1118 test: increase coverage of exchange.bittrex 2018-01-09 14:07:50 +01:00
Robert Moggach 9840e0b5b8 use HTTPS git URL in README.md (#347) 2018-01-09 13:31:59 +01:00
Samuel Husso ffae0b2cd5 hyperopt: prettyfie best values when receiving SIGINT, use the global TRIALS 2018-01-09 12:37:56 +02:00
Samuel Husso fe2b0c2862 add unittest to save and read trials file 2018-01-09 12:26:52 +02:00
Samuel Husso 1647e7a0c1 update fix failing tests, unitest that resume hyperopt functionality works 2018-01-09 12:26:52 +02:00
Samuel Husso b35fa4c9f6 hyperopt: show the best results so far 2018-01-09 12:25:58 +02:00
Samuel Husso a48840509b Hyperopt: use results from previous runs 2018-01-09 12:25:58 +02:00
Samuel Husso ca8cab0ce9 Hyperopt to handle SIGINT by saving/reading the trials file 2018-01-09 12:25:58 +02:00
Gérald LONLAS bbcf6943ce Merge pull request #349 from gcarq/docs-update
Update installation.md
2018-01-08 23:50:21 -08:00
Samuel Husso fbf9bfe897 Update installation.md
it seems that ta-lib requires python3.6-dev package to be installed
2018-01-09 07:24:00 +02:00
Janne Sinivirta e46fcf0e02 Merge pull request #344 from gcarq/fix-hyperopt-stoploss
Fix hyperopt stoploss
2018-01-09 06:42:13 +02:00
Rob Moggach 732281bca0 public git URL 2018-01-08 20:27:41 -08:00
Janne Sinivirta f7dd5e6396 use sensible value for stoploss in test 2018-01-08 22:00:10 +02:00
Janne Sinivirta dd2ccea6e5 fix wrong range in stoploss search space 2018-01-08 21:59:46 +02:00
Janne Sinivirta 3d13eb2dc2 Merge pull request #342 from stephendade/fiatfix
Added missing fiat currencies to config
2018-01-08 10:11:01 +02:00
Stephen Dade 26b8661325 Added missing fiat currencies to config 2018-01-08 18:51:04 +11:00
Janne Sinivirta fa97a82568 Merge pull request #332 from gcarq/hyperopt_stoploss
Add stoploss to the hyperopt parameters
2018-01-08 08:03:09 +02:00
Janne Sinivirta 1ae73d7da2 Merge branch 'develop' into hyperopt_stoploss 2018-01-08 07:49:44 +02:00
Samuel Husso d8e692c9a3 Merge pull request #339 from gcarq/upgrade_flake8
Upgrade flake8
2018-01-08 07:34:45 +02:00
Gerald Lonlas ca05d1f79e Fix for flake8 2018-01-07 21:08:12 -08:00
Janne Sinivirta 9dd38aebe0 add stoploss to the hyperopt parameters 2018-01-07 21:08:12 -08:00
Gérald LONLAS ceded8a20a Merge pull request #338 from gcarq/fix/issue-337
Fix hypeopt issue when no result found
2018-01-07 21:07:07 -08:00
Gerald Lonlas 9c21077dc1 Fix hypeopt issue when no result found 2018-01-07 17:53:21 -08:00
Gérald LONLAS fca6a09a41 Merge pull request #293 from jblestang/fix_issue_278
The /status table command was getting slower when we had multiple trades opened
2018-01-07 15:15:25 -08:00
Jean-Baptiste LE STANG bba711c89a with flake8 ... 2018-01-07 23:35:16 +01:00
Jean-Baptiste LE STANG 5fbaa6d4cf rebasing for ta-lib dependency 2018-01-07 23:30:37 +01:00
Jean-Baptiste LE STANG 5b1f84f816 without debug print 2018-01-07 23:29:19 +01:00
Jean-Baptiste LE STANG 65127533ef fixing unittest 2018-01-07 23:29:19 +01:00
Jean-Baptiste LE STANG 05ca00b623 Add a unitest and fix pep8 2018-01-07 23:26:45 +01:00
Jean-Baptiste LE STANG 4b6d855e63 fix a typo in the description of get_ticker 2018-01-07 23:26:45 +01:00
Jean-Baptiste LE STANG 7d7752efbf really fixing 2018-01-07 23:26:45 +01:00
Jean-Baptiste LE STANG ce6f6ab9fe fixing refresh argument ... 2018-01-07 23:26:45 +01:00
Jean-Baptiste LE STANG 3a0569cfd3 force refresh is the value has never been set 2018-01-07 23:26:45 +01:00
Jean-Baptiste LE STANG 7d21015b52 get_ticker can return a cached value 2018-01-07 23:26:45 +01:00
Gérald LONLAS a57707071c Merge pull request #334 from gcarq/pyup-update-ta-lib-0.4.10-to-0.4.14
Update ta-lib to 0.4.14
2018-01-07 14:25:01 -08:00
Gérald LONLAS 2a347e4027 Merge pull request #328 from kryofly/datadir
--datadir <path> argument
2018-01-07 14:17:43 -08:00
Jean-Baptiste LE STANG 4c8ae3a7af without debug print 2018-01-07 23:15:33 +01:00
Jean-Baptiste LE STANG 2773ce7ebf rebasing against develop 2018-01-07 21:34:42 +01:00
Jean-Baptiste LE STANG f4e4104d14 Fixing unitest 2018-01-07 21:26:43 +01:00
Jean-Baptiste LE STANG b722a89276 fixing unittest 2018-01-07 21:24:17 +01:00
pyup-bot 4bf6711dbb Update ta-lib from 0.4.10 to 0.4.14 2018-01-07 18:08:15 +01:00
Janne Sinivirta 5be733a174 fix flake8 warnings 2018-01-07 14:37:09 +02:00
Janne Sinivirta c3cae5dfc4 have pip upgrade flake8 and coveralls 2018-01-07 14:32:01 +02:00
kryofly 0c9d862a49 docs: --datadir documentation 2018-01-07 10:15:26 +01:00
Jean-Baptiste LE STANG 975a785e68 Add a unitest and fix pep8 2018-01-07 10:14:11 +01:00
Jean-Baptiste LE STANG 6be607e528 fix a typo in the description of get_ticker 2018-01-07 10:14:11 +01:00
Jean-Baptiste LE STANG 80c4dea875 really fixing 2018-01-07 10:14:11 +01:00
Jean-Baptiste LE STANG 9e7a4c3717 fixing refresh argument ... 2018-01-07 10:14:11 +01:00
Jean-Baptiste LE STANG c72e9c3cef force refresh is the value has never been set 2018-01-07 10:14:11 +01:00
Jean-Baptiste LE STANG 8175eaa48a get_ticker can return a cached value 2018-01-07 10:14:11 +01:00
kryofly 890083ce7f Merge branch 'develop' into datadir 2018-01-07 10:00:35 +01:00
Gérald LONLAS 454cd16df4 Merge pull request #331 from gcarq/fix/work_without_network
Fix _coinmarketcap that fails backtesting and Hyperopt when no network
2018-01-06 21:33:24 -08:00
Gérald LONLAS 7e233b536c Merge pull request #323 from gcarq/add_indicators
Add 28 optional indicators populate_indicators()
2018-01-06 21:30:27 -08:00
Gérald LONLAS ae19ab3dd3 Merge pull request #330 from gcarq/feature/better_hp_result_display
Make readable hyperopt best parameters result
2018-01-06 21:30:02 -08:00
Gerald Lonlas bf4b2dc05e Fix _coinmarketcap that fails backtesting and Hyperopt when no network 2018-01-06 21:21:28 -08:00
Janne Sinivirta 571ea6a2bc Merge pull request #329 from gcarq/pyup-update-numpy-1.13.3-to-1.14.0
Update numpy to 1.14.0
2018-01-07 07:19:29 +02:00
Gerald Lonlas b3ea0f4ec5 Make readable hyperopt best parameters result 2018-01-06 17:19:48 -08:00
pyup-bot d4c8ad5ba7 Update numpy from 1.13.3 to 1.14.0 2018-01-07 01:47:18 +01:00
Gérald LONLAS 2432c9f290 Merge pull request #324 from kryofly/parse-common
Parsing: common options, reduce function scope
2018-01-06 15:11:30 -08:00
Gérald LONLAS 7f7d53adb7 Merge pull request #327 from gcarq/fix_profit_experimental
Fix profit experimental
2018-01-06 15:05:20 -08:00
kryofly 60ed4b9d1e --datadir <path> argument
This argument enables usage of different backtesting directories.
Useful if one wants compare backtesting performance over time.
2018-01-06 23:24:35 +01:00
Gerald Lonlas 83a999d16e Change Bollinger bands for qtpylib.bollinger_bands 2018-01-06 13:19:45 -08:00
Janne Sinivirta a29f3de025 fix variable names to pythonic 2018-01-06 21:21:56 +02:00
Janne Sinivirta 6ab0ec6aac only apply profit guarantee to sell_signal 2018-01-06 21:18:57 +02:00
kryofly 984204e380 let parse_args only parse, no continuation
This removes parse_args() from the call stack
It pushes down the test-mocking one level [from parse_args() to main()].
Moves parse_args into a more generic 'modules' parsing direction.
2018-01-06 11:21:09 +01:00
Gerald Lonlas 297166fcb9 Add 29 optional indicators populate_indicators() 2018-01-06 01:11:01 -08:00
kryofly e6e57e47cf plot script can take arguments 2018-01-06 09:55:15 +01:00
Janne Sinivirta bcde377019 Merge pull request #321 from gcarq/log-exceptions
Log exceptions
2018-01-06 10:14:57 +02:00
Samuel Husso 2d39759d34 pep8 fix 2018-01-06 10:08:25 +02:00
kryofly e4500af736 test case for common CLI parsing
Rearrange current tests.
2018-01-06 08:27:44 +01:00
Janne Sinivirta 41933c31ca Merge pull request #315 from kryofly/tests_jan05
tests cover more backtesting
2018-01-06 09:26:20 +02:00
kryofly 47675943ee split common command line args parsing
A new function parse_args_common() that only parses
common command line options. The returned object can
be composed to parse more arguments.
As is done by parse_args().
2018-01-06 07:39:05 +01:00
Gérald LONLAS 74a708b794 Merge pull request #312 from gcarq/fix_backtesting_header
Fix Backtesting header alignment
2018-01-05 19:30:04 -08:00
Janne Sinivirta 833c7f21af Merge pull request #306 from stephendade/timeoutfix
Unfilled order timeouts - now using timestamps from exchange
2018-01-05 18:04:27 +02:00
Janne Sinivirta f8eedc69dd Merge pull request #313 from seansan/patch-4
Add CCI
2018-01-05 18:04:08 +02:00
Samuel Husso 797324c35e Merge pull request #317 from gcarq/pyup-update-pymarketcap-3.3.143-to-3.3.145
Update pymarketcap to 3.3.145
2018-01-05 13:48:51 +02:00
Samuel Husso ae967a4f40 add test to handle analyze_ticker raising exception 2018-01-05 13:43:56 +02:00
pyup-bot 188fc69e56 Update pymarketcap from 3.3.143 to 3.3.145 2018-01-05 12:08:16 +01:00
Samuel Husso be8506b45e log exceptions, catch *all* exceptions when analysing ticker 2018-01-05 12:18:44 +02:00
kryofly 79fcd0b06c tests cover more backtesting 2018-01-05 10:44:10 +01:00
kryofly 421ccb23d3 split load tickerdata function 2018-01-05 10:20:48 +01:00
seansan f1969175cd Add CCI 2018-01-05 08:40:03 +01:00
Gerald Lonlas 7fd6d089c0 Fix Backtesting header alignment 2018-01-04 23:14:10 -08:00
Gérald LONLAS 552fba773d Merge pull request #310 from gcarq/pyup-update-pytest-3.3.1-to-3.3.2
Update pytest to 3.3.2
2018-01-04 22:38:37 -08:00
Gérald LONLAS 8e272cfd53 Merge pull request #311 from gcarq/use_named_arguments
Use named argument for backtest()
2018-01-04 22:38:25 -08:00
Gérald LONLAS 36fbe54634 Merge pull request #307 from gcarq/pyup-update-pymarketcap-3.3.141-to-3.3.143
Update pymarketcap to 3.3.143
2018-01-04 22:38:04 -08:00
Gerald Lonlas 90017998fc Use named argument for backtest() 2018-01-04 22:27:55 -08:00
Stephen Dade ebe95ba1e1 Open order times should be strings, not datetime objectsy 2018-01-05 15:12:13 +11:00
pyup-bot c803762704 Update pytest from 3.3.1 to 3.3.2 2018-01-05 01:28:53 +01:00
pyup-bot f8d8f3347a Update pymarketcap from 3.3.141 to 3.3.143 2018-01-04 20:08:11 +01:00
Stephen Dade d4fcc38a57 Unfilled order timeouts - now using timestamps from exchange 2018-01-05 01:39:01 +11:00
Janne Sinivirta c60ef181dc Merge pull request #297 from jblestang/add_stoploss_and_use_sell_profit_only_to_hyperopt
Add stoploss, sell_only_profit and use_sell_signal conf parameters to backtest function
2018-01-04 13:33:01 +02:00
Samuel Husso db4ad2f6f9 Merge pull request #295 from stephendade/Ordertimeout
Added order timeout handling
2018-01-04 09:26:16 +02:00
Stephen Dade b5d2cfecc7 Unfilled Order timeout - better documentation and variable naming 2018-01-04 10:35:57 +11:00
Jean-Baptiste LE STANG 75955fcc04 Add a unitest and fix pep8 2018-01-03 17:58:08 +01:00
Jean-Baptiste LE STANG 050e73d960 fix a typo in the description of get_ticker 2018-01-03 17:51:01 +01:00
Jean-Baptiste LE STANG 0f2d3adbbc applying pep8 2018-01-03 17:36:40 +01:00
Jean-Baptiste LE STANG ea6a1c629d fixing pep8 compliance 2018-01-03 11:50:30 +01:00
Jean-Baptiste LE STANG eb53a796e2 pep8 compliance 2018-01-03 11:35:54 +01:00
Jean-Baptiste LE STANG 2d273a8509 Update unittests 2018-01-03 11:30:24 +01:00
Stephen Dade 7169ad557f Correct documentation for opentradetimeout 2018-01-03 21:24:42 +11:00
Stephen Dade b4d6250d55 Added order timeout handling 2018-01-03 21:22:35 +11:00
Jean-Baptiste LE STANG 45f2d01895 - add a profit/loss counter
- the use of the sell_signal is conditional now (taken from the config)
2018-01-03 11:19:46 +01:00
Jean-Baptiste LE STANG c176ace889 Adding sell_profit_only and stoploss in hyperopt 2018-01-03 10:56:18 +01:00
Gérald LONLAS 1ce4613aad Merge pull request #296 from gcarq/update_documentation
Update documentation
2018-01-03 00:07:41 -08:00
Gerald Lonlas eb473842b8 Update documentation 2018-01-02 23:59:14 -08:00
Gérald LONLAS 407eaa0870 Merge pull request #279 from gcarq/revamp_documentations
Reorder and revamp the documentation
2018-01-02 23:48:49 -08:00
Gérald LONLAS 9b09b5aa29 Merge pull request #291 from gcarq/backtesting_speed_opt
Backtesting speed optimizations
2018-01-02 23:35:47 -08:00
Gerald Lonlas 70d1511f73 Update ISSUE_TEMPLATE.md and PULL_REQUEST_TEMPLATE.md 2018-01-02 23:34:26 -08:00
Gérald LONLAS 4a717f3df8 Merge pull request #294 from jblestang/add_trades_count_in_performance
Add trades count foreach pair in performance command
2018-01-02 23:03:30 -08:00
Gerald Lonlas cb7c36a512 Add Backtesting and Hyperopt documentation 2018-01-02 22:50:54 -08:00
Gerald Lonlas f37c495b90 Update the documentation from the PR review 2018-01-02 22:50:54 -08:00
Gerald Lonlas 284c6c4223 Reorder and revamp the documentation 2018-01-02 22:50:54 -08:00
Samuel Husso fd5497cfc7 Merge pull request #265 from gcarq/feature/experimental/force_profit_sell
Add experimental feature to sell only if we make a profit
2018-01-03 08:14:54 +02:00
Samuel Husso 208d3770da Merge pull request #292 from jblestang/fix_pair_black_list
Bug in blacklist pair handling
2018-01-03 07:54:18 +02:00
Jean-Baptiste LE STANG 01b49dc502 Merge branch 'develop' into add_trades_count_in_performance 2018-01-03 00:06:56 +01:00
Jean-Baptiste LE STANG fbb19e451d Adding the number of trades for each traded pair in the performance command 2018-01-03 00:06:50 +01:00
Jean-Baptiste LE STANG a1ffa4497d Merge branch 'develop' into fix_issue_278 2018-01-02 23:12:21 +01:00
Jean-Baptiste LE STANG e69f9dd029 Bad unittest detected reading coverage report, rewritten and bug found 2018-01-02 23:00:03 +01:00
Janne Sinivirta fed3024302 rewrite get_timeframe in backtesting 2018-01-02 21:54:31 +02:00
Janne Sinivirta dc2f048c98 make tuples smaller in backtesting loops 2018-01-02 21:52:47 +02:00
Samuel Husso f4ccd4609b Merge pull request #284 from jblestang/fix_issue_283
fixing the sorting issue in MarketSummary when using --dynamic-whitelist (issue #283)
2018-01-02 21:00:20 +02:00
Samuel Husso 1e3a29c049 Merge pull request #287 from gcarq/fix_tabulate
Improve backtesting result formatting
2018-01-02 19:00:54 +02:00
Janne Sinivirta 82e9ed2ac2 shorten table title to match table length 2018-01-02 17:53:47 +02:00
Janne Sinivirta ae52880f81 improve backtesting result formatting 2018-01-02 17:39:02 +02:00
Jean-Baptiste LE STANG 90236fb537 Fixing error log on inactive wallet 2018-01-02 15:17:23 +01:00
Jean-Baptiste LE STANG 55d0d27756 message too long, removing URL for now 2018-01-02 14:55:31 +01:00
Jean-Baptiste LE STANG d849694a70 Adding URL to market graph and number of trades/pair in /performance commande 2018-01-02 14:43:38 +01:00
Jean-Baptiste LE STANG 29987c3ff6 Adding the number of trades in the performance display 2018-01-02 14:32:13 +01:00
Jean-Baptiste LE STANG 5f696a0cce really fixing 2018-01-02 14:13:55 +01:00
Jean-Baptiste LE STANG 90d3c09536 fixing refresh argument ... 2018-01-02 14:13:40 +01:00
Jean-Baptiste LE STANG 3f65fc014e flake8 on tests 2018-01-02 13:46:16 +01:00
Jean-Baptiste LE STANG 5344b711ea Add two more unit tests for covering pair that are in a blacklist, and unknown pairs in the conf 2018-01-02 13:42:10 +01:00
Jean-Baptiste LE STANG a3e827c144 with flake8 code review 2018-01-02 12:18:26 +01:00
Jean-Baptiste LE STANG 52e267e864 fix for issue #283 2018-01-02 12:04:47 +01:00
Jean-Baptiste LE STANG 165781a545 force refresh is the value has never been set 2018-01-02 11:00:22 +01:00
Jean-Baptiste LE STANG e10a3d1f9d get_ticker can return a cached value 2018-01-02 10:56:42 +01:00
Samuel Husso 0c11d4443f Merge pull request #277 from stephendade/patch-1
Fixed pytest typo
2018-01-02 07:47:23 +02:00
Stephen 50be2fabbf Fixed pytest typo 2018-01-02 15:04:41 +11:00
jblestang 7a2e9ef535 Add fiat display in sell msg (#271)
* Display amount (fiat currency) in the sell message
* Display also base currency
* Adding more info in Buy Message, the stake amount, and the amount using FIAT Converter
* fix display style and width
* Fixing flake8
2018-01-01 14:21:43 -08:00
Gérald LONLAS 079f2e3609 Merge pull request #276 from jblestang/issue-273
Removing tilde and change profit to loss when negative profit is made
2018-01-01 14:19:29 -08:00
Jean-Baptiste LE STANG 0e0d613191 Removing tilde and change profit to loss when negative profit is made 2018-01-01 20:18:38 +01:00
Samuel Husso de68209f3b Revert "Make get_signals async. This should speed up create_trade calls by at least 10x. (#223)" (#275)
This reverts commit 6768658300.
See details in #PR266
2018-01-01 19:32:58 +01:00
Janne Sinivirta 59546b623e Merge pull request #269 from gcarq/pyup-update-pandas-0.21.1-to-0.22.0
Update pandas to 0.22.0
2018-01-01 07:47:59 +02:00
Gérald LONLAS 0a5463fee8 Merge pull request #267 from gcarq/update_config_example
Add pair_blacklist sample in config.json.example
2017-12-31 11:19:51 -08:00
pyup-bot cdfb18e9b4 Update pandas from 0.21.1 to 0.22.0 2017-12-31 14:21:50 +01:00
Gerald Lonlas 1f635d3793 Add pair_blacklist in config.example 2017-12-31 01:14:17 -08:00
Gerald Lonlas 714d77dbd8 Add expiremental feature to sell only if we make a profit 2017-12-30 18:14:10 -08:00
Gérald LONLAS 9803130848 Merge pull request #259 from gcarq/fix/issue-248
Fix issue #248: missing configuration when executing /forcesell
2017-12-30 17:28:16 -08:00
Samuel Husso ad44d8d42a Merge pull request #263 from jblestang/fix_issue_262
Fixing bug #262
2017-12-30 17:01:00 +02:00
Jean-Baptiste LE STANG 68f81b2abb autopep8 is going to be my new friend 2017-12-30 15:55:49 +01:00
Jean-Baptiste LE STANG 4945331093 Fixing the positional parameter naming + unit tests updated 2017-12-30 15:43:22 +01:00
jblestang 8411844d7e Implement pair_blacklist functionality (#257)
* Adding an optional black_list of pairs not to be traded

* applying the blacklist also when not using --dynamic-whitelist

* fix error retrieving pair in conf

* Refactoring the handling of whitelist among the various functions

* unit test to verify that black listed pairs are being removed from the pair_whitelist

* Fixing newly added unit tests in develop

* fixing flake8 code review

* fix code review from @garcq
2017-12-30 14:15:07 +01:00
Janne Sinivirta 00415d66a2 Merge pull request #260 from gcarq/increase_code_coverage
Increase code coverage
2017-12-30 14:02:33 +02:00
kryofly f7398e615a Improve backtesting tests (#256)
* test bugfix dataframe trimming

* flake8 (as usual)

* tests backtesting cleanup and bugfix

* flake8

* test backtesting::start()

* tests cleanup set() usage

* tests: add missing assert
2017-12-30 11:55:23 +01:00
Gerald Lonlas e81a9cbb17 Increase code coverage
Change log:
* Increase code coverage for test_exchange.py
* Move Exchange Unit tests files tests/exchange/
* Move RPC Unit tests files tests/rpc/
2017-12-29 23:37:02 -08:00
Gerald Lonlas c8c8c626b0 Fix issue #248: missing configuration when executing /forcesell
This is not a beautiful workaround, I am not proud of it,
but a redesigning of main.py and telegram.py will be
necessary for a better integration. Any better solution
is welcome.
2017-12-29 20:03:12 -08:00
Janne Sinivirta 9f5f0ddaaa Merge pull request #243 from gcarq/pyup-update-pymarketcap-3.3.139-to-3.3.141
Update pymarketcap to 3.3.141
2017-12-29 19:31:50 +02:00
Janne Sinivirta 80e1e64eae Merge pull request #249 from kryofly/tests_dec28
tests for dataframe, whitelist and backtesting
2017-12-29 19:14:57 +02:00
kryofly 37613fc056 flake8 2017-12-29 17:53:58 +01:00
Janne Sinivirta 57c6aefe38 Merge branch 'develop' into tests_dec28 2017-12-29 16:34:00 +02:00
Janne Sinivirta 133c467cf4 Merge branch 'develop' into tests_dec28 2017-12-29 16:33:12 +02:00
Janne Sinivirta 900cab4b42 Merge pull request #253 from kryofly/sell_signal
execute sell if get_signal OR ROI reached
2017-12-29 16:31:37 +02:00
Janne Sinivirta f9cc556971 Merge branch 'develop' into sell_signal 2017-12-29 16:27:04 +02:00
Janne Sinivirta f2ce367cec Merge branch 'develop' into sell_signal 2017-12-29 16:26:23 +02:00
Janne Sinivirta fba9cbcff6 Merge pull request #247 from gcarq/add_unittest
Refactor Optimize tests, and add more unit tests
2017-12-29 16:23:36 +02:00
kryofly 3e0458da7d flake8 2017-12-29 09:40:24 +01:00
Gerald Lonlas 0d605d2396 Refactor Optimize tests, and add more unit tests 2017-12-28 22:32:48 -08:00
Janne Sinivirta 145583f0b7 Merge pull request #244 from jblestang/fix_daily_profit
Fixing daily profit,
2017-12-29 06:05:25 +02:00
kryofly 847dde0d65 execute sell if get_signal OR ROI reached 2017-12-29 00:07:54 +01:00
kryofly ab112581a7 tests: anal stretching to accomodate flake8 2017-12-28 20:05:33 +01:00
kryofly f48f5d0f31 tests for dataframe, whitelist and backtesting 2017-12-28 15:58:19 +01:00
Janne Sinivirta 0abf0b0e39 Merge pull request #242 from gcarq/backtesting-unittests
Backtesting and hyperopt unit tests
2017-12-28 12:45:28 +02:00
pyup.io bot 965616b214 Update sqlalchemy from 1.1.15 to 1.2.0 (#245) 2017-12-28 10:11:32 +01:00
Janne Sinivirta a36fd00f6a also print dot when hyperopt eval result is fail 2017-12-28 06:40:11 +02:00
Janne Sinivirta 7f44ba6df4 unit tests for optimize.hyperopt 2017-12-28 06:39:56 +02:00
Janne Sinivirta 7b0beb0afa cleanups 2017-12-28 06:36:18 +02:00
Janne Sinivirta ae0a1436e2 match test files to prod files for backtesting/hyperopt 2017-12-28 06:35:09 +02:00
Jean-Baptiste LE STANG 8537e9f40f CI flake8 error 2017-12-27 21:33:42 +01:00
Jean Baptiste LE STANG d61d88559c Fixing daily profit, taking into account the time part of the date (removing it in fact) 2017-12-27 21:06:05 +01:00
Janne Sinivirta 9b4c0f01f2 more unit tests for backtesting 2017-12-27 17:39:54 +02:00
Gérald LONLAS 6c8253a4f5 Add more unittest (#241) 2017-12-27 11:41:11 +01:00
pyup-bot 6464373636 Update pymarketcap from 3.3.139 to 3.3.141 2017-12-27 10:19:45 +01:00
Janne Sinivirta dcd0a0ec61 Merge pull request #239 from glonlas/feature/value_in_fiat
Display profits in fiat
2017-12-27 11:19:38 +02:00
Gerald Lonlas ff6b0fc1c9 Display profits in fiat 2017-12-26 19:44:19 -08:00
Michael Egger a514b92dcf catch MIN_TRADE_REQUIREMENT_NOT_MET as non-critical exception (#237)
* add MIN_TRADE_REQUIREMENT_NOT_MET to response validation

* implement test
2017-12-26 09:39:29 +01:00
Janne Sinivirta de33d69eed Lint fixes (#236)
* correct docstring

* add type annotation to trade_count_lock

* fix indentations

* allow globals in hyperopt.py

* fix import order

* simplify asserts

* use proper variable name

* simplify condition

* fix path operation that fails on windows
2017-12-25 12:07:50 +01:00
Janne Sinivirta 9959d53f5e Logging improvements to Hyperopt (#235)
* make log texts go on new line

* remove unnecessary fields from hyperopt log messages

* shorten log text in hyperopt

* consider making zero trades a failed hyperopt eval

* only log from hyperopt when result improves

* remove unnecessary temp variables

* remove unused result data variables

* remove unused import

* fix an outdated comment
2017-12-25 08:18:34 +01:00
Pan Long 6768658300 Make get_signals async. This should speed up create_trade calls by at least 10x. (#223) 2017-12-25 07:01:01 +01:00
Samuel Husso 433bf409f4 Merge pull request #232 from gcarq/tweak-hyperopt
Tweak Hyperopt
2017-12-23 19:25:45 +02:00
Janne Sinivirta 353b0d2d34 balance hyperopt objective to adjusted profit calculations 2017-12-23 19:18:28 +02:00
Janne Sinivirta e644d57dbe log should state profit is in BTC to avoid confusion 2017-12-23 19:00:49 +02:00
Janne Sinivirta 50e7cef5f3 remove commented-out code 2017-12-23 19:00:49 +02:00
Janne Sinivirta 1058820e1b just pass stake_amount instead of the whole config 2017-12-23 19:00:49 +02:00
Janne Sinivirta 24bc3a8390 show more digits for profits 2017-12-23 15:11:19 +02:00
Janne Sinivirta 5309ea3820 use newline for each log result for readability 2017-12-23 15:11:19 +02:00
Janne Sinivirta a063680d32 calculate log line only if really logging 2017-12-23 15:11:19 +02:00
Janne Sinivirta 10cf2ce853 remove unnecessary confusing division 2017-12-23 15:11:19 +02:00
Janne Sinivirta 871357a2e3 just require positive results 2017-12-23 15:11:19 +02:00
Janne Sinivirta efe0d77dbb Merge pull request #231 from gcarq/fix/hyperopt-filter-nan
filter nan values from total_profit and avg_profit
2017-12-23 15:07:40 +02:00
Samuel Husso 8d93363655 filter nan values from total_profit and avg_profit 2017-12-23 09:21:04 +02:00
Samuel Husso b6dd9dd227 Merge pull request #227 from gcarq/create-contribute-guideline
Create contribution guideline
2017-12-22 19:00:49 +02:00
Janne Sinivirta 95c6ada2ad link to contribution guide from README.md 2017-12-22 14:31:08 +02:00
Janne Sinivirta 11585f9581 Create contribution guideline 2017-12-22 14:29:31 +02:00
Janne Sinivirta 8085a7b237 Merge pull request #215 from seansan/patch-1
add % in status table for profit
2017-12-22 14:09:06 +02:00
Janne Sinivirta c99e2c12ba Merge branch 'develop' into patch-1 2017-12-22 14:05:09 +02:00
Janne Sinivirta 44a4ff0cb2 Merge branch 'develop' into patch-1 2017-12-22 13:58:13 +02:00
Janne Sinivirta f300af0fe2 Merge pull request #200 from glonlas/fix_fees_calculation
Fix the fee calculation
2017-12-22 13:55:02 +02:00
Samuel Husso ff186c7f65 Merge pull request #218 from glonlas/fix_hyperopt
Fix hyperopt when using MongoDB
2017-12-22 10:48:45 +02:00
Gerald Lonlas 41e22657e4 Fix hyperopt when using MongoDB 2017-12-21 19:20:47 -08:00
Samuel Husso 974815cb14 Merge pull request #220 from seansan/patch-2
added Minimal (advised) system requirements
2017-12-21 10:16:47 +02:00
seansan 33beab9c47 added Minimal (advised) system requirements 2017-12-21 09:13:26 +01:00
Gerald Lonlas d258118b0a Fix the fee calculation, backtesting, and hyperopt fee calculation and avg_profit 2017-12-20 20:18:41 -08:00
seansan 4dab39ed9e add % in status table for profit 2017-12-20 13:58:18 +01:00
Janne Sinivirta 33293d5cdd Merge pull request #205 from gcarq/fix/travis-curl-redirect
pass follow redirects for curl to fix travis
2017-12-19 09:26:42 +02:00
Samuel Husso 285308dcbe pass follow redirects for curl to fix travis 2017-12-19 08:27:52 +02:00
Janne Sinivirta c8fb6c4661 More lint fixes (#198)
* autopep fixes

* remove unused imports

* fix plot_dataframe.py lint warnings

* make pep8 error fails the build

* two more line breakings

* matplotlib.use() must be called before pyplot import
2017-12-18 17:36:00 +01:00
Janne Sinivirta 1a556198b2 Merge pull request #203 from gcarq/travis/fix-ssl
use curl instead of wget (see travis-ci/issues/5059)
2017-12-18 11:09:50 +02:00
Samuel Husso 98650acca0 use curl instead of wget (see travis-ci/issues/5059) 2017-12-18 10:26:48 +02:00
Samuel Husso 123f2781a1 Merge pull request #202 from gcarq/cache-talib
Cache TAlib
2017-12-18 10:06:24 +02:00
Janne Sinivirta 92f6db5bd7 fix checking for cached ta-lib 2017-12-18 09:36:29 +02:00
Janne Sinivirta e5f8c1e75d cache ta-lib folder, skip build if cache exists 2017-12-18 09:29:17 +02:00
Janne Sinivirta 4c0a316e3e enable sudo for installing talib 2017-12-18 09:20:52 +02:00
Gerald Lonlas d613d63fdc Fix the fee calculation 2017-12-17 23:01:34 -08:00
Janne Sinivirta e3941cde7e move wgetting and building of talib to an sh file 2017-12-18 07:15:14 +02:00
Janne Sinivirta 642422d5c4 cache pip dependencies (#199) 2017-12-17 21:19:50 +01:00
Samuel Husso 117ec4e64d Merge pull request #195 from gcarq/feature/travis-smoke-tests
add smoke tests to run a round of hyperopt and backtesting
2017-12-17 15:45:14 +02:00
Samuel Husso 0219584bfe Merge pull request #197 from gcarq/fix_plotting
Fix plotting broken by refactoring
2017-12-17 15:43:01 +02:00
Janne Sinivirta d3947fc893 create config.json for backtesting 2017-12-17 15:19:35 +02:00
Janne Sinivirta fe0c26f536 create config.json for hyperopt 2017-12-17 15:13:39 +02:00
Janne Sinivirta e83e4909a0 install freqtrade module for hyperopting 2017-12-17 15:01:11 +02:00
Janne Sinivirta ed05a1db9d Merge branch 'develop' into feature/travis-smoke-tests 2017-12-17 14:51:26 +02:00
Janne Sinivirta 21a11f5589 run pytest, hyperopt and backtesting in parallel 2017-12-17 14:45:31 +02:00
Janne Sinivirta 6288adfefd fix plotting broken by refactoring 2017-12-17 14:14:57 +02:00
Janne Sinivirta 6a1caafb7a Merge pull request #196 from gcarq/fix/hyperopt
fix hyperopt not getting default ticker_interval
2017-12-17 13:50:25 +02:00
Samuel Husso ce51749177 fix hyperopt not getting default ticker_interval 2017-12-17 12:34:26 +02:00
Samuel Husso a68ca31684 add smoke test commands under script block 2017-12-17 12:01:08 +02:00
Samuel Husso 5f1b9943d1 add smoke tests to run a round of hyperopt and backtesting 2017-12-17 11:55:34 +02:00
Janne Sinivirta 155ed4e501 Merge pull request #191 from gcarq/feature/add-systemd-service-file
add systemd service file
2017-12-17 07:43:20 +02:00
Janne Sinivirta 80ef2cfed4 Merge pull request #193 from gcarq/feature/ci-enforce-pep8
CI: enforce PEP8 conform code
2017-12-17 07:42:23 +02:00
Janne Sinivirta 5efc417690 Merge pull request #192 from gcarq/feature/forcesell-handle-open-orders
/forcesell: handle trades with open orders
2017-12-17 07:41:51 +02:00
Gérald LONLAS 14868615d5 Add mock to improve backtesting tests (#194) 2017-12-17 00:24:21 +01:00
Gérald LONLAS 512fcdbcb1 Allow user to update testdata files with parameter --refresh-pairs-cached (#174) 2017-12-16 15:42:28 +01:00
gcarq 6f2caf9698 invoke flake8 after success 2017-12-16 03:44:49 +01:00
gcarq a395a14eeb adapt README 2017-12-16 03:40:06 +01:00
gcarq 95fe0f4dec fix pep8 warnings 2017-12-16 03:39:47 +01:00
gcarq f6d85e021f add setup.cfg to configure flake8 2017-12-16 03:28:59 +01:00
gcarq 597f08e2a2 update README 2017-12-16 03:00:51 +01:00
gcarq df4784e7b9 add service file 2017-12-16 03:00:43 +01:00
gcarq ddd3d2d0a9 ignore cancelled order during trade state update 2017-12-16 02:36:43 +01:00
gcarq cb4ecfd3a3 move function 2017-12-16 01:37:06 +01:00
gcarq f4b59492ab fix NoneType issue 2017-12-16 01:31:15 +01:00
gcarq ae37f49b51 /forcesell: handle trades with open orders 2017-12-16 01:09:07 +01:00
gcarq 6e68315d2c reorder imports 2017-12-15 23:58:21 +01:00
gcarq c1c9dd03ce /daily: fix identation and simplify loops 2017-12-15 23:56:02 +01:00
Gérald LONLAS e00f02b603 Improve telegram /profit command (#188) 2017-12-15 17:19:00 +01:00
pyup.io bot 9f907d5b5e Update python-bittrex from 0.2.1 to 0.2.2 (#189) 2017-12-15 16:10:10 +01:00
Samuel Husso 6729574201 Merge pull request #186 from glonlas/update_daily_command
Improve  /daily command
2017-12-15 08:19:02 +02:00
Gerald Lonlas 2a2af4878e Update /daily command, reorder telegram menu, limit /daily profit at 8 decimals 2017-12-14 21:18:52 -08:00
Michael Egger bfb3e09d1d raise ContentDecodingError if bittrex responds with NO_API_RESPONSE (#183) 2017-12-14 20:27:04 +01:00
Pan Long 89ee0654f4 Use ENTRYPOINT instead of CMD so additional arguments can be supplied for docker run. (#184) 2017-12-14 18:41:40 +01:00
Gérald LONLAS 2ac8b685d6 Add param for Dry run to use a DB file instead of memory (#182) 2017-12-14 15:10:11 +01:00
Samuel Husso 4b38100ae2 Merge pull request #175 from gcarq/pyup-update-pandas-0.21.0-to-0.21.1
Update pandas to 0.21.1
2017-12-13 08:18:31 +02:00
pyup-bot d6c14d5258 Update pandas from 0.21.0 to 0.21.1 2017-12-13 06:18:06 +01:00
Samuel Husso cb09cabbdd Merge pull request #171 from stephendade/dailymsg
Added daily profit telegram command
2017-12-12 19:42:31 +02:00
Janne Sinivirta 77023c0ecf Merge pull request #169 from jblestang/fix_ticker_interval
Fix ticker interval
2017-12-12 17:21:55 +02:00
Stephen Dade 0b18c93d19 Daily profit command - better message formatting and minor fixes 2017-12-12 19:41:25 +11:00
Jean-Baptiste LE STANG 0617753a7f Adding a test unit for 1 minute ticker interval 2017-12-11 22:11:06 +01:00
Janne Sinivirta b77fad6e5f Merge pull request #173 from glonlas/autoselect_top_currencies
Allow to change the number of currencies used by dynamic-whitelist
2017-12-11 18:04:10 +02:00
Gerald Lonlas 90bf6f2d4a Remove unecessary import 2017-12-11 00:07:36 -08:00
Gerald Lonlas ef7646417b Allow to change the number of currencies used by dynamic-whitelist 2017-12-11 00:01:27 -08:00
Samuel Husso 01874e379f Merge pull request #172 from gcarq/new_pair_set
New currency pair set
2017-12-11 09:33:05 +02:00
Janne Sinivirta 7afd8da28f fix a broken unit test due to changing test dataset 2017-12-10 13:56:39 +02:00
Janne Sinivirta 3d532c6015 update backtest data to match pairs in config.json.example 2017-12-10 11:17:01 +02:00
Janne Sinivirta a692ef6715 update example coins to be from monthly max volume list 2017-12-10 11:16:28 +02:00
Stephen Dade ccb8c3c352 Added daily profit telegram command 2017-12-10 17:32:40 +11:00
toto 18f01113c2 use the CLI arguments as the ticker interval 2017-12-09 11:51:53 +01:00
toto f7def09dec fix for the ticker interval set by default to 5 2017-12-09 11:39:26 +01:00
Janne Sinivirta 82bf0be3e2 Merge pull request #168 from gcarq/pyup-update-python-telegram-bot-8.1.1-to-9.0.0
Update python-telegram-bot to 9.0.0
2017-12-09 07:33:36 +02:00
pyup-bot 212f4fdd95 Update python-telegram-bot from 8.1.1 to 9.0.0 2017-12-08 23:21:03 +01:00
Samuel Husso a5058ff999 Merge pull request #164 from gcarq/pyup-update-pytest-3.3.0-to-3.3.1
Update pytest to 3.3.1
2017-12-06 09:07:18 +02:00
pyup-bot ea1c16f2ac Update pytest from 3.3.0 to 3.3.1 2017-12-06 05:15:53 +01:00
Janne Sinivirta 67337fadaa Merge pull request #157 from gcarq/pyup-update-pytest-3.2.5-to-3.3.0
Update pytest to 3.3.0
2017-12-03 10:02:03 +02:00
Janne Sinivirta 94c1d66e59 Merge pull request #159 from gcarq/pyup-update-tabulate-0.8.1-to-0.8.2
Update tabulate to 0.8.2
2017-12-03 10:01:29 +02:00
Janne Sinivirta 510e6edfbf Merge pull request #156 from gcarq/pyup-update-arrow-0.10.0-to-0.12.0
Update arrow to 0.12.0
2017-12-03 09:40:02 +02:00
Janne Sinivirta e8c31142ae Merge pull request #154 from gcarq/hyperopt/simplify-logging
Hyperopt/simplify logging
2017-12-03 09:39:45 +02:00
pyup-bot 71c780a530 Update tabulate from 0.8.1 to 0.8.2 2017-12-03 08:34:08 +01:00
pyup-bot 7e579de163 Update pytest from 3.2.5 to 3.3.0 2017-12-03 08:34:01 +01:00
pyup-bot dd1a52c534 Update arrow from 0.10.0 to 0.12.0 2017-12-03 08:33:57 +01:00
Janne Sinivirta e815a43164 Merge pull request #137 from gcarq/pyup-initial-update
Initial Update
2017-12-03 09:33:50 +02:00
Janne Sinivirta 2f17706e76 Merge pull request #155 from gcarq/maintenance/remove-btc-time
remove BTC_TIME
2017-12-02 15:29:10 +02:00
Samuel Husso 86a94798dd BTC_TIME will be removed from bittrex Dec 8th 2017-12-02 15:06:33 +02:00
Samuel Husso a7cca4985e omit hyperopt output if total_profit doesn't go pass threashold (3) 2017-12-02 01:32:23 +02:00
Samuel Husso 965c075362 disable info logging on hyperopt.tpe 2017-12-02 00:21:46 +02:00
Janne Sinivirta 05d7746f62 Revert "Update networkx from 1.11 to 2.0"
This reverts commit 0502bd3c2d.
2017-12-01 21:13:02 +02:00
Samuel Husso 688326b58c Merge pull request #146 from gcarq/feature/integrate-backtesting
integrate backtesting/hyperopt into freqtrade.optimize
2017-11-30 08:19:59 +02:00
gcarq 0c9993cc89 convert bash scripts to python scripts 2017-11-25 15:40:19 +01:00
gcarq 0c35e6ad19 minor changes 2017-11-25 03:28:52 +01:00
gcarq 68521ea46c adapt README 2017-11-25 03:28:39 +01:00
gcarq 2fe11cd77a add helper scripts for mongodb 2017-11-25 03:28:18 +01:00
gcarq e27a6a7a91 add mongodb support for hyperopt parallelization 2017-11-25 02:04:37 +01:00
gcarq 5bf583cba4 remove unused imports 2017-11-25 01:23:18 +01:00
gcarq a23fce519d pretty print hyperopt results 2017-11-25 01:22:36 +01:00
gcarq 7f3f127165 remove custom env from .travis.yml 2017-11-25 01:13:28 +01:00
gcarq 9ff1f05e66 add --epochs to hyperopt subcommand 2017-11-25 01:12:44 +01:00
gcarq b9c4eafd96 integrate hyperopt and implement subcommand 2017-11-25 01:04:11 +01:00
gcarq 7fa5846c6b move hyperopt to freqtrade.optimize.hyperopt 2017-11-25 00:30:39 +01:00
gcarq 3b37f77a4d move backtesting to freqtrade.optimize.backtesting 2017-11-24 23:58:35 +01:00
Michael Egger 858d2329e5 add experimental flag support and add use_sell_signal (#143)
* add use_sell_signal to config schema

* check use_sell_signal

* set use_sell_signal to false
2017-11-24 21:58:00 +01:00
Mathieu Favréaux 371ee1e457 In backtesting, ensure we don't buy the same pair again before selling (#139)
* in backtesting, ensure we don't buy before we sell

* no overlapping trades only if max_open_trades > 0

* --limit-max-trades now --realistic-simulation
2017-11-24 21:09:44 +01:00
Geka000 cfbfe90aa0 keyboard markup for telegram bot (#142) 2017-11-24 20:54:50 +01:00
Michael Egger fd30f5dc59 Merge branch 'develop' into pyup-initial-update 2017-11-23 21:49:56 +01:00
pyup-bot 0502bd3c2d Update networkx from 1.11 to 2.0 2017-11-23 21:07:43 +01:00
pyup-bot 3ce7ef5e8b Update pytest from 3.2.3 to 3.2.5 2017-11-23 21:07:42 +01:00
pyup-bot 2324aa0782 Update scipy from 0.19.1 to 1.0.0 2017-11-23 21:07:40 +01:00
pyup-bot 6a57a8da12 Update scikit-learn from 0.19.0 to 0.19.1 2017-11-23 21:07:39 +01:00
pyup-bot 9276f3202c Update pandas from 0.20.3 to 0.21.0 2017-11-23 21:07:37 +01:00
pyup-bot a6598997e2 Update sqlalchemy from 1.1.14 to 1.1.15 2017-11-23 21:07:36 +01:00
gcarq 82913cd3f4 upgrade python-bittrex to 0.2.1 2017-11-23 20:53:13 +01:00
gcarq be6939ee8a use 8 digits of precision for amount and rate in formatting 2017-11-23 20:52:07 +01:00
Samuel Husso 7ba4a5d24b Merge pull request #136 from gcarq/stoploss_tweak
Stoploss tweak
2017-11-23 19:54:08 +02:00
Janne Sinivirta 371e6d99c9 set stoploss to -10% 2017-11-23 18:43:19 +02:00
Janne Sinivirta 84b105c82b fix invalid json in example config 2017-11-23 18:41:25 +02:00
Janne Sinivirta c6def418cf Merge pull request #135 from rybolov/develop
Better buy and sell strategy
2017-11-23 18:25:56 +02:00
Michael Smith 5fce2c5712 Better buy and sell strategy:
Buy if at the low end of normal range and the price is increasing.
Buy into extreme gains regardless of if it's on the low part of the range.
Avoid buying when the price is on a long decrease even if it's low.
Sell anytime the price is above the top end of normal range and the momentum slows.
Sell on an extreme drop.
2017-11-23 22:33:41 +08:00
Janne Sinivirta aacd7d8987 Merge pull request #131 from gcarq/feature/backtesting-max-open-trades
implement trade count lock for backtesting
2017-11-23 16:16:43 +02:00
gcarq 4a707d7452 add --limit-max-trades 2017-11-23 00:25:06 +01:00
Janne Sinivirta 21551b3c40 Merge pull request #133 from gcarq/feature/fix-buy-amount-calc
fix LIMIT_BUY amount calculation
2017-11-22 22:31:25 +02:00
gcarq 7727f2cc8f implement test 2017-11-22 21:02:36 +01:00
gcarq 9a87dcf0a1 dont apply fees on trade creation 2017-11-22 21:01:44 +01:00
gcarq 9136e64d89 force flush in create_trade and execute_sell (fixes #128) 2017-11-22 20:51:25 +01:00
Samuel Husso 765a762ccf Merge pull request #122 from gcarq/feature/fix-signal-handling
fix signal handling
2017-11-22 13:38:57 +02:00
gcarq 02ca2ed585 implement trade count lock for backtesting 2017-11-21 22:33:34 +01:00
gcarq f3ba3ddd54 move buy_price and sell_price to plotting script 2017-11-21 20:41:49 +01:00
gcarq 65ce948b0b catch ValueErrors from analyze_ticker (fixes #123) 2017-11-21 20:37:29 +01:00
gcarq 383a9f6eeb catch BaseException to force stdout flush when process dies 2017-11-21 20:24:52 +01:00
Janne Sinivirta 43dda9c9cf Merge pull request #125 from gcarq/conf-update
update conf example
2017-11-21 09:38:25 +02:00
Samuel Husso 7a44a1d1c1 match example config to backtest_conf and update README to fix #124 2017-11-21 07:37:31 +02:00
gcarq 5d934cd5b6 enhance open order formatting in status handle 2017-11-20 23:33:52 +01:00
gcarq 788cda4925 add missing import 2017-11-20 22:26:32 +01:00
gcarq 55a69e4a45 use normal program flow to handle interrupts 2017-11-20 22:15:19 +01:00
gcarq 1931d31147 Merge tag '0.14.3' into develop
0.14.3
2017-11-20 20:01:23 +01:00
gcarq e9dbdc9247 Merge branch 'release/0.14.3' 2017-11-20 20:01:18 +01:00
gcarq 86b6c6f334 version bump 2017-11-20 20:01:10 +01:00
gcarq cd5afd6ff4 use jsonschema regex pattern for whitelist format and enhance validation error messages (closes #120) 2017-11-20 19:37:25 +01:00
Janne Sinivirta d88cc084e6 align numbers in hyperopt print out (#119) 2017-11-20 10:22:11 +01:00
Jeff Pipas 5deaebf0c2 Tests now use UTC time with arrow instead of datetime (#117)
* fixing tests to use arrow-utc

* removing datetime import
2017-11-19 04:58:35 +01:00
gcarq 19734ad863 set bootstrap_retries to infinite (fixes #113) 2017-11-18 22:23:05 +01:00
gcarq b16ccb9919 handle requests exception in validate_pairs 2017-11-18 22:22:45 +01:00
gcarq d41837817c move logging to freqtrade.rpc 2017-11-18 21:43:21 +01:00
gcarq 3ab14dfe39 add middleware to expose common functionality for multiple rpc implementations 2017-11-18 21:30:31 +01:00
Michael Egger 4a91ecd91a Merge pull request #115 from gcarq/pylint_cleanups
Pylint cleanups
2017-11-18 16:00:21 +01:00
Samuel Husso a3da2911e8 Merge pull request #114 from gcarq/new_algo
New buy strategy
2017-11-18 13:09:40 +02:00
Janne Sinivirta 6f5b418f0b small balancing to hyperopt objective 2017-11-18 10:24:18 +02:00
Janne Sinivirta 57691c82b1 whitelist TA-lib in pylint 2017-11-18 10:13:14 +02:00
Janne Sinivirta 37a74b38ba more little pylint fixes 2017-11-18 10:09:19 +02:00
Janne Sinivirta 9ab81a987d fix pylint warnings in test_main.py 2017-11-18 09:58:55 +02:00
Janne Sinivirta 4b08e3d571 fix pylint warnings in __init__ files 2017-11-18 09:58:29 +02:00
Janne Sinivirta 187fea0c28 disable bunch of meaningless pylint warnings 2017-11-18 09:45:01 +02:00
Janne Sinivirta 4e54b27398 use parentheses for multiline string instead of backslash 2017-11-18 09:44:28 +02:00
Janne Sinivirta aced5cc3ba rename variable to remove Mypy warning of type error 2017-11-18 09:43:42 +02:00
Janne Sinivirta 669ec30413 remove unused import 2017-11-18 09:34:57 +02:00
Janne Sinivirta 0082b7abdd add missing module and class docstring 2017-11-18 09:34:32 +02:00
Janne Sinivirta 7903f3a546 fix test name 2017-11-18 09:19:22 +02:00
Janne Sinivirta ec75586bdd new buy strategy 2017-11-18 08:45:57 +02:00
Janne Sinivirta df9902d6a4 Merge pull request #107 from gcarq/feature/add-backtesting-subcommand
add backtesting subcommand and refresh test data
2017-11-18 08:13:42 +02:00
Janne Sinivirta 315919cdd6 fix platform dependent bug in argparse test 2017-11-18 08:07:37 +02:00
gcarq 63c95a3546 modify trade life cycle (should fix #112) 2017-11-17 20:17:29 +01:00
gcarq 59d04d1d0c catch TelegramError (fixes #113) 2017-11-17 19:49:03 +01:00
gcarq d1cc9e868b adapt README 2017-11-17 19:03:08 +01:00
gcarq 14de46576b use load_backtesting_data 2017-11-17 18:23:40 +01:00
gcarq bdff29a472 remove code duplicates 2017-11-17 18:17:59 +01:00
gcarq 8655c6c264 reduce backtest data samples to 10 2017-11-17 18:15:25 +01:00
gcarq 3f4e4a23a0 add argparse handling tests 2017-11-17 18:15:24 +01:00
gcarq b682262486 refactor argparse handling 2017-11-17 18:15:24 +01:00
gcarq 5be7be6189 adapt tests 2017-11-17 18:15:24 +01:00
gcarq 3475a07522 fetching new testing data for oneMin and fiveMin intervals 2017-11-17 18:15:24 +01:00
gcarq fb7ea169d4 fix some formatting issues 2017-11-17 18:13:34 +01:00
gcarq 5469293e5f use tabulate to format backtesting result 2017-11-17 18:13:02 +01:00
gcarq 9b644b0305 add --ticker-interval 2017-11-17 18:09:55 +01:00
gcarq 0df1404d6a fix typo 2017-11-17 18:09:55 +01:00
gcarq bb4a9ed20f implement backtest subcommand 2017-11-17 18:09:55 +01:00
Samuel Husso 77887d6fbc Merge pull request #111 from gcarq/memoryfix-hyperopt
Memory fix hyperopt
2017-11-17 18:41:38 +02:00
Janne Sinivirta d89db50465 avoid copy operation due to memory consumption 2017-11-17 12:30:54 +02:00
Janne Sinivirta 632d00e01d move price point calculations out from populate functions 2017-11-17 12:30:03 +02:00
Janne Sinivirta 2a56031cdc remove unnecessary line 2017-11-17 12:30:03 +02:00
Janne Sinivirta 16d412323c add a little snippet to allow running line_profiler with hyperopt 2017-11-16 20:43:24 +02:00
Janne Sinivirta 27a6b29c80 move time diff calculation out of a loop 2017-11-16 20:43:24 +02:00
Janne Sinivirta 5d1f874041 switch ix to loc, ix is apparently deprecated 2017-11-16 20:43:24 +02:00
Janne Sinivirta 174122a09b remove unnecessary calculation 2017-11-16 20:38:59 +02:00
Janne Sinivirta 1b6a60ecb2 refactor backtesting to avoid recalculating indicators in hyperopt 2017-11-16 20:38:46 +02:00
Michael Egger 1ccb266032 Merge pull request #104 from gcarq/sell_signal
Add sell_signal support
2017-11-16 17:02:24 +01:00
Janne Sinivirta a963f1820c rename should_sell to min_roi_reached 2017-11-16 16:53:34 +01:00
Janne Sinivirta b9983149ef plug sell strategy to backtesting 2017-11-16 16:53:34 +01:00
Janne Sinivirta c1ef3f526c remove unnecessary comparison 2017-11-16 16:53:34 +01:00
Janne Sinivirta 6b7afb80b2 fix failing test 2017-11-16 16:53:34 +01:00
Janne Sinivirta 0b8afa12e9 exit strategy after roi check 2017-11-16 16:53:34 +01:00
Janne Sinivirta 1db0a7d4ce populate sell signal 2017-11-16 16:53:34 +01:00
Janne Sinivirta c12a9ebd92 make signal getting parametrized 2017-11-16 16:53:34 +01:00
gcarq d86dcc4752 check if result exists in get_ticker (fixes #106) 2017-11-16 16:39:06 +01:00
gcarq 0bc96241d5 rework exception handling (fixes #108) 2017-11-16 16:14:43 +01:00
gcarq a0bb7a61e6 Merge tag '0.14.2' into develop
0.14.2
2017-11-16 00:40:48 +01:00
177 changed files with 25138 additions and 3029 deletions
+2 -1
View File
@@ -2,4 +2,5 @@
omit =
scripts/*
freqtrade/tests/*
freqtrade/vendor/*
freqtrade/vendor/*
freqtrade/__main__.py
+9
View File
@@ -4,3 +4,12 @@ Dockerfile
.dockerignore
config.json*
*.sqlite
.coveragerc
.eggs
.github
.pylintrc
.travis.yml
CONTRIBUTING.md
MANIFEST.in
README.md
freqtrade.service
+32
View File
@@ -0,0 +1,32 @@
## Step 1: Have you search for this issue before posting it?
If you have discovered a bug in the bot, please [search our issue tracker](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue).
If it hasn't been reported, please create a new issue.
## Step 2: Describe your environment
* Python Version: _____ (`python -V`)
* CCXT version: _____ (`pip freeze | grep ccxt`)
* Branch: Master | Develop
* Last Commit ID: _____ (`git log --format="%H" -n 1`)
## Step 3: Describe the problem:
*Explain the problem you have encountered*
### Steps to reproduce:
1. _____
2. _____
3. _____
### Observed Results:
* What happened?
* What did you expect to happen?
### Relevant code exceptions or logs:
```
// paste your log here
```
+15
View File
@@ -0,0 +1,15 @@
Thank you for sending your pull request. But first, have you included
unit tests, and is your code PEP8 conformant? [More details](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md)
## Summary
Explain in one sentence the goal of this PR
Solve the issue: #___
## Quick changelog
- <change log #1>
- <change log #2>
## What's new?
*Explain in details what this PR solve or improve. You can include visuals.*
+15 -4
View File
@@ -1,3 +1,15 @@
# Freqtrade rules
freqtrade/tests/testdata/*.json
hyperopt_conf.py
config*.json
*.sqlite
.hyperopt
logfile.txt
hyperopt_trials.pickle
user_data/
freqtrade-plot.html
freqtrade-profit-plot.html
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
@@ -73,11 +85,10 @@ target/
# pyenv
.python-version
config.json
preprocessor.py
*.sqlite
.env
.venv
.idea
.vscode
.pytest_cache/
.mypy_cache/
+8 -1
View File
@@ -1,3 +1,10 @@
[MASTER]
extension-pkg-whitelist=numpy,talib,talib.abstract
[BASIC]
good-names=logger
ignore=vendor
ignore=vendor
[TYPECHECK]
ignored-modules=numpy,talib,talib.abstract
+32
View File
@@ -0,0 +1,32 @@
# autogenerated pyup.io config file
# see https://pyup.io/docs/configuration/ for all available options
# configure updates globally
# default: all
# allowed: all, insecure, False
update: all
# configure dependency pinning globally
# default: True
# allowed: True, False
pin: True
schedule: "every day"
search: False
# Specify requirement files by hand, default is empty
# default: empty
# allowed: list
requirements:
- requirements.txt
- requirements-dev.txt
# configure the branch prefix the bot is using
# default: pyup-
branch_prefix: pyup/
# allow to close stale PRs
# default: True
close_prs: True
+41 -12
View File
@@ -1,12 +1,15 @@
sudo: false
sudo: true
os:
- linux
dist: trusty
language: python
python:
- 3.6
services:
- docker
env:
- BACKTEST=
- BACKTEST=true
global:
- IMAGE_NAME=freqtradeorg/freqtrade
addons:
apt:
packages:
@@ -14,16 +17,42 @@ addons:
- libdw-dev
- binutils-dev
install:
- wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
- tar zxvf ta-lib-0.4.0-src.tar.gz
- cd ta-lib && ./configure && sudo make && sudo make install && cd ..
- ./build_helpers/install_ta-lib.sh
- export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
- pip install coveralls
- pip install -r requirements.txt
script:
- pytest --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/
after_success:
- coveralls
- pip install --upgrade flake8 coveralls pytest-random-order pytest-asyncio mypy
- pip install -r requirements-dev.txt
- pip install -e .
jobs:
include:
- stage: tests
script:
- pytest --cov=freqtrade --cov-config=.coveragerc freqtrade/tests/
- coveralls
name: pytest
- script:
- cp config.json.example config.json
- python freqtrade/main.py --datadir freqtrade/tests/testdata backtesting
name: backtest
- script:
- cp config.json.example config.json
- python freqtrade/main.py --datadir freqtrade/tests/testdata hyperopt -e 5
name: hyperopt
- script: flake8 freqtrade
name: flake8
- script: mypy freqtrade
name: mypy
- stage: docker
if: branch in (master, develop, feat/improve_travis) AND (type in (push, cron))
script:
- build_helpers/publish_docker.sh
name: "Build and test and push docker image"
notifications:
slack:
secure: bKLXmOrx8e2aPZl7W8DA5BdPAXWGpI5UzST33oc1G/thegXcDVmHBTJrBs4sZak6bgAclQQrdZIsRd2eFYzHLalJEaw6pk7hoAw8SvLnZO0ZurWboz7qg2+aZZXfK4eKl/VUe4sM9M4e/qxjkK+yWG7Marg69c4v1ypF7ezUi1fPYILYw8u0paaiX0N5UX8XNlXy+PBlga2MxDjUY70MuajSZhPsY2pDUvYnMY1D/7XN3cFW0g+3O8zXjF0IF4q1Z/1ASQe+eYjKwPQacE+O8KDD+ZJYoTOFBAPllrtpO1jnOPFjNGf3JIbVMZw4bFjIL0mSQaiSUaUErbU3sFZ5Or79rF93XZ81V7uEZ55vD8KMfR2CB1cQJcZcj0v50BxLo0InkFqa0Y8Nra3sbpV4fV5Oe8pDmomPJrNFJnX6ULQhQ1gTCe0M5beKgVms5SITEpt4/Y0CmLUr6iHDT0CUiyMIRWAXdIgbGh1jfaWOMksybeRevlgDsIsNBjXmYI1Sw2ZZR2Eo2u4R6zyfyjOMLwYJ3vgq9IrACv2w5nmf0+oguMWHf6iWi2hiOqhlAN1W74+3HsYQcqnuM3LGOmuCnPprV1oGBqkPXjIFGpy21gNx4vHfO1noLUyJnMnlu2L7SSuN1CdLsnjJ1hVjpJjPfqB4nn8g12x87TqM1bOm+3Q=
cache:
directories:
- $HOME/.cache/pip
- ta-lib
+131
View File
@@ -0,0 +1,131 @@
# Contributing
## Contribute to freqtrade
Feel like our bot is missing a feature? We welcome your pull requests!
Issues labeled [good first issue](https://github.com/freqtrade/freqtrade/labels/good%20first%20issue) can be good first contributions, and will help get you familiar with the codebase.
Few pointers for contributions:
- Create your PR against the `develop` branch, not `master`.
- New features need to contain unit tests and must be PEP8 conformant (max-line-length = 100).
If you are unsure, discuss the feature on our [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE)
or in a [issue](https://github.com/freqtrade/freqtrade/issues) before a PR.
## Before sending the PR:
### 1. Run unit tests
All unit tests must pass. If a unit test is broken, change your code to
make it pass. It means you have introduced a regression.
#### Test the whole project
```bash
pytest freqtrade
```
#### Test only one file
```bash
pytest freqtrade/tests/test_<file_name>.py
```
#### Test only one method from one file
```bash
pytest freqtrade/tests/test_<file_name>.py::test_<method_name>
```
### 2. Test if your code is PEP8 compliant
#### Install packages
```bash
pip3.6 install flake8 coveralls
```
#### Run Flake8
```bash
flake8 freqtrade
```
We receive a lot of code that fails the `flake8` checks.
To help with that, we encourage you to install the git pre-commit
hook that will warn you when you try to commit code that fails these checks.
Guide for installing them is [here](http://flake8.pycqa.org/en/latest/user/using-hooks.html).
### 3. Test if all type-hints are correct
#### Install packages
``` bash
pip3.6 install mypy
```
#### Run mypy
``` bash
mypy freqtrade
```
## Getting started
Best start by reading the [documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md) to get a feel for what is possible with the bot, or head straight to the [Developer-documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/developer.md) (WIP) which should help you getting started.
## (Core)-Committer Guide
### Process: Pull Requests
How to prioritize pull requests, from most to least important:
1. Fixes for broken tests. Broken means broken on any supported platform or Python version.
1. Extra tests to cover corner cases.
1. Minor edits to docs.
1. Bug fixes.
1. Major edits to docs.
1. Features.
Ensure that each pull request meets all requirements in the Contributing document.
### Process: Issues
If an issue is a bug that needs an urgent fix, mark it for the next patch release.
Then either fix it or mark as please-help.
For other issues: encourage friendly discussion, moderate debate, offer your thoughts.
### Process: Your own code changes
All code changes, regardless of who does them, need to be reviewed and merged by someone else.
This rule applies to all the core committers.
Exceptions:
- Minor corrections and fixes to pull requests submitted by others.
- While making a formal release, the release manager can make necessary, appropriate changes.
- Small documentation changes that reinforce existing subject matter. Most commonly being, but not limited to spelling and grammar corrections.
### Responsibilities
- Ensure cross-platform compatibility for every change that's accepted. Windows, Mac & Linux.
- Ensure no malicious code is introduced into the core code.
- Create issues for any major changes and enhancements that you wish to make. Discuss things transparently and get community feedback.
- Keep feature versions as small as possible, preferably one new feature per version.
- Be welcoming to newcomers and encourage diverse new contributors from all backgrounds. See the Python Community Code of Conduct (https://www.python.org/psf/codeofconduct/).
### Becoming a Committer
Contributors may be given commit privileges. Preference will be given to those with:
1. Past contributions to FreqTrade and other related open-source projects. Contributions to FreqTrade include both code (both accepted and pending) and friendly participation in the issue tracker and Pull request reviews. Quantity and quality are considered.
1. A coding style that the other core committers find simple, minimal, and clean.
1. Access to resources for cross-platform development and testing.
1. Time to devote to the project regularly.
Beeing a Committer does not grant write permission on `develop` or `master` for security reasons (Users trust FreqTrade with their Exchange API keys).
After beeing Committer for some time, a Committer may be named Core Committer and given full repository access.
+15 -12
View File
@@ -1,23 +1,26 @@
FROM python:3.6.2
FROM python:3.7.0-slim-stretch
# Install TA-lib
RUN apt-get update && apt-get -y install build-essential && apt-get clean
RUN curl -L http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz | \
tar xzvf - && \
cd ta-lib && \
./configure && make && make install && \
cd .. && rm -rf ta-lib
ENV LD_LIBRARY_PATH /usr/local/lib
RUN apt-get update \
&& apt-get -y install curl build-essential \
&& apt-get clean \
&& pip install --upgrade pip
# Prepare environment
RUN mkdir /freqtrade
WORKDIR /freqtrade
# Install TA-lib
COPY build_helpers/* /tmp/
RUN cd /tmp && /tmp/install_ta-lib.sh && rm -r /tmp/*ta-lib*
ENV LD_LIBRARY_PATH /usr/local/lib
# Install dependencies
COPY requirements.txt /freqtrade/
RUN pip install -r requirements.txt
RUN pip install numpy --no-cache-dir \
&& pip install -r requirements.txt --no-cache-dir
# Install and execute
COPY . /freqtrade/
RUN pip install -e .
CMD ["freqtrade"]
RUN pip install -e . --no-cache-dir
ENTRYPOINT ["freqtrade"]
+9
View File
@@ -0,0 +1,9 @@
FROM freqtradeorg/freqtrade:develop
# Install dependencies
COPY requirements-dev.txt /freqtrade/
RUN pip install numpy --no-cache-dir \
&& pip install -r requirements-dev.txt --no-cache-dir
# Empty the ENTRYPOINT to allow all commands
ENTRYPOINT []
+6
View File
@@ -0,0 +1,6 @@
FROM freqtradeorg/freqtrade:develop
RUN apt-get update \
&& apt-get -y install git \
&& apt-get clean \
&& pip install git+https://github.com/berlinguyinca/technical
+191 -118
View File
@@ -1,157 +1,230 @@
# freqtrade
[![Build Status](https://travis-ci.org/gcarq/freqtrade.svg?branch=develop)](https://travis-ci.org/gcarq/freqtrade)
[![Coverage Status](https://coveralls.io/repos/github/gcarq/freqtrade/badge.svg?branch=develop)](https://coveralls.io/github/gcarq/freqtrade?branch=develop)
[![Build Status](https://travis-ci.org/freqtrade/freqtrade.svg?branch=develop)](https://travis-ci.org/freqtrade/freqtrade)
[![Coverage Status](https://coveralls.io/repos/github/freqtrade/freqtrade/badge.svg?branch=develop&service=github)](https://coveralls.io/github/freqtrade/freqtrade?branch=develop)
[![Maintainability](https://api.codeclimate.com/v1/badges/5737e6d668200b7518ff/maintainability)](https://codeclimate.com/github/freqtrade/freqtrade/maintainability)
Simple High frequency trading bot for crypto currencies designed to support multi exchanges and be controlled via Telegram.
![freqtrade](https://raw.githubusercontent.com/freqtrade/freqtrade/develop/docs/assets/freqtrade-screenshot.png)
## Disclaimer
This software is for educational purposes only. Do not risk money which
you are afraid to lose. USE THE SOFTWARE AT YOUR OWN RISK. THE AUTHORS
AND ALL AFFILIATES ASSUME NO RESPONSIBILITY FOR YOUR TRADING RESULTS.
Always start by running a trading bot in Dry-run and do not engage money
before you understand how it works and what profit/loss you should
expect.
We strongly recommend you to have coding and Python knowledge. Do not
hesitate to read the source code and understand the mechanism of this bot.
## Exchange marketplaces supported
- [X] [Bittrex](https://bittrex.com/)
- [X] [Binance](https://www.binance.com/) ([*Note for binance users](#a-note-on-binance))
- [ ] [113 others to tests](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_
## Features
- [x] **Based on Python 3.6+**: For botting on any operating system - Windows, macOS and Linux
- [x] **Persistence**: Persistence is achieved through sqlite
- [x] **Dry-run**: Run the bot without playing money.
- [x] **Backtesting**: Run a simulation of your buy/sell strategy.
- [x] **Strategy Optimization by machine learning**: Use machine learning to optimize your buy/sell strategy parameters with real exchange data.
- [x] **Edge position sizing** Calculate your win rate, risk reward ratio, the best stoploss and adjust your position size before taking a position for each specific market. [Learn more](https://github.com/freqtrade/freqtrade/blob/develop/docs/edge.md)
- [x] **Whitelist crypto-currencies**: Select which crypto-currency you want to trade or use dynamic whitelists.
- [x] **Blacklist crypto-currencies**: Select which crypto-currency you want to avoid.
- [x] **Manageable via Telegram**: Manage the bot with Telegram
- [x] **Display profit/loss in fiat**: Display your profit/loss in 33 fiat.
- [x] **Daily summary of profit/loss**: Provide a daily summary of your profit/loss.
- [x] **Performance status report**: Provide a performance status of your current trades.
Simple High frequency trading bot for crypto currencies.
Currently supports trading on Bittrex exchange.
## Table of Contents
This software is for educational purposes only.
Don't risk money which you are afraid to lose.
- [Quick start](#quick-start)
- [Documentations](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md)
- [Installation](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md)
- [Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)
- [Strategy Optimization](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md)
- [Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md)
- [Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md)
- [Sandbox Testing](https://github.com/freqtrade/freqtrade/blob/develop/docs/sandbox-testing.md)
- [Edge](https://github.com/freqtrade/freqtrade/blob/develop/docs/edge.md)
- [Basic Usage](#basic-usage)
- [Bot commands](#bot-commands)
- [Telegram RPC commands](#telegram-rpc-commands)
- [Support](#support)
- [Help](#help--slack)
- [Bugs](#bugs--issues)
- [Feature Requests](#feature-requests)
- [Pull Requests](#pull-requests)
- [Requirements](#requirements)
- [Min hardware required](#min-hardware-required)
- [Software requirements](#software-requirements)
- [Wanna help?](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md)
- [Dev - getting started](https://github.com/freqtrade/freqtrade/blob/develop/docs/developer.md) (WIP)
The command interface is accessible via Telegram (not required).
Just register a new bot on https://telegram.me/BotFather
and enter the telegram `token` and your `chat_id` in `config.json`
## Quick start
Persistence is achieved through sqlite.
Freqtrade provides a Linux/macOS script to install all dependencies and help you to configure the bot.
### Telegram RPC commands:
* /start: Starts the trader
* /stop: Stops the trader
* /status [table]: Lists all open trades
* /count: Displays number of open trades
* /profit: Lists cumulative profit from all finished trades
* /forcesell <trade_id>|all: Instantly sells the given trade (Ignoring `minimum_roi`).
* /performance: Show performance of each finished trade grouped by pair
* /balance: Show account balance per currency
* /help: Show help message
* /version: Show version
### Config
`minimal_roi` is a JSON object where the key is a duration
in minutes and the value is the minimum ROI in percent.
See the example below:
```
"minimal_roi": {
"50": 0.0, # Sell after 30 minutes if the profit is not negative
"40": 0.01, # Sell after 25 minutes if there is at least 1% profit
"30": 0.02, # Sell after 15 minutes if there is at least 2% profit
"0": 0.045 # Sell immediately if there is at least 4.5% profit
},
```bash
git clone git@github.com:freqtrade/freqtrade.git
cd freqtrade
git checkout develop
./setup.sh --install
```
`stoploss` is loss in percentage that should trigger a sale.
For example value `-0.10` will cause immediate sell if the
profit dips below -10% for a given trade. This parameter is optional.
_Windows installation is explained in [Installation doc](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md)_
`initial_state` is an optional field that defines the initial application state.
Possible values are `running` or `stopped`. (default=`running`)
If the value is `stopped` the bot has to be started with `/start` first.
## Documentation
`ask_last_balance` sets the bidding price. Value `0.0` will use `ask` price, `1.0` will
use the `last` price and values between those interpolate between ask and last
price. Using `ask` price will guarantee quick success in bid, but bot will also
end up paying more then would probably have been necessary.
We invite you to read the bot documentation to ensure you understand how the bot is working.
The other values should be self-explanatory,
if not feel free to raise a github issue.
- [Index](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md)
- [Installation](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md)
- [Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)
- [Bot usage](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md)
- [How to run the bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#bot-commands)
- [How to use Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#backtesting-commands)
- [How to use Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#hyperopt-commands)
- [Strategy Optimization](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md)
- [Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md)
- [Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md)
### Prerequisites
* python3.6
* sqlite
* [TA-lib](https://github.com/mrjbq7/ta-lib#dependencies) binaries
## Basic Usage
### Install
### Bot commands
#### Arch Linux
```bash
usage: main.py [-h] [-v] [--version] [-c PATH] [-d PATH] [-s NAME]
[--strategy-path PATH] [--dynamic-whitelist [INT]]
[--dry-run-db]
{backtesting,hyperopt} ...
Use your favorite AUR helper and install `python-freqtrade-git`.
Simple High Frequency Trading Bot for crypto currencies
#### Manually
positional arguments:
{backtesting,hyperopt}
backtesting backtesting module
hyperopt hyperopt module
`master` branch contains the latest stable release.
`develop` branch has often new features, but might also cause breaking changes. To use it, you are encouraged to join our [slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE).
```
$ cd freqtrade/
# copy example config. Dont forget to insert your api keys
$ cp config.json.example config.json
$ python -m venv .env
$ source .env/bin/activate
$ pip install -r requirements.txt
$ pip install -e .
$ ./freqtrade/main.py
optional arguments:
-h, --help show this help message and exit
-v, --verbose be verbose
--version show program's version number and exit
-c PATH, --config PATH
specify configuration file (default: config.json)
-d PATH, --datadir PATH
path to backtest data (default:
freqtrade/tests/testdata
-s NAME, --strategy NAME
specify strategy class name (default: DefaultStrategy)
--strategy-path PATH specify additional strategy lookup path
--dynamic-whitelist [INT]
dynamically generate and update whitelist based on 24h
BaseVolume (Default 20 currencies)
--dry-run-db Force dry run to use a local DB
"tradesv3.dry_run.sqlite" instead of memory DB. Work
only if dry_run is enabled.
```
There is also an [article](https://www.sales4k.com/blockchain/high-frequency-trading-bot-tutorial/) about how to setup the bot (thanks [@gurghet](https://github.com/gurghet)).*
### Telegram RPC commands
\* *Note:* that article was written for an earlier version, so it may be outdated
Telegram is not mandatory. However, this is a great way to control your bot. More details on our [documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/index.md)
#### Docker
- `/start`: Starts the trader
- `/stop`: Stops the trader
- `/status [table]`: Lists all open trades
- `/count`: Displays number of open trades
- `/profit`: Lists cumulative profit from all finished trades
- `/forcesell <trade_id>|all`: Instantly sells the given trade (Ignoring `minimum_roi`).
- `/performance`: Show performance of each finished trade grouped by pair
- `/balance`: Show account balance per currency
- `/daily <n>`: Shows profit or loss per day, over the last n days
- `/help`: Show help message
- `/version`: Show version
Building the image:
```
$ cd freqtrade
$ docker build -t freqtrade .
```
## Development branches
For security reasons, your configuration file will not be included in the
image, you will need to bind mount it. It is also advised to bind mount
a SQLite database file (see second example) to keep it between updates.
The project is currently setup in two main branches:
You can run a one-off container that is immediately deleted upon exiting with
the following command (config.json must be in the current working directory):
- `develop` - This branch has often new features, but might also cause breaking changes.
- `master` - This branch contains the latest stable release. The bot 'should' be stable on this branch, and is generally well tested.
- `feat/*` - These are feature branches, which are being worked on heavily. Please don't use these unless you want to test a specific feature.
```
$ docker run --rm -v `pwd`/config.json:/freqtrade/config.json -it freqtrade
```
To run a restartable instance in the background (feel free to place your
configuration and database files wherever it feels comfortable on your
filesystem):
## A note on Binance
```
$ cd ~/.freq
$ touch tradesv3.sqlite
$ docker run -d \
--name freqtrade \
-v ~/.freq/config.json:/freqtrade/config.json \
-v ~/.freq/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \
freqtrade
```
If you are using `dry_run=True` it's not necessary to mount `tradesv3.sqlite`.
For Binance, please add `"BNB/<STAKE>"` to your blacklist to avoid issues.
Accounts having BNB accounts use this to pay for fees - if your first trade happens to be on `BNB`, further trades will consume this position and make the initial BNB order unsellable as the expected amount is not there anymore.
You can then use the following commands to monitor and manage your container:
## Support
```
$ docker logs freqtrade
$ docker logs -f freqtrade
$ docker restart freqtrade
$ docker stop freqtrade
$ docker start freqtrade
```
### Help / Slack
You do not need to rebuild the image for configuration
changes, it will suffice to edit `config.json` and restart the container.
For any questions not covered by the documentation or for further
information about the bot, we encourage you to join our slack channel.
### Execute tests
- [Click here to join Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE).
```
$ pytest
```
This will by default skip the slow running backtest set. To run backtest set:
### [Bugs / Issues](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue)
```
$ BACKTEST=true pytest -s freqtrade/tests/test_backtesting.py
```
If you discover a bug in the bot, please
[search our issue tracker](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue)
first. If it hasn't been reported, please
[create a new issue](https://github.com/freqtrade/freqtrade/issues/new) and
ensure you follow the template guide so that our team can assist you as
quickly as possible.
### Contributing
### [Feature Requests](https://github.com/freqtrade/freqtrade/labels/enhancement)
Feel like our bot is missing a feature? We welcome your pull requests! Few pointers for contributions:
Have you a great idea to improve the bot you want to share? Please,
first search if this feature was not [already discussed](https://github.com/freqtrade/freqtrade/labels/enhancement).
If it hasn't been requested, please
[create a new request](https://github.com/freqtrade/freqtrade/issues/new)
and ensure you follow the template guide so that it does not get lost
in the bug reports.
### [Pull Requests](https://github.com/freqtrade/freqtrade/pulls)
Feel like our bot is missing a feature? We welcome your pull requests!
Please read our
[Contributing document](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md)
to understand the requirements before sending your pull-requests.
Coding is not a neccessity to contribute - maybe start with improving our documentation?
Issues labeled [good first issue](https://github.com/freqtrade/freqtrade/labels/good%20first%20issue) can be good first contributions, and will help get you familiar with the codebase.
**Note** before starting any major new feature work, *please open an issue describing what you are planning to do* or talk to us on [Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE). This will ensure that interested parties can give valuable feedback on the feature, and let others know that you are working on it.
**Important:** Always create your PR against the `develop` branch, not `master`.
## Requirements
### Uptodate clock
The clock must be accurate, syncronized to a NTP server very frequently to avoid problems with communication to the exchanges.
### Min hardware required
To run this bot we recommend you a cloud instance with a minimum of:
- Minimal (advised) system requirements: 2GB RAM, 1GB disk space, 2vCPU
### Software requirements
- [Python 3.6.x](http://docs.python-guide.org/en/latest/starting/installation/)
- [pip](https://pip.pypa.io/en/stable/installing/)
- [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- [TA-Lib](https://mrjbq7.github.io/ta-lib/install.html)
- [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) (Recommended)
- [Docker](https://www.docker.com/products/docker) (Recommended)
- Create your PR against the `develop` branch, not `master`.
- New features need to contain unit tests.
- If you are unsure, discuss the feature on [slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) or in a [issue](https://github.com/gcarq/freqtrade/issues) before a PR.
+5 -2
View File
@@ -1,4 +1,7 @@
#!/usr/bin/env python3
from freqtrade.main import main
main()
import sys
from freqtrade.main import main, set_loggers
set_loggers()
main(sys.argv[1:])
+13
View File
@@ -0,0 +1,13 @@
if [ ! -f "ta-lib/CHANGELOG.TXT" ]; 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 \
&& make \
&& which sudo && sudo make install || make install \
&& cd ..
else
echo "TA-lib already installed, skipping download and build."
cd ta-lib && sudo make install && cd ..
fi
+60
View File
@@ -0,0 +1,60 @@
#!/bin/sh
# - export TAG=`if [ "$TRAVIS_BRANCH" == "develop" ]; then echo "latest"; else echo $TRAVIS_BRANCH ; fi`
# Replace / with _ to create a valid tag
TAG=$(echo "${TRAVIS_BRANCH}" | sed -e "s/\//_/")
# Add commit and commit_message to docker container
echo "${TRAVIS_COMMIT} ${TRAVIS_COMMIT_MESSAGE}" > freqtrade_commit
if [ "${TRAVIS_EVENT_TYPE}" = "cron" ]; then
echo "event ${TRAVIS_EVENT_TYPE}: full rebuild - skipping cache"
docker build -t freqtrade:${TAG} .
else
echo "event ${TRAVIS_EVENT_TYPE}: building with cache"
# Pull last build to avoid rebuilding the whole image
docker pull ${REPO}:${TAG}
docker build --cache-from ${IMAGE_NAME}:${TAG} -t freqtrade:${TAG} .
fi
if [ $? -ne 0 ]; then
echo "failed building image"
return 1
fi
# Run backtest
docker run --rm -it -v $(pwd)/config.json.example:/freqtrade/config.json:ro freqtrade:${TAG} --datadir freqtrade/tests/testdata backtesting
if [ $? -ne 0 ]; then
echo "failed running backtest"
return 1
fi
# Tag image for upload
docker tag freqtrade:$TAG ${IMAGE_NAME}:$TAG
if [ $? -ne 0 ]; then
echo "failed tagging image"
return 1
fi
# Tag as latest for develop builds
if [ "${TRAVIS_BRANCH}" = "develop" ]; then
docker tag freqtrade:$TAG ${IMAGE_NAME}:latest
fi
# Login
echo "$DOCKER_PASS" | docker login -u $DOCKER_USER --password-stdin
if [ $? -ne 0 ]; then
echo "failed login"
return 1
fi
# Show all available images
docker images
docker push ${IMAGE_NAME}
if [ $? -ne 0 ]; then
echo "failed pushing repo"
return 1
fi
Binary file not shown.
+61 -21
View File
@@ -2,40 +2,80 @@
"max_open_trades": 3,
"stake_currency": "BTC",
"stake_amount": 0.05,
"fiat_display_currency": "USD",
"ticker_interval" : "5m",
"dry_run": false,
"minimal_roi": {
"50": 0.0,
"40": 0.01,
"30": 0.02,
"0": 0.045
"trailing_stop": false,
"unfilledtimeout": {
"buy": 10,
"sell": 30
},
"stoploss": -0.40,
"bid_strategy": {
"ask_last_balance": 0.0
"ask_last_balance": 0.0,
"use_order_book": false,
"order_book_top": 1,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"ask_strategy":{
"use_order_book": false,
"order_book_min": 1,
"order_book_max": 9
},
"exchange": {
"name": "bittrex",
"key": "key",
"secret": "secret",
"key": "your_exchange_key",
"secret": "your_exchange_secret",
"ccxt_config": {"enableRateLimit": true},
"ccxt_async_config": {
"enableRateLimit": false
},
"pair_whitelist": [
"BTC_RLC",
"BTC_TKN",
"BTC_TRST",
"BTC_SWT",
"BTC_PIVX",
"BTC_MLN",
"BTC_XZC",
"BTC_TIME",
"BTC_LUN"
"ETH/BTC",
"LTC/BTC",
"ETC/BTC",
"DASH/BTC",
"ZEC/BTC",
"XLM/BTC",
"NXT/BTC",
"POWR/BTC",
"ADA/BTC",
"XMR/BTC"
],
"pair_blacklist": [
"DOGE/BTC"
]
},
"experimental": {
"use_sell_signal": false,
"sell_profit_only": false,
"ignore_roi_if_buy_signal": false
},
"edge": {
"enabled": false,
"process_throttle_secs": 3600,
"calculate_since_number_of_days": 7,
"capital_available_percentage": 0.5,
"allowed_risk": 0.01,
"stoploss_range_min": -0.01,
"stoploss_range_max": -0.1,
"stoploss_range_step": -0.01,
"minimum_winrate": 0.60,
"minimum_expectancy": 0.20,
"min_trade_number": 10,
"max_trade_duration_minute": 1440,
"remove_pumps": false
},
"telegram": {
"enabled": true,
"token": "token",
"chat_id": "chat_id"
"token": "your_telegram_token",
"chat_id": "your_telegram_chat_id"
},
"initial_state": "running",
"forcebuy_enable": false,
"internals": {
"process_throttle_secs": 5
}
}
}
+83
View File
@@ -0,0 +1,83 @@
{
"max_open_trades": 3,
"stake_currency": "BTC",
"stake_amount": 0.05,
"fiat_display_currency": "USD",
"ticker_interval" : "5m",
"dry_run": true,
"trailing_stop": false,
"unfilledtimeout": {
"buy": 10,
"sell": 30
},
"bid_strategy": {
"ask_last_balance": 0.0,
"use_order_book": false,
"order_book_top": 1,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"ask_strategy":{
"use_order_book": false,
"order_book_min": 1,
"order_book_max": 9
},
"exchange": {
"name": "binance",
"key": "your_exchange_key",
"secret": "your_exchange_secret",
"ccxt_config": {"enableRateLimit": true},
"ccxt_async_config": {
"enableRateLimit": false
},
"pair_whitelist": [
"AST/BTC",
"ETC/BTC",
"ETH/BTC",
"EOS/BTC",
"IOTA/BTC",
"LTC/BTC",
"MTH/BTC",
"NCASH/BTC",
"TNT/BTC",
"XMR/BTC",
"XLM/BTC",
"XRP/BTC"
],
"pair_blacklist": [
"BNB/BTC"
]
},
"experimental": {
"use_sell_signal": false,
"sell_profit_only": false,
"ignore_roi_if_buy_signal": false
},
"edge": {
"enabled": false,
"process_throttle_secs": 3600,
"calculate_since_number_of_days": 7,
"capital_available_percentage": 0.5,
"allowed_risk": 0.01,
"stoploss_range_min": -0.01,
"stoploss_range_max": -0.1,
"stoploss_range_step": -0.01,
"minimum_winrate": 0.60,
"minimum_expectancy": 0.20,
"min_trade_number": 10,
"max_trade_duration_minute": 1440,
"remove_pumps": false
},
"telegram": {
"enabled": false,
"token": "your_telegram_token",
"chat_id": "your_telegram_chat_id"
},
"initial_state": "running",
"forcebuy_enable": false,
"internals": {
"process_throttle_secs": 5
}
}
+112
View File
@@ -0,0 +1,112 @@
{
"max_open_trades": 3,
"stake_currency": "BTC",
"stake_amount": 0.05,
"fiat_display_currency": "USD",
"dry_run": false,
"ticker_interval": "5m",
"trailing_stop": false,
"trailing_stop_positive": 0.005,
"trailing_stop_positive_offset": 0.0051,
"minimal_roi": {
"40": 0.0,
"30": 0.01,
"20": 0.02,
"0": 0.04
},
"stoploss": -0.10,
"unfilledtimeout": {
"buy": 10,
"sell": 30
},
"bid_strategy": {
"ask_last_balance": 0.0,
"use_order_book": false,
"order_book_top": 1,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"ask_strategy":{
"use_order_book": false,
"order_book_min": 1,
"order_book_max": 9
},
"order_types": {
"buy": "limit",
"sell": "limit",
"stoploss": "market",
"stoploss_on_exchange": "false"
},
"order_time_in_force": {
"buy": "gtc",
"sell": "gtc",
},
"pairlist": {
"method": "VolumePairList",
"config": {
"number_assets": 20,
"sort_key": "quoteVolume"
}
},
"exchange": {
"name": "bittrex",
"key": "your_exchange_key",
"secret": "your_exchange_secret",
"ccxt_config": {"enableRateLimit": true},
"ccxt_async_config": {
"enableRateLimit": false,
"aiohttp_trust_env": false
},
"pair_whitelist": [
"ETH/BTC",
"LTC/BTC",
"ETC/BTC",
"DASH/BTC",
"ZEC/BTC",
"XLM/BTC",
"NXT/BTC",
"POWR/BTC",
"ADA/BTC",
"XMR/BTC"
],
"pair_blacklist": [
"DOGE/BTC"
],
"outdated_offset": 5
},
"edge": {
"enabled": false,
"process_throttle_secs": 3600,
"calculate_since_number_of_days": 7,
"capital_available_percentage": 0.5,
"allowed_risk": 0.01,
"stoploss_range_min": -0.01,
"stoploss_range_max": -0.1,
"stoploss_range_step": -0.01,
"minimum_winrate": 0.60,
"minimum_expectancy": 0.20,
"min_trade_number": 10,
"max_trade_duration_minute": 1440,
"remove_pumps": false
},
"experimental": {
"use_sell_signal": false,
"sell_profit_only": false,
"ignore_roi_if_buy_signal": false
},
"telegram": {
"enabled": true,
"token": "your_telegram_token",
"chat_id": "your_telegram_chat_id"
},
"db_url": "sqlite:///tradesv3.sqlite",
"initial_state": "running",
"forcebuy_enable": false,
"internals": {
"process_throttle_secs": 5
},
"strategy": "DefaultStrategy",
"strategy_path": "/some/folder/"
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

+270
View File
@@ -0,0 +1,270 @@
# Backtesting
This page explains how to validate your strategy performance by using
Backtesting.
## Table of Contents
- [Test your strategy with Backtesting](#test-your-strategy-with-backtesting)
- [Understand the backtesting result](#understand-the-backtesting-result)
## Test your strategy with Backtesting
Now you have good Buy and Sell strategies, you want to test it against
real data. This is what we call
[backtesting](https://en.wikipedia.org/wiki/Backtesting).
Backtesting will use the crypto-currencies (pair) from your config file
and load static tickers located in
[/freqtrade/tests/testdata](https://github.com/freqtrade/freqtrade/tree/develop/freqtrade/tests/testdata).
If the 5 min and 1 min ticker for the crypto-currencies to test is not
already in the `testdata` folder, backtesting will download them
automatically. Testdata files will not be updated until you specify it.
The result of backtesting will confirm you if your bot has better odds of making a profit than a loss.
The backtesting is very easy with freqtrade.
### Run a backtesting against the currencies listed in your config file
#### With 5 min tickers (Per default)
```bash
python3 ./freqtrade/main.py backtesting
```
#### With 1 min tickers
```bash
python3 ./freqtrade/main.py backtesting --ticker-interval 1m
```
#### Update cached pairs with the latest data
```bash
python3 ./freqtrade/main.py backtesting --refresh-pairs-cached
```
#### With live data (do not alter your testdata files)
```bash
python3 ./freqtrade/main.py backtesting --live
```
#### Using a different on-disk ticker-data source
```bash
python3 ./freqtrade/main.py backtesting --datadir freqtrade/tests/testdata-20180101
```
#### With a (custom) strategy file
```bash
python3 ./freqtrade/main.py -s TestStrategy backtesting
```
Where `-s TestStrategy` refers to the class name within the strategy file `test_strategy.py` found in the `freqtrade/user_data/strategies` directory
#### Exporting trades to file
```bash
python3 ./freqtrade/main.py backtesting --export trades
```
The exported trades can be read using the following code for manual analysis, or can be used by the plotting script `plot_dataframe.py` in the scripts folder.
``` python
import json
from pathlib import Path
import pandas as pd
filename=Path('user_data/backtest_data/backtest-result.json')
with filename.open() as file:
data = json.load(file)
columns = ["pair", "profit", "opents", "closets", "index", "duration",
"open_rate", "close_rate", "open_at_end", "sell_reason"]
df = pd.DataFrame(data, columns=columns)
df['opents'] = pd.to_datetime(df['opents'],
unit='s',
utc=True,
infer_datetime_format=True
)
df['closets'] = pd.to_datetime(df['closets'],
unit='s',
utc=True,
infer_datetime_format=True
)
```
If you have some ideas for interesting / helpful backtest data analysis, feel free to submit a PR so the community can benefit from it.
#### Exporting trades to file specifying a custom filename
```bash
python3 ./freqtrade/main.py backtesting --export trades --export-filename=backtest_teststrategy.json
```
#### Running backtest with smaller testset
Use the `--timerange` argument to change how much of the testset
you want to use. The last N ticks/timeframes will be used.
Example:
```bash
python3 ./freqtrade/main.py backtesting --timerange=-200
```
#### Advanced use of timerange
Doing `--timerange=-200` will get the last 200 timeframes
from your inputdata. You can also specify specific dates,
or a range span indexed by start and stop.
The full timerange specification:
- Use last 123 tickframes of data: `--timerange=-123`
- Use first 123 tickframes of data: `--timerange=123-`
- Use tickframes from line 123 through 456: `--timerange=123-456`
- Use tickframes till 2018/01/31: `--timerange=-20180131`
- Use tickframes since 2018/01/31: `--timerange=20180131-`
- Use tickframes since 2018/01/31 till 2018/03/01 : `--timerange=20180131-20180301`
- Use tickframes between POSIX timestamps 1527595200 1527618600:
`--timerange=1527595200-1527618600`
#### Downloading new set of ticker data
To download new set of backtesting ticker data, you can use a download script.
If you are using Binance for example:
- create a folder `user_data/data/binance` and copy `pairs.json` in that folder.
- update the `pairs.json` to contain the currency pairs you are interested in.
```bash
mkdir -p user_data/data/binance
cp freqtrade/tests/testdata/pairs.json user_data/data/binance
```
Then run:
```bash
python scripts/download_backtest_data.py --exchange binance
```
This will download ticker data for all the currency pairs you defined in `pairs.json`.
- To use a different folder than the exchange specific default, use `--export user_data/data/some_directory`.
- To change the exchange used to download the tickers, use `--exchange`. Default is `bittrex`.
- To use `pairs.json` from some other folder, use `--pairs-file some_other_dir/pairs.json`.
- To download ticker data for only 10 days, use `--days 10`.
- Use `--timeframes` to specify which tickers to download. Default is `--timeframes 1m 5m` which will download 1-minute and 5-minute tickers.
For help about backtesting usage, please refer to [Backtesting commands](#backtesting-commands).
## Understand the backtesting result
The most important in the backtesting is to understand the result.
A backtesting result will look like that:
```
======================================== BACKTESTING REPORT =========================================
| pair | buy count | avg profit % | total profit BTC | avg duration | profit | loss |
|:---------|------------:|---------------:|-------------------:|---------------:|---------:|-------:|
| ETH/BTC | 44 | 0.18 | 0.00159118 | 50.9 | 44 | 0 |
| LTC/BTC | 27 | 0.10 | 0.00051931 | 103.1 | 26 | 1 |
| ETC/BTC | 24 | 0.05 | 0.00022434 | 166.0 | 22 | 2 |
| DASH/BTC | 29 | 0.18 | 0.00103223 | 192.2 | 29 | 0 |
| ZEC/BTC | 65 | -0.02 | -0.00020621 | 202.7 | 62 | 3 |
| XLM/BTC | 35 | 0.02 | 0.00012877 | 242.4 | 32 | 3 |
| BCH/BTC | 12 | 0.62 | 0.00149284 | 50.0 | 12 | 0 |
| POWR/BTC | 21 | 0.26 | 0.00108215 | 134.8 | 21 | 0 |
| ADA/BTC | 54 | -0.19 | -0.00205202 | 191.3 | 47 | 7 |
| XMR/BTC | 24 | -0.43 | -0.00206013 | 120.6 | 20 | 4 |
| TOTAL | 335 | 0.03 | 0.00175246 | 157.9 | 315 | 20 |
2018-06-13 06:57:27,347 - freqtrade.optimize.backtesting - INFO -
====================================== LEFT OPEN TRADES REPORT ======================================
| pair | buy count | avg profit % | total profit BTC | avg duration | profit | loss |
|:---------|------------:|---------------:|-------------------:|---------------:|---------:|-------:|
| ETH/BTC | 3 | 0.16 | 0.00009619 | 25.0 | 3 | 0 |
| LTC/BTC | 1 | -1.00 | -0.00020118 | 1085.0 | 0 | 1 |
| ETC/BTC | 2 | -1.80 | -0.00071933 | 1092.5 | 0 | 2 |
| DASH/BTC | 0 | nan | 0.00000000 | nan | 0 | 0 |
| ZEC/BTC | 3 | -4.27 | -0.00256826 | 1301.7 | 0 | 3 |
| XLM/BTC | 3 | -1.11 | -0.00066744 | 965.0 | 0 | 3 |
| BCH/BTC | 0 | nan | 0.00000000 | nan | 0 | 0 |
| POWR/BTC | 0 | nan | 0.00000000 | nan | 0 | 0 |
| ADA/BTC | 7 | -3.58 | -0.00503604 | 850.0 | 0 | 7 |
| XMR/BTC | 4 | -3.79 | -0.00303456 | 291.2 | 0 | 4 |
| TOTAL | 23 | -2.63 | -0.01213062 | 750.4 | 3 | 20 |
```
The 1st table will contain all trades the bot made.
The 2nd table will contain all trades the bot had to `forcesell` at the end of the backtest period to prsent a full picture.
These trades are also included in the first table, but are extracted separately for clarity.
The last line will give you the overall performance of your strategy,
here:
```
TOTAL 419 -0.41 -0.00348593 52.9
```
We understand the bot has made `419` trades for an average duration of
`52.9` min, with a performance of `-0.41%` (loss), that means it has
lost a total of `-0.00348593 BTC`.
As you will see your strategy performance will be influenced by your buy
strategy, your sell strategy, and also by the `minimal_roi` and
`stop_loss` you have set.
As for an example if your minimal_roi is only `"0": 0.01`. You cannot
expect the bot to make more profit than 1% (because it will sell every
time a trade will reach 1%).
```json
"minimal_roi": {
"0": 0.01
},
```
On the other hand, if you set a too high `minimal_roi` like `"0": 0.55`
(55%), there is a lot of chance that the bot will never reach this
profit. Hence, keep in mind that your performance is a mix of your
strategies, your configuration, and the crypto-currency you have set up.
## Backtesting multiple strategies
To backtest multiple strategies, a list of Strategies can be provided.
This is limited to 1 ticker-interval per run, however, data is only loaded once from disk so if you have multiple
strategies you'd like to compare, this should give a nice runtime boost.
All listed Strategies need to be in the same folder.
``` bash
freqtrade backtesting --timerange 20180401-20180410 --ticker-interval 5m --strategy-list Strategy001 Strategy002 --export trades
```
This will save the results to `user_data/backtest_data/backtest-result-<strategy>.json`, injecting the strategy-name into the target filename.
There will be an additional table comparing win/losses of the different strategies (identical to the "Total" row in the first table).
Detailed output for all strategies one after the other will be available, so make sure to scroll up.
```
=================================================== Strategy Summary ====================================================
| Strategy | buy count | avg profit % | cum profit % | total profit ETH | avg duration | profit | loss |
|:-----------|------------:|---------------:|---------------:|-------------------:|:----------------|---------:|-------:|
| Strategy1 | 19 | -0.76 | -14.39 | -0.01440287 | 15:48:00 | 15 | 4 |
| Strategy2 | 6 | -2.73 | -16.40 | -0.01641299 | 1 day, 14:12:00 | 3 | 3 |
```
## Next step
Great, your strategy is profitable. What if the bot can give your the
optimal parameters to use for your strategy?
Your next step is to learn [how to find optimal parameters with Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md)
+270
View File
@@ -0,0 +1,270 @@
# Bot Optimization
This page explains where to customize your strategies, and add new
indicators.
## Table of Contents
- [Install a custom strategy file](#install-a-custom-strategy-file)
- [Customize your strategy](#change-your-strategy)
- [Anatomy of a strategy](#anatomy-of-a-strategy)
- [Customize indicators](#customize-indicators)
- [Buy signal rules](#buy-signal-rules)
- [Sell signal rules](#sell-signal-rules)
- [Minimal ROI](#minimal-roi)
- [Stoploss](#stoploss)
- [Ticker interval](#ticker-interval)
- [Metadata dict](#metadata-dict)
- [Where is the default strategy](#where-is-the-default-strategy)
- [Specify custom strategy location](#specify-custom-strategy-location)
- [Further strategy ideas](#further-strategy-ideas)
- [Where is the default strategy](#where-is-the-default-strategy)
Since the version `0.16.0` the bot allows using custom strategy file.
## Install a custom strategy file
This is very simple. Copy paste your strategy file into the folder
`user_data/strategies`.
Let assume you have a class called `AwesomeStrategy` in the file `awesome-strategy.py`:
1. Move your file into `user_data/strategies` (you should have `user_data/strategies/awesome-strategy.py`
2. Start the bot with the param `--strategy AwesomeStrategy` (the parameter is the class name)
```bash
python3 ./freqtrade/main.py --strategy AwesomeStrategy
```
## Change your strategy
The bot includes a default strategy file. However, we recommend you to
use your own file to not have to lose your parameters every time the default
strategy file will be updated on Github. Put your custom strategy file
into the folder `user_data/strategies`.
Best copy the test-strategy and modify this copy to avoid having bot-updates override your changes.
`cp user_data/strategies/test_strategy.py user_data/strategies/awesome-strategy.py`
### Anatomy of a strategy
A strategy file contains all the information needed to build a good strategy:
- Indicators
- Buy strategy rules
- Sell strategy rules
- Minimal ROI recommended
- Stoploss strongly recommended
The bot also include a sample strategy called `TestStrategy` you can update: `user_data/strategies/test_strategy.py`.
You can test it with the parameter: `--strategy TestStrategy`
``` bash
python3 ./freqtrade/main.py --strategy AwesomeStrategy
```
**For the following section we will use the [user_data/strategies/test_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/test_strategy.py)
file as reference.**
### Customize Indicators
Buy and sell strategies need indicators. You can add more indicators by extending the list contained in the method `populate_indicators()` from your strategy file.
You should only add the indicators used in either `populate_buy_trend()`, `populate_sell_trend()`, or to populate another indicator, otherwise performance may suffer.
It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected.
Sample:
```python
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Adds several different TA indicators to the given DataFrame
Performance Note: For the best performance be frugal on the number of indicators
you are using. Let uncomment only the indicator you are using in your strategies
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe()
:param metadata: Additional information, like the currently traded pair
:return: a Dataframe with all mandatory indicators for the strategies
"""
dataframe['sar'] = ta.SAR(dataframe)
dataframe['adx'] = ta.ADX(dataframe)
stoch = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch['fastd']
dataframe['fastk'] = stoch['fastk']
dataframe['blower'] = ta.BBANDS(dataframe, nbdevup=2, nbdevdn=2)['lowerband']
dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
dataframe['mfi'] = ta.MFI(dataframe)
dataframe['rsi'] = ta.RSI(dataframe)
dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
dataframe['ao'] = awesome_oscillator(dataframe)
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
hilbert = ta.HT_SINE(dataframe)
dataframe['htsine'] = hilbert['sine']
dataframe['htleadsine'] = hilbert['leadsine']
dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
dataframe['plus_di'] = ta.PLUS_DI(dataframe)
dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
return dataframe
```
#### Want more indicator examples
Look into the [user_data/strategies/test_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/user_data/strategies/test_strategy.py).
Then uncomment indicators you need.
### Buy signal rules
Edit the method `populate_buy_trend()` in your strategy file to update your buy strategy.
It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected.
This will method will also define a new column, `"buy"`, which needs to contain 1 for buys, and 0 for "no action".
Sample from `user_data/strategies/test_strategy.py`:
```python
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the buy signal for the given dataframe
:param dataframe: DataFrame populated with indicators
:param metadata: Additional information, like the currently traded pair
:return: DataFrame with buy column
"""
dataframe.loc[
(
(dataframe['adx'] > 30) &
(dataframe['tema'] <= dataframe['bb_middleband']) &
(dataframe['tema'] > dataframe['tema'].shift(1))
),
'buy'] = 1
return dataframe
```
### Sell signal rules
Edit the method `populate_sell_trend()` into your strategy file to update your sell strategy.
Please note that the sell-signal is only used if `use_sell_signal` is set to true in the configuration.
It's important to always return the dataframe without removing/modifying the columns `"open", "high", "low", "close", "volume"`, otherwise these fields would contain something unexpected.
This will method will also define a new column, `"sell"`, which needs to contain 1 for sells, and 0 for "no action".
Sample from `user_data/strategies/test_strategy.py`:
```python
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the sell signal for the given dataframe
:param dataframe: DataFrame populated with indicators
:param metadata: Additional information, like the currently traded pair
:return: DataFrame with buy column
"""
dataframe.loc[
(
(dataframe['adx'] > 70) &
(dataframe['tema'] > dataframe['bb_middleband']) &
(dataframe['tema'] < dataframe['tema'].shift(1))
),
'sell'] = 1
return dataframe
```
### Minimal ROI
This dict defines the minimal Return On Investment (ROI) a trade should reach before selling, independent from the sell signal.
It is of the following format, with the dict key (left side of the colon) being the minutes passed since the trade opened, and the value (right side of the colon) being the percentage.
```python
minimal_roi = {
"40": 0.0,
"30": 0.01,
"20": 0.02,
"0": 0.04
}
```
The above configuration would therefore mean:
- Sell whenever 4% profit was reached
- Sell after 20 minutes when 2% profit was reached
- Sell after 20 minutes when 2% profit was reached
- Sell after 30 minutes when 1% profit was reached
- Sell after 40 minutes when the trade is non-loosing (no profit)
The calculation does include fees.
To disable ROI completely, set it to an insanely high number:
```python
minimal_roi = {
"0": 100
}
```
While technically not completely disabled, this would sell once the trade reaches 10000% Profit.
### Stoploss
Setting a stoploss is highly recommended to protect your capital from strong moves against you.
Sample:
``` python
stoploss = -0.10
```
This would signify a stoploss of -10%.
If your exchange supports it, it's recommended to also set `"stoploss_on_exchange"` in the order dict, so your stoploss is on the exchange and cannot be missed for network-problems (or other problems).
For more information on order_types please look [here](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md#understand-order_types).
### Ticker interval
This is the set of candles the bot should download and use for the analysis.
Common values are `"1m"`, `"5m"`, `"15m"`, `"1h"`, however all values supported by your exchange should work.
Please note that the same buy/sell signals may work with one interval, but not the other.
### Metadata dict
The metadata-dict (available for `populate_buy_trend`, `populate_sell_trend`, `populate_indicators`) contains additional information.
Currently this is `pair`, which can be accessed using `metadata['pair']` - and will return a pair in the format `XRP/BTC`.
### Where is the default strategy?
The default buy strategy is located in the file
[freqtrade/default_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/strategy/default_strategy.py).
### Specify custom strategy location
If you want to use a strategy from a different folder you can pass `--strategy-path`
```bash
python3 ./freqtrade/main.py --strategy AwesomeStrategy --strategy-path /some/folder
```
### Further strategy ideas
To get additional Ideas for strategies, head over to our [strategy repository](https://github.com/freqtrade/freqtrade-strategies). Feel free to use them as they are - but results will depend on the current market situation, pairs used etc. - therefore please backtest the strategy for your exchange/desired pairs first, evaluate carefully, use at your own risk.
Feel free to use any of them as inspiration for your own strategies.
We're happy to accept Pull Requests containing new Strategies to that repo.
We also got a *strategy-sharing* channel in our [Slack community](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) which is a great place to get and/or share ideas.
## Next step
Now you have a perfect strategy you probably want to backtest it.
Your next step is to learn [How to use the Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md).
+253
View File
@@ -0,0 +1,253 @@
# Bot usage
This page explains the difference parameters of the bot and how to run it.
## Table of Contents
- [Bot commands](#bot-commands)
- [Backtesting commands](#backtesting-commands)
- [Hyperopt commands](#hyperopt-commands)
## Bot commands
```
usage: freqtrade [-h] [-v] [--version] [-c PATH] [-d PATH] [-s NAME]
[--strategy-path PATH] [--dynamic-whitelist [INT]]
[--db-url PATH]
{backtesting,hyperopt} ...
Simple High Frequency Trading Bot for crypto currencies
positional arguments:
{backtesting,hyperopt}
backtesting backtesting module
hyperopt hyperopt module
optional arguments:
-h, --help show this help message and exit
-v, --verbose be verbose
--version show program's version number and exit
-c PATH, --config PATH
specify configuration file (default: config.json)
-d PATH, --datadir PATH
path to backtest data
-s NAME, --strategy NAME
specify strategy class name (default: DefaultStrategy)
--strategy-path PATH specify additional strategy lookup path
--dynamic-whitelist [INT]
dynamically generate and update whitelist based on 24h
BaseVolume (default: 20) DEPRECATED
--db-url PATH Override trades database URL, this is useful if
dry_run is enabled or in custom deployments (default:
sqlite:///tradesv3.sqlite)
```
### How to use a different config file?
The bot allows you to select which config file you want to use. Per
default, the bot will load the file `./config.json`
```bash
python3 ./freqtrade/main.py -c path/far/far/away/config.json
```
### How to use --strategy?
This parameter will allow you to load your custom strategy class.
Per default without `--strategy` or `-s` the bot will load the
`DefaultStrategy` included with the bot (`freqtrade/strategy/default_strategy.py`).
The bot will search your strategy file within `user_data/strategies` and `freqtrade/strategy`.
To load a strategy, simply pass the class name (e.g.: `CustomStrategy`) in this parameter.
**Example:**
In `user_data/strategies` you have a file `my_awesome_strategy.py` which has
a strategy class called `AwesomeStrategy` to load it:
```bash
python3 ./freqtrade/main.py --strategy AwesomeStrategy
```
If the bot does not find your strategy file, it will display in an error
message the reason (File not found, or errors in your code).
Learn more about strategy file in [optimize your bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md).
### How to use --strategy-path?
This parameter allows you to add an additional strategy lookup path, which gets
checked before the default locations (The passed path must be a folder!):
```bash
python3 ./freqtrade/main.py --strategy AwesomeStrategy --strategy-path /some/folder
```
#### How to install a strategy?
This is very simple. Copy paste your strategy file into the folder
`user_data/strategies` or use `--strategy-path`. And voila, the bot is ready to use it.
### How to use --dynamic-whitelist?
> Dynamic-whitelist is deprecated. Please move your configurations to the configuration as outlined [here](docs/configuration.md#Dynamic-Pairlists)
Per default `--dynamic-whitelist` will retrieve the 20 currencies based
on BaseVolume. This value can be changed when you run the script.
**By Default**
Get the 20 currencies based on BaseVolume.
```bash
python3 ./freqtrade/main.py --dynamic-whitelist
```
**Customize the number of currencies to retrieve**
Get the 30 currencies based on BaseVolume.
```bash
python3 ./freqtrade/main.py --dynamic-whitelist 30
```
**Exception**
`--dynamic-whitelist` must be greater than 0. If you enter 0 or a
negative value (e.g -2), `--dynamic-whitelist` will use the default
value (20).
### How to use --db-url?
When you run the bot in Dry-run mode, per default no transactions are
stored in a database. If you want to store your bot actions in a DB
using `--db-url`. This can also be used to specify a custom database
in production mode. Example command:
```bash
python3 ./freqtrade/main.py -c config.json --db-url sqlite:///tradesv3.dry_run.sqlite
```
## Backtesting commands
Backtesting also uses the config specified via `-c/--config`.
```
usage: freqtrade backtesting [-h] [-i TICKER_INTERVAL] [--eps] [--dmmp]
[--timerange TIMERANGE] [-l] [-r]
[--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]]
[--export EXPORT] [--export-filename PATH]
optional arguments:
-h, --help show this help message and exit
-i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL
specify ticker interval (1m, 5m, 30m, 1h, 1d)
--eps, --enable-position-stacking
Allow buying the same pair multiple times (position
stacking)
--dmmp, --disable-max-market-positions
Disable applying `max_open_trades` during backtest
(same as setting `max_open_trades` to a very high
number)
--timerange TIMERANGE
specify what timerange of data to use.
-l, --live using live data
-r, --refresh-pairs-cached
refresh the pairs files in tests/testdata with the
latest data from the exchange. Use it if you want to
run your backtesting with up-to-date data.
--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]
Provide a commaseparated list of strategies to
backtest Please note that ticker-interval needs to be
set either in config or via command line. When using
this together with --export trades, the strategy-name
is injected into the filename (so backtest-data.json
becomes backtest-data-DefaultStrategy.json
--export EXPORT export backtest results, argument are: trades Example
--export=trades
--export-filename PATH
Save backtest results to this filename requires
--export to be set as well Example --export-
filename=user_data/backtest_data/backtest_today.json
(default: user_data/backtest_data/backtest-
result.json)
```
### How to use --refresh-pairs-cached parameter?
The first time your run Backtesting, it will take the pairs you have
set in your config file and download data from Bittrex.
If for any reason you want to update your data set, you use
`--refresh-pairs-cached` to force Backtesting to update the data it has.
**Use it only if you want to update your data set. You will not be able
to come back to the previous version.**
To test your strategy with latest data, we recommend continuing using
the parameter `-l` or `--live`.
## Hyperopt commands
To optimize your strategy, you can use hyperopt parameter hyperoptimization
to find optimal parameter values for your stategy.
```
usage: freqtrade hyperopt [-h] [-i TICKER_INTERVAL] [--eps] [--dmmp]
[--timerange TIMERANGE] [-e INT]
[-s {all,buy,roi,stoploss} [{all,buy,roi,stoploss} ...]]
optional arguments:
-h, --help show this help message and exit
-i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL
specify ticker interval (1m, 5m, 30m, 1h, 1d)
--eps, --enable-position-stacking
Allow buying the same pair multiple times (position
stacking)
--dmmp, --disable-max-market-positions
Disable applying `max_open_trades` during backtest
(same as setting `max_open_trades` to a very high
number)
--timerange TIMERANGE
specify what timerange of data to use.
--hyperopt PATH specify hyperopt file (default:
freqtrade/optimize/default_hyperopt.py)
-e INT, --epochs INT specify number of epochs (default: 100)
-s {all,buy,roi,stoploss} [{all,buy,roi,stoploss} ...], --spaces {all,buy,roi,stoploss} [{all,buy,roi,stoploss} ...]
Specify which parameters to hyperopt. Space separate
list. Default: all
```
## Edge commands
To know your trade expectacny and winrate against historical data, you can use Edge.
```
usage: main.py edge [-h] [-i TICKER_INTERVAL] [--timerange TIMERANGE] [-r]
[--stoplosses STOPLOSS_RANGE]
optional arguments:
-h, --help show this help message and exit
-i TICKER_INTERVAL, --ticker-interval TICKER_INTERVAL
specify ticker interval (1m, 5m, 30m, 1h, 1d)
--timerange TIMERANGE
specify what timerange of data to use.
-r, --refresh-pairs-cached
refresh the pairs files in tests/testdata with the
latest data from the exchange. Use it if you want to
run your edge with up-to-date data.
--stoplosses STOPLOSS_RANGE
defines a range of stoploss against which edge will
assess the strategythe format is "min,max,step"
(without any space).example:
--stoplosses=-0.01,-0.1,-0.001
```
To understand edge and how to read the results, please read the [edge documentation](edge.md).
## A parameter missing in the configuration?
All parameters for `main.py`, `backtesting`, `hyperopt` are referenced
in [misc.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/misc.py#L84)
## Next step
The optimal strategy of the bot will change with time depending of the market trends. The next step is to
[optimize your bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md).
+340
View File
@@ -0,0 +1,340 @@
# Configure the bot
This page explains how to configure your `config.json` file.
## Table of Contents
- [Bot commands](#bot-commands)
- [Backtesting commands](#backtesting-commands)
- [Hyperopt commands](#hyperopt-commands)
## Setup config.json
We recommend to copy and use the `config.json.example` as a template
for your bot configuration.
The table below will list all configuration parameters.
| Command | Default | Mandatory | Description |
|----------|---------|----------|-------------|
| `max_open_trades` | 3 | Yes | Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades)
| `stake_currency` | BTC | Yes | Crypto-currency used for trading.
| `stake_amount` | 0.05 | Yes | Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to 'unlimited' to allow the bot to use all avaliable balance.
| `ticker_interval` | [1m, 5m, 30m, 1h, 1d] | No | The ticker interval to use (1min, 5 min, 30 min, 1 hour or 1 day). Default is 5 minutes
| `fiat_display_currency` | USD | Yes | Fiat currency used to show your profits. More information below.
| `dry_run` | true | Yes | Define if the bot must be in Dry-run or production mode.
| `process_only_new_candles` | false | No | If set to true indicators are processed only once a new candle arrives. If false each loop populates the indicators, this will mean the same candle is processed many times creating system load but can be useful of your strategy depends on tick data not only candle. Can be set either in Configuration or in the strategy.
| `minimal_roi` | See below | No | Set the threshold in percent the bot will use to sell a trade. More information below. If set, this parameter will override `minimal_roi` from your strategy file.
| `stoploss` | -0.10 | No | Value of the stoploss in percent used by the bot. More information below. If set, this parameter will override `stoploss` from your strategy file.
| `trailing_stop` | false | No | Enables trailing stop-loss (based on `stoploss` in either configuration or strategy file).
| `trailing_stop_positve` | 0 | No | Changes stop-loss once profit has been reached.
| `trailing_stop_positve_offset` | 0 | No | Offset on when to apply `trailing_stop_positive`. Percentage value which should be positive.
| `unfilledtimeout.buy` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled buy order to complete, after which the order will be cancelled.
| `unfilledtimeout.sell` | 10 | Yes | How long (in minutes) the bot will wait for an unfilled sell order to complete, after which the order will be cancelled.
| `bid_strategy.ask_last_balance` | 0.0 | Yes | Set the bidding price. More information below.
| `bid_strategy.use_order_book` | false | No | Allows buying of pair using the rates in Order Book Bids.
| `bid_strategy.order_book_top` | 0 | No | Bot will use the top N rate in Order Book Bids. Ie. a value of 2 will allow the bot to pick the 2nd bid rate in Order Book Bids.
| `bid_strategy. check_depth_of_market.enabled` | false | No | Does not buy if the % difference of buy orders and sell orders is met in Order Book.
| `bid_strategy. check_depth_of_market.bids_to_ask_delta` | 0 | No | The % difference of buy orders and sell orders found in Order Book. A value lesser than 1 means sell orders is greater, while value greater than 1 means buy orders is higher.
| `ask_strategy.use_order_book` | false | No | Allows selling of open traded pair using the rates in Order Book Asks.
| `ask_strategy.order_book_min` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate.
| `ask_strategy.order_book_max` | 0 | No | Bot will scan from the top min to max Order Book Asks searching for a profitable rate.
| `order_types` | None | No | Configure order-types depending on the action (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`). [More information below](#understand-order_types).
| `order_time_in_force` | None | No | Configure time in force for buy and sell orders. [More information below](#understand-order_time_in_force).
| `exchange.name` | bittrex | Yes | Name of the exchange class to use. [List below](#user-content-what-values-for-exchangename).
| `exchange.key` | key | No | API key to use for the exchange. Only required when you are in production mode.
| `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode.
| `exchange.pair_whitelist` | [] | No | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param.
| `exchange.pair_blacklist` | [] | No | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param.
| `exchange.ccxt_rate_limit` | True | No | DEPRECATED!! Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange.
| `exchange.ccxt_config` | None | No | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation)
| `exchange.ccxt_async_config` | None | No | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation)
| `edge` | false | No | Please refer to [edge configuration document](edge.md) for detailed explanation.
| `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`.
| `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision.
| `experimental.ignore_roi_if_buy_signal` | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal`
| `pairlist.method` | StaticPairList | No | Use Static whitelist. [More information below](#dynamic-pairlists).
| `pairlist.config` | None | No | Additional configuration for dynamic pairlists. [More information below](#dynamic-pairlists).
| `telegram.enabled` | true | Yes | Enable or not the usage of Telegram.
| `telegram.token` | token | No | Your Telegram bot token. Only required if `telegram.enabled` is `true`.
| `telegram.chat_id` | chat_id | No | Your personal Telegram account id. Only required if `telegram.enabled` is `true`.
| `webhook.enabled` | false | No | Enable usage of Webhook notifications
| `webhook.url` | false | No | URL for the webhook. Only required if `webhook.enabled` is `true`. See the [webhook documentation](webhook-config.md) for more details.
| `webhook.webhookbuy` | false | No | Payload to send on buy. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details.
| `webhook.webhooksell` | false | No | Payload to send on sell. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details.
| `webhook.webhookstatus` | false | No | Payload to send on status calls. Only required if `webhook.enabled` is `true`. See the [webhook documentationV](webhook-config.md) for more details.
| `db_url` | `sqlite:///tradesv3.sqlite` | No | Declares database URL to use. NOTE: This defaults to `sqlite://` if `dry_run` is `True`.
| `initial_state` | running | No | Defines the initial application state. More information below.
| `forcebuy_enable` | false | No | Enables the RPC Commands to force a buy. More information below.
| `strategy` | DefaultStrategy | No | Defines Strategy class to use.
| `strategy_path` | null | No | Adds an additional strategy lookup path (must be a folder).
| `internals.process_throttle_secs` | 5 | Yes | Set the process throttle. Value in second.
The definition of each config parameters is in [misc.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/misc.py#L205).
### Understand stake_amount
`stake_amount` is an amount of crypto-currency your bot will use for each trade.
The minimal value is 0.0005. If there is not enough crypto-currency in
the account an exception is generated.
To allow the bot to trade all the avaliable `stake_currency` in your account set `stake_amount` = `unlimited`.
In this case a trade amount is calclulated as `currency_balanse / (max_open_trades - current_open_trades)`.
### Understand minimal_roi
`minimal_roi` is a JSON object where the key is a duration
in minutes and the value is the minimum ROI in percent.
See the example below:
```
"minimal_roi": {
"40": 0.0, # Sell after 40 minutes if the profit is not negative
"30": 0.01, # Sell after 30 minutes if there is at least 1% profit
"20": 0.02, # Sell after 20 minutes if there is at least 2% profit
"0": 0.04 # Sell immediately if there is at least 4% profit
},
```
Most of the strategy files already include the optimal `minimal_roi`
value. This parameter is optional. If you use it, it will take over the
`minimal_roi` value from the strategy file.
### Understand stoploss
`stoploss` is loss in percentage that should trigger a sale.
For example value `-0.10` will cause immediate sell if the
profit dips below -10% for a given trade. This parameter is optional.
Most of the strategy files already include the optimal `stoploss`
value. This parameter is optional. If you use it, it will take over the
`stoploss` value from the strategy file.
### Understand trailing stoploss
Go to the [trailing stoploss Documentation](stoploss.md) for details on trailing stoploss.
### Understand initial_state
`initial_state` is an optional field that defines the initial application state.
Possible values are `running` or `stopped`. (default=`running`)
If the value is `stopped` the bot has to be started with `/start` first.
### Understand forcebuy_enable
`forcebuy_enable` enables the usage of forcebuy commands via Telegram.
This is disabled for security reasons by default, and will show a warning message on startup if enabled.
You send `/forcebuy ETH/BTC` to the bot, who buys the pair and holds it until a regular sell-signal appears (ROI, stoploss, /forcesell).
Can be dangerous with some strategies, so use with care
See [the telegram documentation](telegram-usage.md) for details on usage.
### Understand process_throttle_secs
`process_throttle_secs` is an optional field that defines in seconds how long the bot should wait
before asking the strategy if we should buy or a sell an asset. After each wait period, the strategy is asked again for
every opened trade wether or not we should sell, and for all the remaining pairs (either the dynamic list of pairs or
the static list of pairs) if we should buy.
### Understand ask_last_balance
`ask_last_balance` sets the bidding price. Value `0.0` will use `ask` price, `1.0` will
use the `last` price and values between those interpolate between ask and last
price. Using `ask` price will guarantee quick success in bid, but bot will also
end up paying more then would probably have been necessary.
### Understand order_types
`order_types` contains a dict mapping order-types to market-types as well as stoploss on or off exchange type. This allows to buy using limit orders, sell using limit-orders, and create stoploss orders using market. It also allows to set the stoploss "on exchange" which means stoploss order would be placed immediately once the buy order is fulfilled.
This can be set in the configuration or in the strategy. Configuration overwrites strategy configurations.
If this is configured, all 4 values (`"buy"`, `"sell"`, `"stoploss"`, `"stoploss_on_exchange"`) need to be present, otherwise the bot warn about it and will fail to start.
The below is the default which is used if this is not configured in either Strategy or configuration.
``` python
"order_types": {
"buy": "limit",
"sell": "limit",
"stoploss": "market",
"stoploss_on_exchange": False
},
```
**NOTE**: Not all exchanges support "market" orders.
The following message will be shown if your exchange does not support market orders: `"Exchange <yourexchange> does not support market orders."`
### Understand order_time_in_force
Order time in force defines the policy by which the order is executed on the exchange. Three commonly used time in force are:<br/>
**GTC (Goog Till Canceled):**
This is most of the time the default time in force. It means the order will remain on exchange till it is canceled by user. It can be fully or partially fulfilled. If partially fulfilled, the remaining will stay on the exchange till cancelled.<br/>
**FOK (Full Or Kill):**
It means if the order is not executed immediately AND fully then it is canceled by the exchange.<br/>
**IOC (Immediate Or Canceled):**
It is the same as FOK (above) except it can be partially fulfilled. The remaining part is automatically cancelled by the exchange.
<br/>
`order_time_in_force` contains a dict buy and sell time in force policy. This can be set in the configuration or in the strategy. Configuration overwrites strategy configurations.<br/>
possible values are: `gtc` (default), `fok` or `ioc`.<br/>
``` python
"order_time_in_force": {
"buy": "gtc",
"sell": "gtc"
},
```
**NOTE**: This is an ongoing work. For now it is supported only for binance and only for buy orders. Please don't change the default value unless you know what you are doing.<br/>
### What values for exchange.name?
Freqtrade is based on [CCXT library](https://github.com/ccxt/ccxt) that supports 115 cryptocurrency
exchange markets and trading APIs. The complete up-to-date list can be found in the
[CCXT repo homepage](https://github.com/ccxt/ccxt/tree/master/python). However, the bot was tested
with only Bittrex and Binance.
The bot was tested with the following exchanges:
- [Bittrex](https://bittrex.com/): "bittrex"
- [Binance](https://www.binance.com/): "binance"
Feel free to test other exchanges and submit your PR to improve the bot.
### What values for fiat_display_currency?
`fiat_display_currency` set the base currency to use for the conversion from coin to fiat in Telegram.
The valid values are: "AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK", "EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY", "KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN", "RUB", "SEK", "SGD", "THB", "TRY", "TWD", "ZAR", "USD".
In addition to central bank currencies, a range of cryto currencies are supported.
The valid values are: "BTC", "ETH", "XRP", "LTC", "BCH", "USDT".
## Switch to dry-run mode
We recommend starting the bot in dry-run mode to see how your bot will
behave and how is the performance of your strategy. In Dry-run mode the
bot does not engage your money. It only runs a live simulation without
creating trades.
### To switch your bot in Dry-run mode:
1. Edit your `config.json` file
2. Switch dry-run to true and specify db_url for a persistent db
```json
"dry_run": true,
"db_url": "sqlite///tradesv3.dryrun.sqlite",
```
3. Remove your Exchange API key (change them by fake api credentials)
```json
"exchange": {
"name": "bittrex",
"key": "key",
"secret": "secret",
...
}
```
Once you will be happy with your bot performance, you can switch it to
production mode.
### Dynamic Pairlists
Dynamic pairlists select pairs for you based on the logic configured.
The bot runs against all pairs (with that stake) on the exchange, and a number of assets (`number_assets`) is selected based on the selected criteria.
By *default*, a Static Pairlist is used (configured as `"pair_whitelist"` under the `"exchange"` section of this configuration).
#### Available Pairlist methods
* `"StaticPairList"`
* uses configuration from `exchange.pair_whitelist` and `exchange.pair_blacklist`
* `"VolumePairList"`
* Formerly available as `--dynamic-whitelist [<number_assets>]`
* Selects `number_assets` top pairs based on `sort_key`, which can be one of `askVolume`, `bidVolume` and `quoteVolume`, defaults to `quoteVolume`.
```json
"pairlist": {
"method": "VolumePairList",
"config": {
"number_assets": 20,
"sort_key": "quoteVolume"
}
},
```
## Switch to production mode
In production mode, the bot will engage your money. Be careful a wrong
strategy can lose all your money. Be aware of what you are doing when
you run it in production mode.
### To switch your bot in production mode
1. Edit your `config.json` file
2. Switch dry-run to false and don't forget to adapt your database URL if set
```json
"dry_run": false,
```
3. Insert your Exchange API key (change them by fake api keys)
```json
"exchange": {
"name": "bittrex",
"key": "af8ddd35195e9dc500b9a6f799f6f5c93d89193b",
"secret": "08a9dc6db3d7b53e1acebd9275677f4b0a04f1a5",
...
}
```
If you have not your Bittrex API key yet, [see our tutorial](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md).
### Using proxy with FreqTrade
To use a proxy with freqtrade, add the kwarg `"aiohttp_trust_env"=true` to the `"ccxt_async_kwargs"` dict in the exchange section of the configuration.
An example for this can be found in `config_full.json.example`
``` json
"ccxt_async_config": {
"aiohttp_trust_env": true
}
```
Then, export your proxy settings using the variables `"HTTP_PROXY"` and `"HTTPS_PROXY"` set to the appropriate values
``` bash
export HTTP_PROXY="http://addr:port"
export HTTPS_PROXY="http://addr:port"
freqtrade
```
### Embedding Strategies
FreqTrade provides you with with an easy way to embed the strategy into your configuration file.
This is done by utilizing BASE64 encoding and providing this string at the strategy configuration field,
in your chosen config file.
#### Encoding a string as BASE64
This is a quick example, how to generate the BASE64 string in python
```python
from base64 import urlsafe_b64encode
with open(file, 'r') as f:
content = f.read()
content = urlsafe_b64encode(content.encode('utf-8'))
```
The variable 'content', will contain the strategy file in a BASE64 encoded form. Which can now be set in your configurations file as following
```json
"strategy": "NameOfStrategy:BASE64String"
```
Please ensure that 'NameOfStrategy' is identical to the strategy name!
## Next step
Now you have configured your config.json, the next step is to [start your bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md).
+70
View File
@@ -0,0 +1,70 @@
# Development Help
This page is intended for developers of FreqTrade, people who want to contribute to the FreqTrade codebase or documentation, or people who want to understand the source code of the application they're running.
All contributions, bug reports, bug fixes, documentation improvements, enhancements and ideas are welcome. We [track issues](https://github.com/freqtrade/freqtrade/issues) on [GitHub](https://github.com) and also have a dev channel in [slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE) where you can ask questions.
## Module
### Dynamic Pairlist
You have a great idea for a new pair selection algorithm you would like to try out? Great.
Hopefully you also want to contribute this back upstream.
Whatever your motivations are - This should get you off the ground in trying to develop a new Pairlist provider.
First of all, have a look at the [VolumePairList](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/pairlist/VolumePairList.py) provider, and best copy this file with a name of your new Pairlist Provider.
This is a simple provider, which however serves as a good example on how to start developing.
Next, modify the classname of the provider (ideally align this with the Filename).
The base-class provides the an instance of the bot (`self._freqtrade`), as well as the configuration (`self._config`), and initiates both `_blacklist` and `_whitelist`.
```python
self._freqtrade = freqtrade
self._config = config
self._whitelist = self._config['exchange']['pair_whitelist']
self._blacklist = self._config['exchange'].get('pair_blacklist', [])
```
Now, let's step through the methods which require actions:
#### configuration
Configuration for PairListProvider is done in the bot configuration file in the element `"pairlist"`.
This Pairlist-object may contain a `"config"` dict with additional configurations for the configured pairlist.
By convention, `"number_assets"` is used to specify the maximum number of pairs to keep in the whitelist. Please follow this to ensure a consistent user experience.
Additional elements can be configured as needed. `VolumePairList` uses `"sort_key"` to specify the sorting value - however feel free to specify whatever is necessary for your great algorithm to be successfull and dynamic.
#### short_desc
Returns a description used for Telegram messages.
This should contain the name of the Provider, as well as a short description containing the number of assets. Please follow the format `"PairlistName - top/bottom X pairs"`.
#### refresh_pairlist
Override this method and run all calculations needed in this method.
This is called with each iteration of the bot - so consider implementing caching for compute/network heavy calculations.
Assign the resulting whiteslist to `self._whitelist` and `self._blacklist` respectively. These will then be used to run the bot in this iteration. Pairs with open trades will be added to the whitelist to have the sell-methods run correctly.
Please also run `self._validate_whitelist(pairs)` and to check and remove pairs with inactive markets. This function is available in the Parent class (`StaticPairList`) and should ideally not be overwritten.
##### sample
``` python
def refresh_pairlist(self) -> None:
# Generate dynamic whitelist
pairs = self._gen_pair_whitelist(self._config['stake_currency'], self._sort_key)
# Validate whitelist to only have active market pairs
self._whitelist = self._validate_whitelist(pairs)[:self._number_pairs]
```
#### _gen_pair_whitelist
This is a simple method used by `VolumePairList` - however serves as a good example.
It implements caching (`@cached(TTLCache(maxsize=1, ttl=1800))`) as well as a configuration option to allow different (but similar) strategies to work with the same PairListProvider.
+216
View File
@@ -0,0 +1,216 @@
# Edge positioning
This page explains how to use Edge Positioning module in your bot in order to enter into a trade only if the trade has a reasonable win rate and risk reward ratio, and consequently adjust your position size and stoploss.
**NOTICE:** Edge positioning is not compatible with dynamic whitelist. it overrides dynamic whitelist.
**NOTICE2:** Edge won't consider anything else than buy/sell/stoploss signals. So trailing stoploss, ROI, and everything else will be ignored in its calculation.
## Table of Contents
- [Introduction](#introduction)
- [How does it work?](#how-does-it-work?)
- [Configurations](#configurations)
- [Running Edge independently](#running-edge-independently)
## Introduction
Trading is all about probability. No one can claim that he has a strategy working all the time. You have to assume that sometimes you lose.<br/><br/>
But it doesn't mean there is no rule, it only means rules should work "most of the time". Let's play a game: we toss a coin, heads: I give you 10$, tails: You give me 10$. Is it an interesting game ? no, it is quite boring, isn't it?<br/><br/>
But let's say the probability that we have heads is 80%, and the probability that we have tails is 20%. Now it is becoming interesting ...
That means 10$ x 80% versus 10$ x 20%. 8$ versus 2$. That means over time you will win 8$ risking only 2$ on each toss of coin.<br/><br/>
Let's complicate it more: you win 80% of the time but only 2$, I win 20% of the time but 8$. The calculation is: 80% * 2$ versus 20% * 8$. It is becoming boring again because overtime you win $1.6$ (80% x 2$) and me $1.6 (20% * 8$) too.<br/><br/>
The question is: How do you calculate that? how do you know if you wanna play?
The answer comes to two factors:
- Win Rate
- Risk Reward Ratio
### Win Rate
Means over X trades what is the percentage of winning trades to total number of trades (note that we don't consider how much you gained but only If you won or not).
`W = (Number of winning trades) / (Number of losing trades)`
### Risk Reward Ratio
Risk Reward Ratio is a formula used to measure the expected gains of a given investment against the risk of loss. It is basically what you potentially win divided by what you potentially lose:
`R = Profit / Loss`
Over time, on many trades, you can calculate your risk reward by dividing your average profit on winning trades by your average loss on losing trades:
`Average profit = (Sum of profits) / (Number of winning trades)`
`Average loss = (Sum of losses) / (Number of losing trades)`
`R = (Average profit) / (Average loss)`
### Expectancy
At this point we can combine W and R to create an expectancy ratio. This is a simple process of multiplying the risk reward ratio by the percentage of winning trades, and subtracting the percentage of losing trades, which is calculated as follows:
Expectancy Ratio = (Risk Reward Ratio x Win Rate) Loss Rate
So lets say your Win rate is 28% and your Risk Reward Ratio is 5:
`Expectancy = (5 * 0.28) - 0.72 = 0.68`
Superficially, this means that on average you expect this strategys trades to return .68 times the size of your losers. This is important for two reasons: First, it may seem obvious, but you know right away that you have a positive return. Second, you now have a number you can compare to other candidate systems to make decisions about which ones you employ.
It is important to remember that any system with an expectancy greater than 0 is profitable using past data. The key is finding one that will be profitable in the future.
You can also use this number to evaluate the effectiveness of modifications to this system.
**NOTICE:** It's important to keep in mind that Edge is testing your expectancy using historical data , there's no guarantee that you will have a similar edge in the future. It's still vital to do this testing in order to build confidence in your methodology, but be wary of "curve-fitting" your approach to the historical data as things are unlikely to play out the exact same way for future trades.
## How does it work?
If enabled in config, Edge will go through historical data with a range of stoplosses in order to find buy and sell/stoploss signals. It then calculates win rate and expectancy over X trades for each stoploss. Here is an example:
| Pair | Stoploss | Win Rate | Risk Reward Ratio | Expectancy |
|----------|:-------------:|-------------:|------------------:|-----------:|
| XZC/ETH | -0.03 | 0.52 |1.359670 | 0.228 |
| XZC/ETH | -0.01 | 0.50 |1.176384 | 0.088 |
| XZC/ETH | -0.02 | 0.51 |1.115941 | 0.079 |
The goal here is to find the best stoploss for the strategy in order to have the maximum expectancy. In the above example stoploss at 3% leads to the maximum expectancy according to historical data.
Edge then forces stoploss to your strategy dynamically.
### Position size
Edge dictates the stake amount for each trade to the bot according to the following factors:
- Allowed capital at risk
- Stoploss
Allowed capital at risk is calculated as follows:
**allowed capital at risk** = **capital_available_percentage** X **allowed risk per trade**
**Stoploss** is calculated as described above against historical data.
Your position size then will be:
**position size** = **allowed capital at risk** / **stoploss**
Example:<br/>
Let's say the stake currency is ETH and you have 10 ETH on the exchange, your **capital_available_percentage** is 50% and you would allow 1% of risk for each trade. thus your available capital for trading is **10 x 0.5 = 5 ETH** and allowed capital at risk would be **5 x 0.01 = 0.05 ETH**. <br/>
Let's assume Edge has calculated that for **XLM/ETH** market your stoploss should be at 2%. So your position size will be **0.05 / 0.02 = 2.5ETH**.<br/>
Bot takes a position of 2.5ETH on XLM/ETH (call it trade 1). Up next, you receive another buy signal while trade 1 is still open. This time on BTC/ETH market. Edge calculated stoploss for this market at 4%. So your position size would be 0.05 / 0.04 = 1.25ETH (call it trade 2).<br/>
Note that available capital for trading didnt change for trade 2 even if you had already trade 1. The available capital doesnt mean the free amount on your wallet.<br/>
Now you have two trades open. The Bot receives yet another buy signal for another market: **ADA/ETH**. This time the stoploss is calculated at 1%. So your position size is **0.05 / 0.01 = 5ETH**. But there are already 4ETH blocked in two previous trades. So the position size for this third trade would be 1ETH.<br/>
Available capital doesnt change before a position is sold. Lets assume that trade 1 receives a sell signal and it is sold with a profit of 1ETH. Your total capital on exchange would be 11 ETH and the available capital for trading becomes 5.5ETH. <br/>
So the Bot receives another buy signal for trade 4 with a stoploss at 2% then your position size would be **0.055 / 0.02 = 2.75**.
## Configurations
Edge has following configurations:
#### enabled
If true, then Edge will run periodically.<br/>
(default to false)
#### process_throttle_secs
How often should Edge run in seconds? <br/>
(default to 3600 so one hour)
#### calculate_since_number_of_days
Number of days of data against which Edge calculates Win Rate, Risk Reward and Expectancy
Note that it downloads historical data so increasing this number would lead to slowing down the bot.<br/>
(default to 7)
#### capital_available_percentage
This is the percentage of the total capital on exchange in stake currency. <br/>
As an example if you have 10 ETH available in your wallet on the exchange and this value is 0.5 (which is 50%), then the bot will use a maximum amount of 5 ETH for trading and considers it as available capital.<br/>
(default to 0.5)
#### allowed_risk
Percentage of allowed risk per trade.<br/>
(default to 0.01 [1%])
#### stoploss_range_min
Minimum stoploss.<br/>
(default to -0.01)
#### stoploss_range_max
Maximum stoploss.<br/>
(default to -0.10)
#### stoploss_range_step
As an example if this is set to -0.01 then Edge will test the strategy for [-0.01, -0,02, -0,03 ..., -0.09, -0.10] ranges.
Note than having a smaller step means having a bigger range which could lead to slow calculation. <br/>
if you set this parameter to -0.001, you then slow down the Edge calculation by a factor of 10. <br/>
(default to -0.01)
#### minimum_winrate
It filters pairs which don't have at least minimum_winrate.
This comes handy if you want to be conservative and don't comprise win rate in favor of risk reward ratio.<br/>
(default to 0.60)
#### minimum_expectancy
It filters paris which have an expectancy lower than this number .
Having an expectancy of 0.20 means if you put 10$ on a trade you expect a 12$ return.<br/>
(default to 0.20)
#### min_trade_number
When calculating W and R and E (expectancy) against historical data, you always want to have a minimum number of trades. The more this number is the more Edge is reliable. Having a win rate of 100% on a single trade doesn't mean anything at all. But having a win rate of 70% over past 100 trades means clearly something. <br/>
(default to 10, it is highly recommended not to decrease this number)
#### max_trade_duration_minute
Edge will filter out trades with long duration. If a trade is profitable after 1 month, it is hard to evaluate the strategy based on it. But if most of trades are profitable and they have maximum duration of 30 minutes, then it is clearly a good sign.<br/>
**NOTICE:** While configuring this value, you should take into consideration your ticker interval. as an example filtering out trades having duration less than one day for a strategy which has 4h interval does not make sense. default value is set assuming your strategy interval is relatively small (1m or 5m, etc).<br/>
(default to 1 day, 1440 = 60 * 24)
#### remove_pumps
Edge will remove sudden pumps in a given market while going through historical data. However, given that pumps happen very often in crypto markets, we recommend you keep this off.<br/>
(default to false)
## Running Edge independently
You can run Edge independently in order to see in details the result. Here is an example:
```bash
python3 ./freqtrade/main.py edge
```
An example of its output:
| pair | stoploss | win rate | risk reward ratio | required risk reward | expectancy | total number of trades | average duration (min) |
|:----------|-----------:|-----------:|--------------------:|-----------------------:|-------------:|-------------------------:|-------------------------:|
| AGI/BTC | -0.02 | 0.64 | 5.86 | 0.56 | 3.41 | 14 | 54 |
| NXS/BTC | -0.03 | 0.64 | 2.99 | 0.57 | 1.54 | 11 | 26 |
| LEND/BTC | -0.02 | 0.82 | 2.05 | 0.22 | 1.50 | 11 | 36 |
| VIA/BTC | -0.01 | 0.55 | 3.01 | 0.83 | 1.19 | 11 | 48 |
| MTH/BTC | -0.09 | 0.56 | 2.82 | 0.80 | 1.12 | 18 | 52 |
| ARDR/BTC | -0.04 | 0.42 | 3.14 | 1.40 | 0.73 | 12 | 42 |
| BCPT/BTC | -0.01 | 0.71 | 1.34 | 0.40 | 0.67 | 14 | 30 |
| WINGS/BTC | -0.02 | 0.56 | 1.97 | 0.80 | 0.65 | 27 | 42 |
| VIBE/BTC | -0.02 | 0.83 | 0.91 | 0.20 | 0.59 | 12 | 35 |
| MCO/BTC | -0.02 | 0.79 | 0.97 | 0.27 | 0.55 | 14 | 31 |
| GNT/BTC | -0.02 | 0.50 | 2.06 | 1.00 | 0.53 | 18 | 24 |
| HOT/BTC | -0.01 | 0.17 | 7.72 | 4.81 | 0.50 | 209 | 7 |
| SNM/BTC | -0.03 | 0.71 | 1.06 | 0.42 | 0.45 | 17 | 38 |
| APPC/BTC | -0.02 | 0.44 | 2.28 | 1.27 | 0.44 | 25 | 43 |
| NEBL/BTC | -0.03 | 0.63 | 1.29 | 0.58 | 0.44 | 19 | 59 |
### Update cached pairs with the latest data
```bash
python3 ./freqtrade/main.py edge --refresh-pairs-cached
```
### Precising stoploss range
```bash
python3 ./freqtrade/main.py edge --stoplosses=-0.01,-0.1,-0.001 #min,max,step
```
### Advanced use of timerange
```bash
python3 ./freqtrade/main.py edge --timerange=20181110-20181113
```
Doing --timerange=-200 will get the last 200 timeframes from your inputdata. You can also specify specific dates, or a range span indexed by start and stop.
The full timerange specification:
* Use last 123 tickframes of data: --timerange=-123
* Use first 123 tickframes of data: --timerange=123-
* Use tickframes from line 123 through 456: --timerange=123-456
* Use tickframes till 2018/01/31: --timerange=-20180131
* Use tickframes since 2018/01/31: --timerange=20180131-
* Use tickframes since 2018/01/31 till 2018/03/01 : --timerange=20180131-20180301
* Use tickframes between POSIX timestamps 1527595200 1527618600: --timerange=1527595200-1527618600
+71
View File
@@ -0,0 +1,71 @@
# freqtrade FAQ
#### I have waited 5 minutes, why hasn't the bot made any trades yet?!
Depending on the buy strategy, the amount of whitelisted coins, the
situation of the market etc, it can take up to hours to find good entry
position for a trade. Be patient!
#### I have made 12 trades already, why is my total profit negative?!
I understand your disappointment but unfortunately 12 trades is just
not enough to say anything. If you run backtesting, you can see that our
current algorithm does leave you on the plus side, but that is after
thousands of trades and even there, you will be left with losses on
specific coins that you have traded tens if not hundreds of times. We
of course constantly aim to improve the bot but it will _always_ be a
gamble, which should leave you with modest wins on monthly basis but
you can't say much from few trades.
#### Id like to change the stake amount. Can I just stop the bot with
/stop and then change the config.json and run it again?
Not quite. Trades are persisted to a database but the configuration is
currently only read when the bot is killed and restarted. `/stop` more
like pauses. You can stop your bot, adjust settings and start it again.
#### I want to improve the bot with a new strategy
That's great. We have a nice backtesting and hyperoptimizing setup. See
the tutorial [here|Testing-new-strategies-with-Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#hyperopt-commands).
#### Is there a setting to only SELL the coins being held and not
perform anymore BUYS?
You can use the `/forcesell all` command from Telegram.
### How many epoch do I need to get a good Hyperopt result?
Per default Hyperopts without `-e` or `--epochs` parameter will only
run 100 epochs, means 100 evals of your triggers, guards, .... Too few
to find a great result (unless if you are very lucky), so you probably
have to run it for 10.000 or more. But it will take an eternity to
compute.
We recommend you to run it at least 10.000 epochs:
```bash
python3 ./freqtrade/main.py hyperopt -e 10000
```
or if you want intermediate result to see
```bash
for i in {1..100}; do python3 ./freqtrade/main.py hyperopt -e 100; done
```
#### Why it is so long to run hyperopt?
Finding a great Hyperopt results takes time.
If you wonder why it takes a while to find great hyperopt results
This answer was written during the under the release 0.15.1, when we had
:
- 8 triggers
- 9 guards: let's say we evaluate even 10 values from each
- 1 stoploss calculation: let's say we want 10 values from that too to
be evaluated
The following calculation is still very rough and not very precise
but it will give the idea. With only these triggers and guards there is
already 8*10^9*10 evaluations. A roughly total of 80 billion evals.
Did you run 100 000 evals? Congrats, you've done roughly 1 / 100 000 th
of the search space.
+253
View File
@@ -0,0 +1,253 @@
# Hyperopt
This page explains how to tune your strategy by finding the optimal
parameters, a process called hyperparameter optimization. The bot uses several
algorithms included in the `scikit-optimize` package to accomplish this. The
search will burn all your CPU cores, make your laptop sound like a fighter jet
and still take a long time.
*Note:* Hyperopt will crash when used with only 1 CPU Core as found out in [Issue #1133](https://github.com/freqtrade/freqtrade/issues/1133)
## Table of Contents
- [Prepare your Hyperopt](#prepare-hyperopt)
- [Configure your Guards and Triggers](#configure-your-guards-and-triggers)
- [Solving a Mystery](#solving-a-mystery)
- [Adding New Indicators](#adding-new-indicators)
- [Execute Hyperopt](#execute-hyperopt)
- [Understand the hyperopt result](#understand-the-hyperopt-result)
## Prepare Hyperopting
Before we start digging in Hyperopt, we recommend you to take a look at
an example hyperopt file located into [user_data/hyperopts/](https://github.com/gcarq/freqtrade/blob/develop/user_data/hyperopts/test_hyperopt.py)
### 1. Install a Custom Hyperopt File
This is very simple. Put your hyperopt file into the folder
`user_data/hyperopts`.
Let assume you want a hyperopt file `awesome_hyperopt.py`:
1. Copy the file `user_data/hyperopts/sample_hyperopt.py` into `user_data/hyperopts/awesome_hyperopt.py`
### 2. Configure your Guards and Triggers
There are two places you need to change in your hyperopt file to add a
new buy hyperopt for testing:
- Inside [populate_buy_trend()](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/test_hyperopt.py#L230-L251).
- Inside [indicator_space()](https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/test_hyperopt.py#L207-L223).
There you have two different types of indicators: 1. `guards` and 2. `triggers`.
1. Guards are conditions like "never buy if ADX < 10", or never buy if
current price is over EMA10.
2. Triggers are ones that actually trigger buy in specific moment, like
"buy when EMA5 crosses over EMA10" or "buy when close price touches lower
bollinger band".
Hyperoptimization will, for each eval round, pick one trigger and possibly
multiple guards. The constructed strategy will be something like
"*buy exactly when close price touches lower bollinger band, BUT only if
ADX > 10*".
If you have updated the buy strategy, ie. changed the contents of
`populate_buy_trend()` method you have to update the `guards` and
`triggers` hyperopts must use.
## Solving a Mystery
Let's say you are curious: should you use MACD crossings or lower Bollinger
Bands to trigger your buys. And you also wonder should you use RSI or ADX to
help with those buy decisions. If you decide to use RSI or ADX, which values
should I use for them? So let's use hyperparameter optimization to solve this
mystery.
We will start by defining a search space:
```
def indicator_space() -> List[Dimension]:
"""
Define your Hyperopt space for searching strategy parameters
"""
return [
Integer(20, 40, name='adx-value'),
Integer(20, 40, name='rsi-value'),
Categorical([True, False], name='adx-enabled'),
Categorical([True, False], name='rsi-enabled'),
Categorical(['bb_lower', 'macd_cross_signal'], name='trigger')
]
```
Above definition says: I have five parameters I want you to randomly combine
to find the best combination. Two of them are integer values (`adx-value`
and `rsi-value`) and I want you test in the range of values 20 to 40.
Then we have three category variables. First two are either `True` or `False`.
We use these to either enable or disable the ADX and RSI guards. The last
one we call `trigger` and use it to decide which buy trigger we want to use.
So let's write the buy strategy using these values:
```
def populate_buy_trend(dataframe: DataFrame) -> DataFrame:
conditions = []
# GUARDS AND TRENDS
if 'adx-enabled' in params and params['adx-enabled']:
conditions.append(dataframe['adx'] > params['adx-value'])
if 'rsi-enabled' in params and params['rsi-enabled']:
conditions.append(dataframe['rsi'] < params['rsi-value'])
# TRIGGERS
if params['trigger'] == 'bb_lower':
conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
if params['trigger'] == 'macd_cross_signal':
conditions.append(qtpylib.crossed_above(
dataframe['macd'], dataframe['macdsignal']
))
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'buy'] = 1
return dataframe
return populate_buy_trend
```
Hyperopting will now call this `populate_buy_trend` as many times you ask it (`epochs`)
with different value combinations. It will then use the given historical data and make
buys based on the buy signals generated with the above function and based on the results
it will end with telling you which paramter combination produced the best profits.
The search for best parameters starts with a few random combinations and then uses a
regressor algorithm (currently ExtraTreesRegressor) to quickly find a parameter combination
that minimizes the value of the objective function `calculate_loss` in `hyperopt.py`.
The above setup expects to find ADX, RSI and Bollinger Bands in the populated indicators.
When you want to test an indicator that isn't used by the bot currently, remember to
add it to the `populate_indicators()` method in `hyperopt.py`.
## Execute Hyperopt
Once you have updated your hyperopt configuration you can run it.
Because hyperopt tries a lot of combinations to find the best parameters it will take time you will have the result (more than 30 mins).
We strongly recommend to use `screen` or `tmux` to prevent any connection loss.
```bash
python3 ./freqtrade/main.py -s <strategyname> --hyperopt <hyperoptname> -c config.json hyperopt -e 5000
```
Use `<strategyname>` and `<hyperoptname>` as the names of the custom strategy
(only required for generating sells) and the custom hyperopt used.
The `-e` flag will set how many evaluations hyperopt will do. We recommend
running at least several thousand evaluations.
### Execute Hyperopt with Different Ticker-Data Source
If you would like to hyperopt parameters using an alternate ticker data that
you have on-disk, use the `--datadir PATH` option. Default hyperopt will
use data from directory `user_data/data`.
### Running Hyperopt with Smaller Testset
Use the `--timerange` argument to change how much of the testset
you want to use. The last N ticks/timeframes will be used.
Example:
```bash
python3 ./freqtrade/main.py hyperopt --timerange -200
```
### Running Hyperopt with Smaller Search Space
Use the `--spaces` argument to limit the search space used by hyperopt.
Letting Hyperopt optimize everything is a huuuuge search space. Often it
might make more sense to start by just searching for initial buy algorithm.
Or maybe you just want to optimize your stoploss or roi table for that awesome
new buy strategy you have.
Legal values are:
- `all`: optimize everything
- `buy`: just search for a new buy strategy
- `roi`: just optimize the minimal profit table for your strategy
- `stoploss`: search for the best stoploss value
- space-separated list of any of the above values for example `--spaces roi stoploss`
## Understand the Hyperopt Result
Once Hyperopt is completed you can use the result to create a new strategy.
Given the following result from hyperopt:
```
Best result:
135 trades. Avg profit 0.57%. Total profit 0.03871918 BTC (0.7722Σ%). Avg duration 180.4 mins.
with values:
{'adx-value': 44, 'rsi-value': 29, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'bb_lower'}
```
You should understand this result like:
- The buy trigger that worked best was `bb_lower`.
- You should not use ADX because `adx-enabled: False`)
- You should **consider** using the RSI indicator (`rsi-enabled: True` and the best value is `29.0` (`rsi-value: 29.0`)
You have to look inside your strategy file into `buy_strategy_generator()`
method, what those values match to.
So for example you had `rsi-value: 29.0` so we would look at `rsi`-block, that translates to the following code block:
```
(dataframe['rsi'] < 29.0)
```
Translating your whole hyperopt result as the new buy-signal
would then look like:
```python
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
dataframe.loc[
(
(dataframe['rsi'] < 29.0) & # rsi-value
dataframe['close'] < dataframe['bb_lowerband'] # trigger
),
'buy'] = 1
return dataframe
```
### Understand Hyperopt ROI results
If you are optimizing ROI, you're result will look as follows and include a ROI table.
```
Best result:
135 trades. Avg profit 0.57%. Total profit 0.03871918 BTC (0.7722Σ%). Avg duration 180.4 mins.
with values:
{'adx-value': 44, 'rsi-value': 29, 'adx-enabled': False, 'rsi-enabled': True, 'trigger': 'bb_lower', 'roi_t1': 40, 'roi_t2': 57, 'roi_t3': 21, 'roi_p1': 0.03634636907306948, 'roi_p2': 0.055237357937802885, 'roi_p3': 0.015163796015548354, 'stoploss': -0.37996664668703606}
ROI table:
{0: 0.10674752302642071, 21: 0.09158372701087236, 78: 0.03634636907306948, 118: 0}
```
This would translate to the following ROI table:
``` python
minimal_roi = {
"118": 0,
"78": 0.0363463,
"21": 0.0915,
"0": 0.106
}
```
### Validate backtest result
Once the optimized strategy has been implemented into your strategy, you should backtest this strategy to make sure everything is working as expected.
To archive the same results (number of trades, ...) than during hyperopt, please use the command line flag `--disable-max-market-positions`.
This setting is the default for hyperopt for speed reasons. You can overwrite this in the configuration by setting `"position_stacking"=false` or by changing the relevant line in your hyperopt file [here](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/optimize/hyperopt.py#L283).
Dry/live runs will **NOT** use position stacking - therefore it does make sense to also validate the strategy without this as it's closer to reality.
## Next Step
Now you have a perfect bot and want to control it from Telegram. Your
next step is to learn the [Telegram usage](https://github.com/freqtrade/freqtrade/blob/develop/docs/telegram-usage.md).
+39
View File
@@ -0,0 +1,39 @@
# freqtrade documentation
Welcome to freqtrade documentation. Please feel free to contribute to
this documentation if you see it became outdated by sending us a
Pull-request. Do not hesitate to reach us on
[Slack](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE)
if you do not find the answer to your questions.
## Table of Contents
- [Pre-requisite](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md)
- [Setup your Bittrex account](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md#setup-your-bittrex-account)
- [Setup your Telegram bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md#setup-your-telegram-bot)
- [Bot Installation](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md)
- [Install with Docker (all platforms)](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#docker)
- [Install on Linux Ubuntu](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#21-linux---ubuntu-1604)
- [Install on MacOS](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#23-macos-installation)
- [Install on Windows](https://github.com/freqtrade/freqtrade/blob/develop/docs/installation.md#windows)
- [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)
- [Bot usage (Start your bot)](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md)
- [Bot commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#bot-commands)
- [Backtesting commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#backtesting-commands)
- [Hyperopt commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#hyperopt-commands)
- [Edge commands](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-usage.md#edge-commands)
- [Bot Optimization](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md)
- [Change your strategy](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md#change-your-strategy)
- [Add more Indicator](https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md#add-more-indicator)
- [Test your strategy with Backtesting](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md)
- [Edge positioning](https://github.com/freqtrade/freqtrade/blob/develop/docs/edge.md)
- [Find optimal parameters with Hyperopt](https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md)
- [Control the bot with telegram](https://github.com/freqtrade/freqtrade/blob/develop/docs/telegram-usage.md)
- [Receive notifications via webhook](https://github.com/freqtrade/freqtrade/blob/develop/docs/webhook-config.md)
- [Contribute to the project](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md)
- [How to contribute](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md)
- [Run tests & Check PEP8 compliance](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md)
- [FAQ](https://github.com/freqtrade/freqtrade/blob/develop/docs/faq.md)
- [SQL cheatsheet](https://github.com/freqtrade/freqtrade/blob/develop/docs/sql_cheatsheet.md)
- [Sandbox Testing](https://github.com/freqtrade/freqtrade/blob/develop/docs/sandbox-testing.md)
- [Developer Docs](https://github.com/freqtrade/freqtrade/blob/develop/docs/developer.md)
+429
View File
@@ -0,0 +1,429 @@
# Installation
This page explains how to prepare your environment for running the bot.
To understand how to set up the bot please read the [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) page.
## Table of Contents
* [Table of Contents](#table-of-contents)
* [Easy Installation - Linux Script](#easy-installation---linux-script)
* [Automatic Installation - Docker](#automatic-installation---docker)
* [Custom Linux MacOS Installation](#custom-installation)
- [Requirements](#requirements)
- [Linux - Ubuntu 16.04](#linux---ubuntu-1604)
- [MacOS](#macos)
- [Setup Config and virtual env](#setup-config-and-virtual-env)
* [Windows](#windows)
<!-- /TOC -->
------
## Easy Installation - Linux Script
If you are on Debian, Ubuntu or MacOS a freqtrade provides a script to Install, Update, Configure, and Reset your bot.
```bash
$ ./setup.sh
usage:
-i,--install Install freqtrade from scratch
-u,--update Command git pull to update.
-r,--reset Hard reset your develop/master branch.
-c,--config Easy config generator (Will override your existing file).
```
### --install
This script will install everything you need to run the bot:
* Mandatory software as: `Python3`, `ta-lib`, `wget`
* Setup your virtualenv
* Configure your `config.json` file
This script is a combination of `install script` `--reset`, `--config`
### --update
Update parameter will pull the last version of your current branch and update your virtualenv.
### --reset
Reset parameter will hard reset your branch (only if you are on `master` or `develop`) and recreate your virtualenv.
### --config
Config parameter is a `config.json` configurator. This script will ask you questions to setup your bot and create your `config.json`.
------
## Automatic Installation - Docker
Start by downloading Docker for your platform:
* [Mac](https://www.docker.com/products/docker#/mac)
* [Windows](https://www.docker.com/products/docker#/windows)
* [Linux](https://www.docker.com/products/docker#/linux)
Once you have Docker installed, simply create the config file (e.g. `config.json`) and then create a Docker image for `freqtrade` using the Dockerfile in this repo.
### 1. Prepare the Bot
#### 1.1. Clone the git repository
```bash
git clone https://github.com/freqtrade/freqtrade.git
```
#### 1.2. (Optional) Checkout the develop branch
```bash
git checkout develop
```
#### 1.3. Go into the new directory
```bash
cd freqtrade
```
#### 1.4. Copy `config.json.example` to `config.json`
```bash
cp -n config.json.example config.json
```
> To edit the config please refer to the [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md) page.
#### 1.5. Create your database file *(optional - the bot will create it if it is missing)*
Production
```bash
touch tradesv3.sqlite
````
Dry-Run
```bash
touch tradesv3.dryrun.sqlite
```
### 2. Download or build the docker image
Either use the prebuilt image from docker hub - or build the image yourself if you would like more control on which version is used.
Branches / tags available can be checked out on [Dockerhub](https://hub.docker.com/r/freqtradeorg/freqtrade/tags/).
#### 2.1. Download the docker image
Pull the image from docker hub and (optionally) change the name of the image
```bash
docker pull freqtradeorg/freqtrade:develop
# Optionally tag the repository so the run-commands remain shorter
docker tag freqtradeorg/freqtrade:develop freqtrade
```
To update the image, simply run the above commands again and restart your running container.
#### 2.2. Build the Docker image
```bash
cd freqtrade
docker build -t freqtrade .
```
If you are developing using Docker, use `Dockerfile.develop` to build a dev Docker image, which will also set up develop dependencies:
```bash
docker build -f ./Dockerfile.develop -t freqtrade-dev .
```
For security reasons, your configuration file will not be included in the image, you will need to bind mount it. It is also advised to bind mount an SQLite database file (see the "5. Run a restartable docker image" section) to keep it between updates.
### 3. Verify the Docker image
After the build process you can verify that the image was created with:
```bash
docker images
```
### 4. Run the Docker image
You can run a one-off container that is immediately deleted upon exiting with the following command (`config.json` must be in the current working directory):
```bash
docker run --rm -v /etc/localtime:/etc/localtime:ro -v `pwd`/config.json:/freqtrade/config.json -it freqtrade
```
There is known issue in OSX Docker versions after 17.09.1, whereby /etc/localtime cannot be shared causing Docker to not start. A work-around for this is to start with the following cmd.
```bash
docker run --rm -e TZ=`ls -la /etc/localtime | cut -d/ -f8-9` -v `pwd`/config.json:/freqtrade/config.json -it freqtrade
```
More information on this docker issue and work-around can be read [here](https://github.com/docker/for-mac/issues/2396)
In this example, the database will be created inside the docker instance and will be lost when you will refresh your image.
### 5. Run a restartable docker image
To run a restartable instance in the background (feel free to place your configuration and database files wherever it feels comfortable on your filesystem).
#### 5.1. Move your config file and database
```bash
mkdir ~/.freqtrade
mv config.json ~/.freqtrade
mv tradesv3.sqlite ~/.freqtrade
```
#### 5.2. Run the docker image
```bash
docker run -d \
--name freqtrade \
-v /etc/localtime:/etc/localtime:ro \
-v ~/.freqtrade/config.json:/freqtrade/config.json \
-v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \
freqtrade --db-url sqlite:///tradesv3.sqlite
```
*Note*: db-url defaults to `sqlite:///tradesv3.sqlite` but it defaults to `sqlite://` if `dry_run=True` is being used.
To override this behaviour use a custom db-url value: i.e.: `--db-url sqlite:///tradesv3.dryrun.sqlite`
### 6. Monitor your Docker instance
You can then use the following commands to monitor and manage your container:
```bash
docker logs freqtrade
docker logs -f freqtrade
docker restart freqtrade
docker stop freqtrade
docker start freqtrade
```
For more information on how to operate Docker, please refer to the [official Docker documentation](https://docs.docker.com/).
*Note*: You do not need to rebuild the image for configuration changes, it will suffice to edit `config.json` and restart the container.
### 7. Backtest with docker
The following assumes that the above steps (1-4) have been completed successfully.
Also, backtest-data should be available at `~/.freqtrade/user_data/`.
``` bash
docker run -d \
--name freqtrade \
-v /etc/localtime:/etc/localtime:ro \
-v ~/.freqtrade/config.json:/freqtrade/config.json \
-v ~/.freqtrade/tradesv3.sqlite:/freqtrade/tradesv3.sqlite \
-v ~/.freqtrade/user_data/:/freqtrade/user_data/ \
freqtrade --strategy AwsomelyProfitableStrategy backtesting
```
Head over to the [Backtesting Documentation](https://github.com/freqtrade/freqtrade/blob/develop/docs/backtesting.md) for more details.
*Note*: Additional parameters can be appended after the image name (`freqtrade` in the above example).
------
## Custom Installation
We've included/collected install instructions for Ubuntu 16.04, MacOS, and Windows. These are guidelines and your success may vary with other distros.
OS Specific steps are listed first, the [common](#common) section below is necessary for all systems.
### Requirements
Click each one for install guide:
* [Python >= 3.6.x](http://docs.python-guide.org/en/latest/starting/installation/)
* [pip](https://pip.pypa.io/en/stable/installing/)
* [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [virtualenv](https://virtualenv.pypa.io/en/stable/installation/) (Recommended)
* [TA-Lib](https://mrjbq7.github.io/ta-lib/install.html)
### Linux - Ubuntu 16.04
#### Install Python 3.6, Git, and wget
```bash
sudo add-apt-repository ppa:jonathonf/python-3.6
sudo apt-get update
sudo apt-get install python3.6 python3.6-venv python3.6-dev build-essential autoconf libtool pkg-config make wget git
```
#### Raspberry Pi / Raspbian
Before installing FreqTrade on a Raspberry Pi running the official Raspbian Image, make sure you have at least Python 3.6 installed. The default image only provides Python 3.5. Probably the easiest way to get a recent version of python is [miniconda](https://repo.continuum.io/miniconda/).
The following assumes that miniconda3 is installed and available in your environment. Last miniconda3 installation file use python 3.4, we will update to python 3.6 on this installation.
It's recommended to use (mini)conda for this as installation/compilation of `numpy`, `scipy` and `pandas` takes a long time.
If you have installed it from (mini)conda, you can remove `numpy`, `scipy`, and `pandas` from `requirements.txt` before you install it with `pip`.
Additional package to install on your Raspbian, `libffi-dev` required by cryptography (from python-telegram-bot).
``` bash
conda config --add channels rpi
conda install python=3.6
conda create -n freqtrade python=3.6
conda activate freqtrade
conda install scipy pandas numpy
sudo apt install libffi-dev
python3 -m pip install -r requirements.txt
python3 -m pip install -e .
```
### MacOS
#### Install Python 3.6, git and wget
```bash
brew install python3 git wget
```
### common
#### 1. Install TA-Lib
Official webpage: https://mrjbq7.github.io/ta-lib/install.html
```bash
wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
tar xvzf 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=/usr/local
make
sudo make install
cd ..
rm -rf ./ta-lib*
```
*Note*: An already downloaded version of ta-lib is included in the repository, as the sourceforge.net source seems to have problems frequently.
#### 2. Setup your Python virtual environment (virtualenv)
*Note*: This step is optional but strongly recommended to keep your system organized
```bash
python3 -m venv .env
source .env/bin/activate
```
#### 3. Install FreqTrade
Clone the git repository:
```bash
git clone https://github.com/freqtrade/freqtrade.git
```
Optionally checkout the stable/master branch:
```bash
git checkout master
```
#### 4. Initialize the configuration
```bash
cd freqtrade
cp config.json.example config.json
```
> *To edit the config please refer to [Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md).*
#### 5. Install python dependencies
``` bash
pip3 install --upgrade pip
pip3 install -r requirements.txt
pip3 install -e .
```
#### 6. Run the Bot
If this is the first time you run the bot, ensure you are running it in Dry-run `"dry_run": true,` otherwise it will start to buy and sell coins.
```bash
python3.6 ./freqtrade/main.py -c config.json
```
*Note*: If you run the bot on a server, you should consider using [Docker](#automatic-installation---docker) a terminal multiplexer like `screen` or [`tmux`](https://en.wikipedia.org/wiki/Tmux) to avoid that the bot is stopped on logout.
#### 7. [Optional] Configure `freqtrade` as a `systemd` service
From the freqtrade repo... copy `freqtrade.service` to your systemd user directory (usually `~/.config/systemd/user`) and update `WorkingDirectory` and `ExecStart` to match your setup.
After that you can start the daemon with:
```bash
systemctl --user start freqtrade
```
For this to be persistent (run when user is logged out) you'll need to enable `linger` for your freqtrade user.
```bash
sudo loginctl enable-linger "$USER"
```
------
## Windows
We recommend that Windows users use [Docker](#docker) as this will work much easier and smoother (also more secure).
If that is not possible, try using the Windows Linux subsystem (WSL) - for which the Ubuntu instructions should work.
If that is not available on your system, feel free to try the instructions below, which led to success for some.
### Install freqtrade manually
#### Clone the git repository
```bash
git clone https://github.com/freqtrade/freqtrade.git
```
copy paste `config.json` to ``\path\freqtrade-develop\freqtrade`
#### Install ta-lib
Install ta-lib according to the [ta-lib documentation](https://github.com/mrjbq7/ta-lib#windows).
As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of inofficial precompiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which needs to be downloaded and installed using `pip install TA_Lib0.4.17cp36cp36mwin32.whl` (make sure to use the version matching your python version)
```cmd
>cd \path\freqtrade-develop
>python -m venv .env
>cd .env\Scripts
>activate.bat
>cd \path\freqtrade-develop
REM optionally install ta-lib from wheel
REM >pip install TA_Lib0.4.17cp36cp36mwin32.whl
>pip install -r requirements.txt
>pip install -e .
>python freqtrade\main.py
```
> Thanks [Owdr](https://github.com/Owdr) for the commands. Source: [Issue #222](https://github.com/freqtrade/freqtrade/issues/222)
#### Error during installation under Windows
``` bash
error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools
```
Unfortunately, many packages requiring compilation don't provide a pre-build wheel. It is therefore mandatory to have a C/C++ compiler installed and available for your python environment to use.
The easiest way is to download install Microsoft Visual Studio Community [here](https://visualstudio.microsoft.com/downloads/) and make sure to install "Common Tools for Visual C++" to enable building c code on Windows. Unfortunately, this is a heavy download / dependency (~4Gb) so you might want to consider WSL or docker first.
---
Now you have an environment ready, the next step is
[Bot Configuration](https://github.com/freqtrade/freqtrade/blob/develop/docs/configuration.md)...
+87
View File
@@ -0,0 +1,87 @@
# Plotting
This page explains how to plot prices, indicator, profits.
## Table of Contents
- [Plot price and indicators](#plot-price-and-indicators)
- [Plot profit](#plot-profit)
## Installation
Plotting scripts use Plotly library. Install/upgrade it with:
```
pip install --upgrade plotly
```
At least version 2.3.0 is required.
## Plot price and indicators
Usage for the price plotter:
```
script/plot_dataframe.py [-h] [-p pair] [--live]
```
Example
```
python scripts/plot_dataframe.py -p BTC/ETH
```
The `-p` pair argument, can be used to specify what
pair you would like to plot.
**Advanced use**
To plot the current live price use the `--live` flag:
```
python scripts/plot_dataframe.py -p BTC/ETH --live
```
To plot a timerange (to zoom in):
```
python scripts/plot_dataframe.py -p BTC/ETH --timerange=100-200
```
Timerange doesn't work with live data.
To plot trades stored in a database use `--db-url` argument:
```
python scripts/plot_dataframe.py --db-url sqlite:///tradesv3.dry_run.sqlite -p BTC/ETH
```
To plot a test strategy the strategy should have first be backtested.
The results may then be plotted with the -s argument:
```
python scripts/plot_dataframe.py -s Strategy_Name -p BTC/ETH --datadir user_data/data/<exchange_name>/
```
## Plot profit
The profit plotter show a picture with three plots:
1) Average closing price for all pairs
2) The summarized profit made by backtesting.
Note that this is not the real-world profit, but
more of an estimate.
3) Each pair individually profit
The first graph is good to get a grip of how the overall market
progresses.
The second graph will show how you algorithm works or doesnt.
Perhaps you want an algorithm that steadily makes small profits,
or one that acts less seldom, but makes big swings.
The third graph can be useful to spot outliers, events in pairs
that makes profit spikes.
Usage for the profit plotter:
```
script/plot_profit.py [-h] [-p pair] [--datadir directory] [--ticker_interval num]
```
The `-p` pair argument, can be used to plot a single pair
Example
```
python3 scripts/plot_profit.py --datadir ../freqtrade/freqtrade/tests/testdata-20171221/ -p BTC_LTC
```
+48
View File
@@ -0,0 +1,48 @@
# Pre-requisite
Before running your bot in production you will need to setup few
external API. In production mode, the bot required valid Bittrex API
credentials and a Telegram bot (optional but recommended).
## Table of Contents
- [Setup your Bittrex account](#setup-your-bittrex-account)
- [Backtesting commands](#setup-your-telegram-bot)
## Setup your Bittrex account
*To be completed, please feel free to complete this section.*
## Setup your Telegram bot
The only things you need is a working Telegram bot and its API token.
Below we explain how to create your Telegram Bot, and how to get your
Telegram user id.
### 1. Create your Telegram bot
**1.1. Start a chat with https://telegram.me/BotFather**
**1.2. Send the message** `/newbot`
*BotFather response:*
```
Alright, a new bot. How are we going to call it? Please choose a name for your bot.
```
**1.3. Choose the public name of your bot (e.g "`Freqtrade bot`")**
*BotFather response:*
```
Good. Now let's choose a username for your bot. It must end in `bot`. Like this, for example: TetrisBot or tetris_bot.
```
**1.4. Choose the name id of your bot (e.g "`My_own_freqtrade_bot`")**
**1.5. Father bot will return you the token (API key)**
Copy it and keep it you will use it for the config parameter `token`.
*BotFather response:*
```
Done! Congratulations on your new bot. You will find it at t.me/My_own_freqtrade_bot. You can now add a description, about section and profile picture for your bot, see /help for a list of commands. By the way, when you've finished creating your cool bot, ping our Bot Support if you want a better username for it. Just make sure the bot is fully operational before you do this.
Use this token to access the HTTP API:
521095879:AAEcEZEL7ADJ56FtG_qD0bQJSKETbXCBCi0
For a description of the Bot API, see this page: https://core.telegram.org/bots/api
```
**1.6. Don't forget to start the conversation with your bot, by clicking /START button**
### 2. Get your user id
**2.1. Talk to https://telegram.me/userinfobot**
**2.2. Get your "Id", you will use it for the config parameter
`chat_id`.**
+141
View File
@@ -0,0 +1,141 @@
# Sandbox API testing
Where an exchange provides a sandbox for risk-free integration, or end-to-end, testing CCXT provides access to these.
This document is a *light overview of configuring Freqtrade and GDAX sandbox.
This can be useful to developers and trader alike as Freqtrade is quite customisable.
When testing your API connectivity, make sure to use the following URLs.
***Website**
https://public.sandbox.gdax.com
***REST API**
https://api-public.sandbox.gdax.com
---
# Configure a Sandbox account on Gdax
Aim of this document section
- An sanbox account
- create 2FA (needed to create an API)
- Add test 50BTC to account
- Create :
- - API-KEY
- - API-Secret
- - API Password
## Acccount
This link will redirect to the sandbox main page to login / create account dialogues:
https://public.sandbox.pro.coinbase.com/orders/
After registration and Email confimation you wil be redirected into your sanbox account. It is easy to verify you're in sandbox by checking the URL bar.
> https://public.sandbox.pro.coinbase.com/
## Enable 2Fa (a prerequisite to creating sandbox API Keys)
From within sand box site select your profile, top right.
>Or as a direct link: https://public.sandbox.pro.coinbase.com/profile
From the menu panel to the left of the screen select
> Security: "*View or Update*"
In the new site select "enable authenticator" as typical google Authenticator.
- open Google Authenticator on your phone
- scan barcode
- enter your generated 2fa
## Enable API Access
From within sandbox select profile>api>create api-keys
>or as a direct link: https://public.sandbox.pro.coinbase.com/profile/api
Click on "create one" and ensure **view** and **trade** are "checked" and sumbit your 2FA
- **Copy and paste the Passphase** into a notepade this will be needed later
- **Copy and paste the API Secret** popup into a notepad this will needed later
- **Copy and paste the API Key** into a notepad this will needed later
## Add 50 BTC test funds
To add funds, use the web interface deposit and withdraw buttons.
To begin select 'Wallets' from the top menu.
> Or as a direct link: https://public.sandbox.pro.coinbase.com/wallets
- Deposits (bottom left of screen)
- - Deposit Funds Bitcoin
- - - Coinbase BTC Wallet
- - - - Max (50 BTC)
- - - - - Deposit
*This process may be repeated for other currencies, ETH as example*
---
# Configure Freqtrade to use Gax Sandbox
The aim of this document section
- Enable sandbox URLs in Freqtrade
- Configure API
- - secret
- - key
- - passphrase
## Sandbox URLs
Freqtrade makes use of CCXT which in turn provides a list of URLs to Freqtrade.
These include `['test']` and `['api']`.
- `[Test]` if available will point to an Exchanges sandbox.
- `[Api]` normally used, and resolves to live API target on the exchange
To make use of sandbox / test add "sandbox": true, to your config.json
```json
"exchange": {
"name": "gdax",
"sandbox": true,
"key": "5wowfxemogxeowo;heiohgmd",
"secret": "/ZMH1P62rCVmwefewrgcewX8nh4gob+lywxfwfxwwfxwfNsH1ySgvWCUR/w==",
"password": "1bkjfkhfhfu6sr",
"outdated_offset": 5
"pair_whitelist": [
"BTC/USD"
```
Also insert your
- api-key (noted earlier)
- api-secret (noted earlier)
- password (the passphrase - noted earlier)
---
## You should now be ready to test your sandbox
Ensure Freqtrade logs show the sandbox URL, and trades made are shown in sandbox.
** Typically the BTC/USD has the most activity in sandbox to test against.
## GDAX - Old Candles problem
It is my experience that GDAX sandbox candles may be 20+- minutes out of date. This can cause trades to fail as one of Freqtrades safety checks.
To disable this check, add / change the `"outdated_offset"` parameter in the exchange section of your configuration to adjust for this delay.
Example based on the above configuration:
```json
"exchange": {
"name": "gdax",
"sandbox": true,
"key": "5wowfxemogxeowo;heiohgmd",
"secret": "/ZMH1P62rCVmwefewrgcewX8nh4gob+lywxfwfxwwfxwfNsH1ySgvWCUR/w==",
"password": "1bkjfkhfhfu6sr",
"outdated_offset": 30
"pair_whitelist": [
"BTC/USD"
```
+93
View File
@@ -0,0 +1,93 @@
# SQL Helper
This page constains some help if you want to edit your sqlite db.
## Install sqlite3
**Ubuntu/Debian installation**
```bash
sudo apt-get install sqlite3
```
## Open the DB
```bash
sqlite3
.open <filepath>
```
## Table structure
### List tables
```bash
.tables
```
### Display table structure
```bash
.schema <table_name>
```
### Trade table structure
```sql
CREATE TABLE trades (
id INTEGER NOT NULL,
exchange VARCHAR NOT NULL,
pair VARCHAR NOT NULL,
is_open BOOLEAN NOT NULL,
fee_open FLOAT NOT NULL,
fee_close FLOAT NOT NULL,
open_rate FLOAT,
open_rate_requested FLOAT,
close_rate FLOAT,
close_rate_requested FLOAT,
close_profit FLOAT,
stake_amount FLOAT NOT NULL,
amount FLOAT,
open_date DATETIME NOT NULL,
close_date DATETIME,
open_order_id VARCHAR,
PRIMARY KEY (id),
CHECK (is_open IN (0, 1))
);
```
## Get all trades in the table
```sql
SELECT * FROM trades;
```
## Fix trade still open after a /forcesell
```sql
UPDATE trades
SET is_open=0, close_date=<close_date>, close_rate=<close_rate>, close_profit=close_rate/open_rate-1
WHERE id=<trade_ID_to_update>;
```
**Example:**
```sql
UPDATE trades
SET is_open=0, close_date='2017-12-20 03:08:45.103418', close_rate=0.19638016, close_profit=0.0496
WHERE id=31;
```
## Insert manually a new trade
```sql
INSERT
INTO trades (exchange, pair, is_open, fee_open, fee_close, open_rate, stake_amount, amount, open_date)
VALUES ('BITTREX', 'BTC_<COIN>', 1, 0.0025, 0.0025, <open_rate>, <stake_amount>, <amount>, '<datetime>')
```
**Example:**
```sql
INSERT INTO trades (exchange, pair, is_open, fee_open, fee_close, open_rate, stake_amount, amount, open_date) VALUES ('BITTREX', 'BTC_ETC', 1, 0.0025, 0.0025, 0.00258580, 0.002, 0.7715262081, '2017-11-28 12:44:24.000000')
```
## Fix wrong fees in the table
If your DB was created before
[PR#200](https://github.com/freqtrade/freqtrade/pull/200) was merged
(before 12/23/17).
```sql
UPDATE trades SET fee=0.0025 WHERE fee=0.005;
```
+51
View File
@@ -0,0 +1,51 @@
# Stop Loss support
At this stage the bot contains the following stoploss support modes:
1. static stop loss, defined in either the strategy or configuration
2. trailing stop loss, defined in the configuration
3. trailing stop loss, custom positive loss, defined in configuration
## Static Stop Loss
This is very simple, basically you define a stop loss of x in your strategy file or alternative in the configuration, which
will overwrite the strategy definition. This will basically try to sell your asset, the second the loss exceeds the defined loss.
## Trail Stop Loss
The initial value for this stop loss, is defined in your strategy or configuration. Just as you would define your Stop Loss normally.
To enable this Feauture all you have to do is to define the configuration element:
``` json
"trailing_stop" : True
```
This will now activate an algorithm, which automatically moves your stop loss up every time the price of your asset increases.
For example, simplified math,
* you buy an asset at a price of 100$
* your stop loss is defined at 2%
* which means your stop loss, gets triggered once your asset dropped below 98$
* assuming your asset now increases to 102$
* your stop loss, will now be 2% of 102$ or 99.96$
* now your asset drops in value to 101$, your stop loss, will still be 99.96$
basically what this means is that your stop loss will be adjusted to be always be 2% of the highest observed price
### Custom positive loss
Due to demand, it is possible to have a default stop loss, when you are in the red with your buy, but once your profit surpasses a certain percentage,
the system will utilize a new stop loss, which can be a different value. For example your default stop loss is 5%, but once you have 1.1% profit,
it will be changed to be only a 1% stop loss, which trails the green candles until it goes below them.
Both values can be configured in the main configuration file and requires `"trailing_stop": true` to be set to true.
``` json
"trailing_stop_positive": 0.01,
"trailing_stop_positive_offset": 0.011,
```
The 0.01 would translate to a 1% stop loss, once you hit 1.1% profit.
You should also make sure to have this value (`trailing_stop_positive_offset`) lower than your minimal ROI, otherwise minimal ROI will apply first and sell your trade.
+149
View File
@@ -0,0 +1,149 @@
# Telegram usage
This page explains how to command your bot with Telegram.
## Pre-requisite
To control your bot with Telegram, you need first to
[set up a Telegram bot](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md)
and add your Telegram API keys into your config file.
## Telegram commands
Per default, the Telegram bot shows predefined commands. Some commands
are only available by sending them to the bot. The table below list the
official commands. You can ask at any moment for help with `/help`.
| Command | Default | Description |
|----------|---------|-------------|
| `/start` | | Starts the trader
| `/stop` | | Stops the trader
| `/reload_conf` | | Reloads the configuration file
| `/status` | | Lists all open trades
| `/status table` | | List all open trades in a table format
| `/count` | | Displays number of trades used and available
| `/profit` | | Display a summary of your profit/loss from close trades and some stats about your performance
| `/forcesell <trade_id>` | | Instantly sells the given trade (Ignoring `minimum_roi`).
| `/forcesell all` | | Instantly sells all open trades (Ignoring `minimum_roi`).
| `/forcebuy <pair> [rate]` | | Instantly buys the given pair. Rate is optional. (`forcebuy_enable` must be set to True)
| `/performance` | | Show performance of each finished trade grouped by pair
| `/balance` | | Show account balance per currency
| `/daily <n>` | 7 | Shows profit or loss per day, over the last n days
| `/help` | | Show help message
| `/version` | | Show version
## Telegram commands in action
Below, example of Telegram message you will receive for each command.
### /start
> **Status:** `running`
### /stop
> `Stopping trader ...`
> **Status:** `stopped`
## /status
For each open trade, the bot will send you the following message.
> **Trade ID:** `123`
> **Current Pair:** CVC/BTC
> **Open Since:** `1 days ago`
> **Amount:** `26.64180098`
> **Open Rate:** `0.00007489`
> **Close Rate:** `None`
> **Current Rate:** `0.00007489`
> **Close Profit:** `None`
> **Current Profit:** `12.95%`
> **Open Order:** `None`
## /status table
Return the status of all open trades in a table format.
```
ID Pair Since Profit
---- -------- ------- --------
67 SC/BTC 1 d 13.33%
123 CVC/BTC 1 h 12.95%
```
## /count
Return the number of trades used and available.
```
current max
--------- -----
2 10
```
## /profit
Return a summary of your profit/loss and performance.
> **ROI:** Close trades
> ∙ `0.00485701 BTC (258.45%)`
> ∙ `62.968 USD`
> **ROI:** All trades
> ∙ `0.00255280 BTC (143.43%)`
> ∙ `33.095 EUR`
>
> **Total Trade Count:** `138`
> **First Trade opened:** `3 days ago`
> **Latest Trade opened:** `2 minutes ago`
> **Avg. Duration:** `2:33:45`
> **Best Performing:** `PAY/BTC: 50.23%`
## /forcesell <trade_id>
> **BITTREX:** Selling BTC/LTC with limit `0.01650000 (profit: ~-4.07%, -0.00008168)`
## /forcebuy <pair>
> **BITTREX**: Buying ETH/BTC with limit `0.03400000` (`1.000000 ETH`, `225.290 USD`)
Note that for this to work, `forcebuy_enable` needs to be set to true.
## /performance
Return the performance of each crypto-currency the bot has sold.
> Performance:
> 1. `RCN/BTC 57.77%`
> 2. `PAY/BTC 56.91%`
> 3. `VIB/BTC 47.07%`
> 4. `SALT/BTC 30.24%`
> 5. `STORJ/BTC 27.24%`
> ...
## /balance
Return the balance of all crypto-currency your have on the exchange.
> **Currency:** BTC
> **Available:** 3.05890234
> **Balance:** 3.05890234
> **Pending:** 0.0
> **Currency:** CVC
> **Available:** 86.64180098
> **Balance:** 86.64180098
> **Pending:** 0.0
## /daily <n>
Per default `/daily` will return the 7 last days.
The example below if for `/daily 3`:
> **Daily Profit over the last 3 days:**
```
Day Profit BTC Profit USD
---------- -------------- ------------
2018-01-03 0.00224175 BTC 29,142 USD
2018-01-02 0.00033131 BTC 4,307 USD
2018-01-01 0.00269130 BTC 34.986 USD
```
## /version
> **Version:** `0.14.3`
+75
View File
@@ -0,0 +1,75 @@
# Webhook usage
This page explains how to configure your bot to talk to webhooks.
## Configuration
Enable webhooks by adding a webhook-section to your configuration file, and setting `webhook.enabled` to `true`.
Sample configuration (tested using IFTTT).
```json
"webhook": {
"enabled": true,
"url": "https://maker.ifttt.com/trigger/<YOUREVENT>/with/key/<YOURKEY>/",
"webhookbuy": {
"value1": "Buying {pair}",
"value2": "limit {limit:8f}",
"value3": "{stake_amount:8f} {stake_currency}"
},
"webhooksell": {
"value1": "Selling {pair}",
"value2": "limit {limit:8f}",
"value3": "profit: {profit_amount:8f} {stake_currency}"
},
"webhookstatus": {
"value1": "Status: {status}",
"value2": "",
"value3": ""
}
},
```
The url in `webhook.url` should point to the correct url for your webhook. If you're using [IFTTT](https://ifttt.com) (as shown in the sample above) please insert our event and key to the url.
Different payloads can be configured for different events. Not all fields are necessary, but you should configure at least one of the dicts, otherwise the webhook will never be called.
### Webhookbuy
The fields in `webhook.webhookbuy` are filled when the bot executes a buy. Parameters are filled using string.format.
Possible parameters are:
* exchange
* pair
* market_url
* limit
* stake_amount
* stake_amount_fiat
* stake_currency
* fiat_currency
### Webhooksell
The fields in `webhook.webhooksell` are filled when the bot sells a trade. Parameters are filled using string.format.
Possible parameters are:
* exchange
* pair
* gain
* market_url
* limit
* amount
* open_rate
* current_rate
* profit_amount
* profit_percent
* profit_fiat
* stake_currency
* fiat_currency
* sell_reason
### Webhookstatus
The fields in `webhook.webhookstatus` are used for regular status messages (Started / Stopped / ...). Parameters are filled using string.format.
The only possible value here is `{status}`.
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=Freqtrade Daemon
After=network.target
[Service]
# Set WorkingDirectory and ExecStart to your file paths accordingly
# NOTE: %h will be resolved to /home/<username>
WorkingDirectory=%h/freqtrade
ExecStart=/usr/bin/freqtrade --dynamic-whitelist 40
Restart=on-failure
[Install]
WantedBy=default.target
+24 -2
View File
@@ -1,3 +1,25 @@
__version__ = '0.14.2'
""" FreqTrade bot """
__version__ = '0.18.0'
from . import main
class DependencyException(BaseException):
"""
Indicates that a assumed dependency is not met.
This could happen when there is currently not enough money on the account.
"""
class OperationalException(BaseException):
"""
Requires manual intervention.
This happens when an exchange returns an unexpected error during runtime
or given configuration is invalid.
"""
class TemporaryError(BaseException):
"""
Temporary network or exchange related error.
This could happen when an exchange is congested, unavailable, or the user
has networking problems. Usually resolves itself after a time.
"""
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env python3
"""
__main__.py for Freqtrade
To launch Freqtrade as a module
> python -m freqtrade (with Python >= 3.6)
"""
import sys
from freqtrade import main
if __name__ == '__main__':
main.set_loggers()
main.main(sys.argv[1:])
-112
View File
@@ -1,112 +0,0 @@
import logging
from datetime import timedelta
import arrow
import talib.abstract as ta
from pandas import DataFrame, to_datetime
from freqtrade.exchange import get_ticker_history
from freqtrade.vendor.qtpylib.indicators import awesome_oscillator
logger = logging.getLogger(__name__)
def parse_ticker_dataframe(ticker: list) -> DataFrame:
"""
Analyses the trend for the given ticker history
:param ticker: See exchange.get_ticker_history
:return: DataFrame
"""
columns = {'C': 'close', 'V': 'volume', 'O': 'open', 'H': 'high', 'L': 'low', 'T': 'date'}
frame = DataFrame(ticker) \
.drop('BV', 1) \
.rename(columns=columns)
frame['date'] = to_datetime(frame['date'], utc=True, infer_datetime_format=True)
frame.sort_values('date', inplace=True)
return frame
def populate_indicators(dataframe: DataFrame) -> DataFrame:
"""
Adds several different TA indicators to the given DataFrame
"""
dataframe['sar'] = ta.SAR(dataframe)
dataframe['adx'] = ta.ADX(dataframe)
stoch = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch['fastd']
dataframe['fastk'] = stoch['fastk']
dataframe['blower'] = ta.BBANDS(dataframe, nbdevup=2, nbdevdn=2)['lowerband']
dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
dataframe['mfi'] = ta.MFI(dataframe)
dataframe['rsi'] = ta.RSI(dataframe)
dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
dataframe['ao'] = awesome_oscillator(dataframe)
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
hilbert = ta.HT_SINE(dataframe)
dataframe['htsine'] = hilbert['sine']
dataframe['htleadsine'] = hilbert['leadsine']
return dataframe
def populate_buy_trend(dataframe: DataFrame) -> DataFrame:
"""
Based on TA indicators, populates the buy trend for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
dataframe.ix[
(dataframe['close'] < dataframe['sma']) &
(dataframe['tema'] <= dataframe['blower']) &
(dataframe['mfi'] < 25) &
(dataframe['fastd'] < 25) &
(dataframe['adx'] > 30),
'buy'] = 1
dataframe.ix[dataframe['buy'] == 1, 'buy_price'] = dataframe['close']
return dataframe
def analyze_ticker(pair: str) -> DataFrame:
"""
Get ticker data for given currency pair, push it to a DataFrame and
add several TA indicators and buy signal to it
:return DataFrame with ticker data and indicator data
"""
ticker_hist = get_ticker_history(pair)
if not ticker_hist:
logger.warning('Empty ticker history for pair %s', pair)
return DataFrame()
dataframe = parse_ticker_dataframe(ticker_hist)
dataframe = populate_indicators(dataframe)
dataframe = populate_buy_trend(dataframe)
return dataframe
def get_buy_signal(pair: str) -> bool:
"""
Calculates a buy signal based several technical analysis indicators
:param pair: pair in format BTC_ANT or BTC-ANT
:return: True if pair is good for buying, False otherwise
"""
dataframe = analyze_ticker(pair)
if dataframe.empty:
return False
latest = dataframe.iloc[-1]
# Check if dataframe is out of date
signal_date = arrow.get(latest['date'])
if signal_date < arrow.now() - timedelta(minutes=10):
return False
signal = latest['buy'] == 1
logger.debug('buy_trigger: %s (pair=%s, signal=%s)', latest['date'], pair, signal)
return signal
+423
View File
@@ -0,0 +1,423 @@
"""
This module contains the argument manager class
"""
import argparse
import os
import re
from typing import List, NamedTuple, Optional
import arrow
from freqtrade import __version__, constants
class TimeRange(NamedTuple):
"""
NamedTuple Defining timerange inputs.
[start/stop]type defines if [start/stop]ts shall be used.
if *type is none, don't use corresponding startvalue.
"""
starttype: Optional[str] = None
stoptype: Optional[str] = None
startts: int = 0
stopts: int = 0
class Arguments(object):
"""
Arguments Class. Manage the arguments received by the cli
"""
def __init__(self, args: List[str], description: str) -> None:
self.args = args
self.parsed_arg: Optional[argparse.Namespace] = None
self.parser = argparse.ArgumentParser(description=description)
def _load_args(self) -> None:
self.common_args_parser()
self._build_subcommands()
def get_parsed_arg(self) -> argparse.Namespace:
"""
Return the list of arguments
:return: List[str] List of arguments
"""
if self.parsed_arg is None:
self._load_args()
self.parsed_arg = self.parse_args()
return self.parsed_arg
def parse_args(self) -> argparse.Namespace:
"""
Parses given arguments and returns an argparse Namespace instance.
"""
parsed_arg = self.parser.parse_args(self.args)
return parsed_arg
def common_args_parser(self) -> None:
"""
Parses given common arguments and returns them as a parsed object.
"""
self.parser.add_argument(
'-v', '--verbose',
help='verbose mode (-vv for more, -vvv to get all messages)',
action='count',
dest='loglevel',
default=0,
)
self.parser.add_argument(
'--version',
action='version',
version=f'%(prog)s {__version__}'
)
self.parser.add_argument(
'-c', '--config',
help='specify configuration file (default: %(default)s)',
dest='config',
default='config.json',
type=str,
metavar='PATH',
)
self.parser.add_argument(
'-d', '--datadir',
help='path to backtest data',
dest='datadir',
default=None,
type=str,
metavar='PATH',
)
self.parser.add_argument(
'-s', '--strategy',
help='specify strategy class name (default: %(default)s)',
dest='strategy',
default='DefaultStrategy',
type=str,
metavar='NAME',
)
self.parser.add_argument(
'--strategy-path',
help='specify additional strategy lookup path',
dest='strategy_path',
type=str,
metavar='PATH',
)
self.parser.add_argument(
'--customhyperopt',
help='specify hyperopt class name (default: %(default)s)',
dest='hyperopt',
default=constants.DEFAULT_HYPEROPT,
type=str,
metavar='NAME',
)
self.parser.add_argument(
'--dynamic-whitelist',
help='dynamically generate and update whitelist'
' based on 24h BaseVolume (default: %(const)s)'
' DEPRECATED.',
dest='dynamic_whitelist',
const=constants.DYNAMIC_WHITELIST,
type=int,
metavar='INT',
nargs='?',
)
self.parser.add_argument(
'--db-url',
help='Override trades database URL, this is useful if dry_run is enabled'
' or in custom deployments (default: %(default)s)',
dest='db_url',
type=str,
metavar='PATH',
)
@staticmethod
def backtesting_options(parser: argparse.ArgumentParser) -> None:
"""
Parses given arguments for Backtesting scripts.
"""
parser.add_argument(
'--eps', '--enable-position-stacking',
help='Allow buying the same pair multiple times (position stacking)',
action='store_true',
dest='position_stacking',
default=False
)
parser.add_argument(
'--dmmp', '--disable-max-market-positions',
help='Disable applying `max_open_trades` during backtest '
'(same as setting `max_open_trades` to a very high number)',
action='store_false',
dest='use_max_market_positions',
default=True
)
parser.add_argument(
'-l', '--live',
help='using live data',
action='store_true',
dest='live',
)
parser.add_argument(
'-r', '--refresh-pairs-cached',
help='refresh the pairs files in tests/testdata with the latest data from the '
'exchange. Use it if you want to run your backtesting with up-to-date data.',
action='store_true',
dest='refresh_pairs',
)
parser.add_argument(
'--strategy-list',
help='Provide a commaseparated list of strategies to backtest '
'Please note that ticker-interval needs to be set either in config '
'or via command line. When using this together with --export trades, '
'the strategy-name is injected into the filename '
'(so backtest-data.json becomes backtest-data-DefaultStrategy.json',
nargs='+',
dest='strategy_list',
)
parser.add_argument(
'--export',
help='export backtest results, argument are: trades\
Example --export=trades',
type=str,
default=None,
dest='export',
)
parser.add_argument(
'--export-filename',
help='Save backtest results to this filename \
requires --export to be set as well\
Example --export-filename=user_data/backtest_data/backtest_today.json\
(default: %(default)s)',
type=str,
default=os.path.join('user_data', 'backtest_data', 'backtest-result.json'),
dest='exportfilename',
metavar='PATH',
)
@staticmethod
def edge_options(parser: argparse.ArgumentParser) -> None:
"""
Parses given arguments for Backtesting scripts.
"""
parser.add_argument(
'-r', '--refresh-pairs-cached',
help='refresh the pairs files in tests/testdata with the latest data from the '
'exchange. Use it if you want to run your edge with up-to-date data.',
action='store_true',
dest='refresh_pairs',
)
parser.add_argument(
'--stoplosses',
help='defines a range of stoploss against which edge will assess the strategy '
'the format is "min,max,step" (without any space).'
'example: --stoplosses=-0.01,-0.1,-0.001',
type=str,
dest='stoploss_range',
)
@staticmethod
def optimizer_shared_options(parser: argparse.ArgumentParser) -> None:
"""
Parses given common arguments for Backtesting and Hyperopt scripts.
:param parser:
:return:
"""
parser.add_argument(
'-i', '--ticker-interval',
help='specify ticker interval (1m, 5m, 30m, 1h, 1d)',
dest='ticker_interval',
type=str,
)
parser.add_argument(
'--timerange',
help='specify what timerange of data to use.',
default=None,
type=str,
dest='timerange',
)
@staticmethod
def hyperopt_options(parser: argparse.ArgumentParser) -> None:
"""
Parses given arguments for Hyperopt scripts.
"""
parser.add_argument(
'--eps', '--enable-position-stacking',
help='Allow buying the same pair multiple times (position stacking)',
action='store_true',
dest='position_stacking',
default=False
)
parser.add_argument(
'--dmmp', '--disable-max-market-positions',
help='Disable applying `max_open_trades` during backtest '
'(same as setting `max_open_trades` to a very high number)',
action='store_false',
dest='use_max_market_positions',
default=True
)
parser.add_argument(
'-e', '--epochs',
help='specify number of epochs (default: %(default)d)',
dest='epochs',
default=constants.HYPEROPT_EPOCH,
type=int,
metavar='INT',
)
parser.add_argument(
'-s', '--spaces',
help='Specify which parameters to hyperopt. Space separate list. \
Default: %(default)s',
choices=['all', 'buy', 'roi', 'stoploss'],
default='all',
nargs='+',
dest='spaces',
)
def _build_subcommands(self) -> None:
"""
Builds and attaches all subcommands
:return: None
"""
from freqtrade.optimize import backtesting, hyperopt, edge_cli
subparsers = self.parser.add_subparsers(dest='subparser')
# Add backtesting subcommand
backtesting_cmd = subparsers.add_parser('backtesting', help='backtesting module')
backtesting_cmd.set_defaults(func=backtesting.start)
self.optimizer_shared_options(backtesting_cmd)
self.backtesting_options(backtesting_cmd)
# Add edge subcommand
edge_cmd = subparsers.add_parser('edge', help='edge module')
edge_cmd.set_defaults(func=edge_cli.start)
self.optimizer_shared_options(edge_cmd)
self.edge_options(edge_cmd)
# Add hyperopt subcommand
hyperopt_cmd = subparsers.add_parser('hyperopt', help='hyperopt module')
hyperopt_cmd.set_defaults(func=hyperopt.start)
self.optimizer_shared_options(hyperopt_cmd)
self.hyperopt_options(hyperopt_cmd)
@staticmethod
def parse_timerange(text: Optional[str]) -> TimeRange:
"""
Parse the value of the argument --timerange to determine what is the range desired
:param text: value from --timerange
:return: Start and End range period
"""
if text is None:
return TimeRange(None, None, 0, 0)
syntax = [(r'^-(\d{8})$', (None, 'date')),
(r'^(\d{8})-$', ('date', None)),
(r'^(\d{8})-(\d{8})$', ('date', 'date')),
(r'^-(\d{10})$', (None, 'date')),
(r'^(\d{10})-$', ('date', None)),
(r'^(\d{10})-(\d{10})$', ('date', 'date')),
(r'^(-\d+)$', (None, 'line')),
(r'^(\d+)-$', ('line', None)),
(r'^(\d+)-(\d+)$', ('index', 'index'))]
for rex, stype in syntax:
# Apply the regular expression to text
match = re.match(rex, text)
if match: # Regex has matched
rvals = match.groups()
index = 0
start: int = 0
stop: int = 0
if stype[0]:
starts = rvals[index]
if stype[0] == 'date' and len(starts) == 8:
start = arrow.get(starts, 'YYYYMMDD').timestamp
else:
start = int(starts)
index += 1
if stype[1]:
stops = rvals[index]
if stype[1] == 'date' and len(stops) == 8:
stop = arrow.get(stops, 'YYYYMMDD').timestamp
else:
stop = int(stops)
return TimeRange(stype[0], stype[1], start, stop)
raise Exception('Incorrect syntax for timerange "%s"' % text)
def scripts_options(self) -> None:
"""
Parses given arguments for scripts.
"""
self.parser.add_argument(
'-p', '--pair',
help='Show profits for only this pairs. Pairs are comma-separated.',
dest='pair',
default=None
)
def testdata_dl_options(self) -> None:
"""
Parses given arguments for testdata download
"""
self.parser.add_argument(
'--pairs-file',
help='File containing a list of pairs to download',
dest='pairs_file',
default=None,
metavar='PATH',
)
self.parser.add_argument(
'--export',
help='Export files to given dir',
dest='export',
default=None,
metavar='PATH',
)
self.parser.add_argument(
'-c', '--config',
help='specify configuration file, used for additional exchange parameters',
dest='config',
default=None,
type=str,
metavar='PATH',
)
self.parser.add_argument(
'--days',
help='Download data for number of days',
dest='days',
type=int,
metavar='INT',
default=None
)
self.parser.add_argument(
'--exchange',
help='Exchange name (default: %(default)s). Only valid if no config is provided',
dest='exchange',
type=str,
default='bittrex'
)
self.parser.add_argument(
'-t', '--timeframes',
help='Specify which tickers to download. Space separated list. \
Default: %(default)s',
choices=['1m', '3m', '5m', '15m', '30m', '1h', '2h', '4h',
'6h', '8h', '12h', '1d', '3d', '1w'],
default=['1m', '5m'],
nargs='+',
dest='timeframes',
)
self.parser.add_argument(
'--erase',
help='Clean all existing data for the selected exchange/pairs/timeframes',
dest='erase',
action='store_true'
)
+325
View File
@@ -0,0 +1,325 @@
"""
This module contains the configuration class
"""
import json
import logging
import os
from argparse import Namespace
from typing import Any, Dict, Optional
import ccxt
from jsonschema import Draft4Validator, validate
from jsonschema.exceptions import ValidationError, best_match
from freqtrade import OperationalException, constants
logger = logging.getLogger(__name__)
def set_loggers(log_level: int = 0) -> None:
"""
Set the logger level for Third party libs
:return: None
"""
logging.getLogger('requests').setLevel(logging.INFO if log_level <= 1 else logging.DEBUG)
logging.getLogger("urllib3").setLevel(logging.INFO if log_level <= 1 else logging.DEBUG)
logging.getLogger('ccxt.base.exchange').setLevel(
logging.INFO if log_level <= 2 else logging.DEBUG)
logging.getLogger('telegram').setLevel(logging.INFO)
class Configuration(object):
"""
Class to read and init the bot configuration
Reuse this class for the bot, backtesting, hyperopt and every script that required configuration
"""
def __init__(self, args: Namespace) -> None:
self.args = args
self.config: Optional[Dict[str, Any]] = None
def load_config(self) -> Dict[str, Any]:
"""
Extract information for sys.argv and load the bot configuration
:return: Configuration dictionary
"""
logger.info('Using config: %s ...', self.args.config)
config = self._load_config_file(self.args.config)
# Set strategy if not specified in config and or if it's non default
if self.args.strategy != constants.DEFAULT_STRATEGY or not config.get('strategy'):
config.update({'strategy': self.args.strategy})
if self.args.strategy_path:
config.update({'strategy_path': self.args.strategy_path})
# Add the hyperopt file to use
config.update({'hyperopt': self.args.hyperopt})
# Load Common configuration
config = self._load_common_config(config)
# Load Backtesting
config = self._load_backtesting_config(config)
# Load Edge
config = self._load_edge_config(config)
# Load Hyperopt
config = self._load_hyperopt_config(config)
return config
def _load_config_file(self, path: str) -> Dict[str, Any]:
"""
Loads a config file from the given path
:param path: path as str
:return: configuration as dictionary
"""
try:
with open(path) as file:
conf = json.load(file)
except FileNotFoundError:
raise OperationalException(
f'Config file "{path}" not found!'
' Please create a config file or check whether it exists.')
if 'internals' not in conf:
conf['internals'] = {}
logger.info('Validating configuration ...')
return self._validate_config(conf)
def _load_common_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
"""
Extract information for sys.argv and load common configuration
:return: configuration as dictionary
"""
# Log level
if 'loglevel' in self.args and self.args.loglevel:
config.update({'verbosity': self.args.loglevel})
else:
config.update({'verbosity': 0})
logging.basicConfig(
level=logging.INFO if config['verbosity'] < 1 else logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
)
set_loggers(config['verbosity'])
logger.info('Verbosity set to %s', config['verbosity'])
# Add dynamic_whitelist if found
if 'dynamic_whitelist' in self.args and self.args.dynamic_whitelist:
# Update to volumePairList (the previous default)
config['pairlist'] = {'method': 'VolumePairList',
'config': {'number_assets': self.args.dynamic_whitelist}
}
logger.warning(
'Parameter --dynamic-whitelist has been deprecated, '
'and will be completely replaced by the whitelist dict in the future. '
'For now: using dynamically generated whitelist based on VolumePairList. '
'(not applicable with Backtesting and Hyperopt)'
)
if self.args.db_url and self.args.db_url != constants.DEFAULT_DB_PROD_URL:
config.update({'db_url': self.args.db_url})
logger.info('Parameter --db-url detected ...')
else:
# Set default here
config.update({'db_url': constants.DEFAULT_DB_PROD_URL})
if config.get('dry_run', False):
logger.info('Dry run is enabled')
if config.get('db_url') in [None, constants.DEFAULT_DB_PROD_URL]:
# Default to in-memory db for dry_run if not specified
config['db_url'] = constants.DEFAULT_DB_DRYRUN_URL
else:
if not config.get('db_url', None):
config['db_url'] = constants.DEFAULT_DB_PROD_URL
logger.info('Dry run is disabled')
if config.get('forcebuy_enable', False):
logger.warning('`forcebuy` RPC message enabled.')
# Setting max_open_trades to infinite if -1
if config.get('max_open_trades') == -1:
config['max_open_trades'] = float('inf')
logger.info(f'Using DB: "{config["db_url"]}"')
# Check if the exchange set by the user is supported
self.check_exchange(config)
return config
def _create_default_datadir(self, config: Dict[str, Any]) -> str:
exchange_name = config.get('exchange', {}).get('name').lower()
default_path = os.path.join('user_data', 'data', exchange_name)
if not os.path.isdir(default_path):
os.makedirs(default_path)
logger.info(f'Created data directory: {default_path}')
return default_path
def _load_backtesting_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
"""
Extract information for sys.argv and load Backtesting configuration
:return: configuration as dictionary
"""
# If -i/--ticker-interval is used we override the configuration parameter
# (that will override the strategy configuration)
if 'ticker_interval' in self.args and self.args.ticker_interval:
config.update({'ticker_interval': self.args.ticker_interval})
logger.info('Parameter -i/--ticker-interval detected ...')
logger.info('Using ticker_interval: %s ...', config.get('ticker_interval'))
# If -l/--live is used we add it to the configuration
if 'live' in self.args and self.args.live:
config.update({'live': True})
logger.info('Parameter -l/--live detected ...')
# If --enable-position-stacking is used we add it to the configuration
if 'position_stacking' in self.args and self.args.position_stacking:
config.update({'position_stacking': True})
logger.info('Parameter --enable-position-stacking detected ...')
# If --disable-max-market-positions is used we add it to the configuration
if 'use_max_market_positions' in self.args and not self.args.use_max_market_positions:
config.update({'use_max_market_positions': False})
logger.info('Parameter --disable-max-market-positions detected ...')
logger.info('max_open_trades set to unlimited ...')
else:
logger.info('Using max_open_trades: %s ...', config.get('max_open_trades'))
# If --timerange is used we add it to the configuration
if 'timerange' in self.args and self.args.timerange:
config.update({'timerange': self.args.timerange})
logger.info('Parameter --timerange detected: %s ...', self.args.timerange)
# If --datadir is used we add it to the configuration
if 'datadir' in self.args and self.args.datadir:
config.update({'datadir': self.args.datadir})
else:
config.update({'datadir': self._create_default_datadir(config)})
logger.info('Using data folder: %s ...', config.get('datadir'))
# If -r/--refresh-pairs-cached is used we add it to the configuration
if 'refresh_pairs' in self.args and self.args.refresh_pairs:
config.update({'refresh_pairs': True})
logger.info('Parameter -r/--refresh-pairs-cached detected ...')
if 'strategy_list' in self.args and self.args.strategy_list:
config.update({'strategy_list': self.args.strategy_list})
logger.info('Using strategy list of %s Strategies', len(self.args.strategy_list))
if 'ticker_interval' in self.args and self.args.ticker_interval:
config.update({'ticker_interval': self.args.ticker_interval})
logger.info('Overriding ticker interval with Command line argument')
# If --export is used we add it to the configuration
if 'export' in self.args and self.args.export:
config.update({'export': self.args.export})
logger.info('Parameter --export detected: %s ...', self.args.export)
# If --export-filename is used we add it to the configuration
if 'export' in config and 'exportfilename' in self.args and self.args.exportfilename:
config.update({'exportfilename': self.args.exportfilename})
logger.info('Storing backtest results to %s ...', self.args.exportfilename)
return config
def _load_edge_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
"""
Extract information for sys.argv and load Edge configuration
:return: configuration as dictionary
"""
# If --timerange is used we add it to the configuration
if 'timerange' in self.args and self.args.timerange:
config.update({'timerange': self.args.timerange})
logger.info('Parameter --timerange detected: %s ...', self.args.timerange)
# If --timerange is used we add it to the configuration
if 'stoploss_range' in self.args and self.args.stoploss_range:
txt_range = eval(self.args.stoploss_range)
config['edge'].update({'stoploss_range_min': txt_range[0]})
config['edge'].update({'stoploss_range_max': txt_range[1]})
config['edge'].update({'stoploss_range_step': txt_range[2]})
logger.info('Parameter --stoplosses detected: %s ...', self.args.stoploss_range)
# If -r/--refresh-pairs-cached is used we add it to the configuration
if 'refresh_pairs' in self.args and self.args.refresh_pairs:
config.update({'refresh_pairs': True})
logger.info('Parameter -r/--refresh-pairs-cached detected ...')
return config
def _load_hyperopt_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
"""
Extract information for sys.argv and load Hyperopt configuration
:return: configuration as dictionary
"""
# If --epochs is used we add it to the configuration
if 'epochs' in self.args and self.args.epochs:
config.update({'epochs': self.args.epochs})
logger.info('Parameter --epochs detected ...')
logger.info('Will run Hyperopt with for %s epochs ...', config.get('epochs'))
# If --spaces is used we add it to the configuration
if 'spaces' in self.args and self.args.spaces:
config.update({'spaces': self.args.spaces})
logger.info('Parameter -s/--spaces detected: %s', config.get('spaces'))
return config
def _validate_config(self, conf: Dict[str, Any]) -> Dict[str, Any]:
"""
Validate the configuration follow the Config Schema
:param conf: Config in JSON format
:return: Returns the config if valid, otherwise throw an exception
"""
try:
validate(conf, constants.CONF_SCHEMA, Draft4Validator)
return conf
except ValidationError as exception:
logger.critical(
'Invalid configuration. See config.json.example. Reason: %s',
exception
)
raise ValidationError(
best_match(Draft4Validator(constants.CONF_SCHEMA).iter_errors(conf)).message
)
def get_config(self) -> Dict[str, Any]:
"""
Return the config. Use this method to get the bot config
:return: Dict: Bot config
"""
if self.config is None:
self.config = self.load_config()
return self.config
def check_exchange(self, config: Dict[str, Any]) -> bool:
"""
Check if the exchange name in the config file is supported by Freqtrade
:return: True or raised an exception if the exchange if not supported
"""
exchange = config.get('exchange', {}).get('name').lower()
if exchange not in ccxt.exchanges:
exception_msg = f'Exchange "{exchange}" not supported.\n' \
f'The following exchanges are supported: {", ".join(ccxt.exchanges)}'
logger.critical(exception_msg)
raise OperationalException(
exception_msg
)
# Depreciation warning
if 'ccxt_rate_limit' in config.get('exchange', {}):
logger.warning("`ccxt_rate_limit` has been deprecated in favor of "
"`ccxt_config` and `ccxt_async_config` and will be removed "
"in a future version.")
logger.debug('Exchange "%s" supported', exchange)
return True
+237
View File
@@ -0,0 +1,237 @@
# pragma pylint: disable=too-few-public-methods
"""
bot constants
"""
DYNAMIC_WHITELIST = 20 # pairs
PROCESS_THROTTLE_SECS = 5 # sec
TICKER_INTERVAL = 5 # min
HYPEROPT_EPOCH = 100 # epochs
RETRY_TIMEOUT = 30 # sec
DEFAULT_STRATEGY = 'DefaultStrategy'
DEFAULT_HYPEROPT = 'DefaultHyperOpts'
DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite'
DEFAULT_DB_DRYRUN_URL = 'sqlite://'
UNLIMITED_STAKE_AMOUNT = 'unlimited'
REQUIRED_ORDERTIF = ['buy', 'sell']
REQUIRED_ORDERTYPES = ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']
ORDERTYPE_POSSIBILITIES = ['limit', 'market']
ORDERTIF_POSSIBILITIES = ['gtc', 'fok', 'ioc']
AVAILABLE_PAIRLISTS = ['StaticPairList', 'VolumePairList']
TICKER_INTERVAL_MINUTES = {
'1m': 1,
'3m': 3,
'5m': 5,
'15m': 15,
'30m': 30,
'1h': 60,
'2h': 120,
'4h': 240,
'6h': 360,
'8h': 480,
'12h': 720,
'1d': 1440,
'3d': 4320,
'1w': 10080,
}
SUPPORTED_FIAT = [
"AUD", "BRL", "CAD", "CHF", "CLP", "CNY", "CZK", "DKK",
"EUR", "GBP", "HKD", "HUF", "IDR", "ILS", "INR", "JPY",
"KRW", "MXN", "MYR", "NOK", "NZD", "PHP", "PKR", "PLN",
"RUB", "SEK", "SGD", "THB", "TRY", "TWD", "ZAR", "USD",
"BTC", "XBT", "ETH", "XRP", "LTC", "BCH", "USDT"
]
# Required json-schema for user specified config
CONF_SCHEMA = {
'type': 'object',
'properties': {
'max_open_trades': {'type': 'integer', 'minimum': -1},
'ticker_interval': {'type': 'string', 'enum': list(TICKER_INTERVAL_MINUTES.keys())},
'stake_currency': {'type': 'string', 'enum': ['BTC', 'XBT', 'ETH', 'USDT', 'EUR', 'USD']},
'stake_amount': {
"type": ["number", "string"],
"minimum": 0.0005,
"pattern": UNLIMITED_STAKE_AMOUNT
},
'fiat_display_currency': {'type': 'string', 'enum': SUPPORTED_FIAT},
'dry_run': {'type': 'boolean'},
'process_only_new_candles': {'type': 'boolean'},
'minimal_roi': {
'type': 'object',
'patternProperties': {
'^[0-9.]+$': {'type': 'number'}
},
'minProperties': 1
},
'stoploss': {'type': 'number', 'maximum': 0, 'exclusiveMaximum': True},
'trailing_stop': {'type': 'boolean'},
'trailing_stop_positive': {'type': 'number', 'minimum': 0, 'maximum': 1},
'trailing_stop_positive_offset': {'type': 'number', 'minimum': 0, 'maximum': 1},
'unfilledtimeout': {
'type': 'object',
'properties': {
'buy': {'type': 'number', 'minimum': 3},
'sell': {'type': 'number', 'minimum': 10}
}
},
'bid_strategy': {
'type': 'object',
'properties': {
'ask_last_balance': {
'type': 'number',
'minimum': 0,
'maximum': 1,
'exclusiveMaximum': False,
'use_order_book': {'type': 'boolean'},
'order_book_top': {'type': 'number', 'maximum': 20, 'minimum': 1},
'check_depth_of_market': {
'type': 'object',
'properties': {
'enabled': {'type': 'boolean'},
'bids_to_ask_delta': {'type': 'number', 'minimum': 0},
}
},
},
},
'required': ['ask_last_balance']
},
'ask_strategy': {
'type': 'object',
'properties': {
'use_order_book': {'type': 'boolean'},
'order_book_min': {'type': 'number', 'minimum': 1},
'order_book_max': {'type': 'number', 'minimum': 1, 'maximum': 50}
}
},
'order_types': {
'type': 'object',
'properties': {
'buy': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
'sell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
'stoploss': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
'stoploss_on_exchange': {'type': 'boolean'}
},
'required': ['buy', 'sell', 'stoploss', 'stoploss_on_exchange']
},
'order_time_in_force': {
'type': 'object',
'properties': {
'buy': {'type': 'string', 'enum': ORDERTIF_POSSIBILITIES},
'sell': {'type': 'string', 'enum': ORDERTIF_POSSIBILITIES}
},
'required': ['buy', 'sell']
},
'exchange': {'$ref': '#/definitions/exchange'},
'edge': {'$ref': '#/definitions/edge'},
'experimental': {
'type': 'object',
'properties': {
'use_sell_signal': {'type': 'boolean'},
'sell_profit_only': {'type': 'boolean'},
'ignore_roi_if_buy_signal_true': {'type': 'boolean'}
}
},
'pairlist': {
'type': 'object',
'properties': {
'method': {'type': 'string', 'enum': AVAILABLE_PAIRLISTS},
'config': {'type': 'object'}
},
'required': ['method']
},
'telegram': {
'type': 'object',
'properties': {
'enabled': {'type': 'boolean'},
'token': {'type': 'string'},
'chat_id': {'type': 'string'},
},
'required': ['enabled', 'token', 'chat_id']
},
'webhook': {
'type': 'object',
'properties': {
'enabled': {'type': 'boolean'},
'webhookbuy': {'type': 'object'},
'webhooksell': {'type': 'object'},
'webhookstatus': {'type': 'object'},
},
},
'db_url': {'type': 'string'},
'initial_state': {'type': 'string', 'enum': ['running', 'stopped']},
'forcebuy_enable': {'type': 'boolean'},
'internals': {
'type': 'object',
'properties': {
'process_throttle_secs': {'type': 'number'},
'interval': {'type': 'integer'}
}
}
},
'definitions': {
'exchange': {
'type': 'object',
'properties': {
'name': {'type': 'string'},
'sandbox': {'type': 'boolean'},
'key': {'type': 'string'},
'secret': {'type': 'string'},
'password': {'type': 'string'},
'uid': {'type': 'string'},
'pair_whitelist': {
'type': 'array',
'items': {
'type': 'string',
'pattern': '^[0-9A-Z]+/[0-9A-Z]+$'
},
'uniqueItems': True
},
'pair_blacklist': {
'type': 'array',
'items': {
'type': 'string',
'pattern': '^[0-9A-Z]+/[0-9A-Z]+$'
},
'uniqueItems': True
},
'outdated_offset': {'type': 'integer', 'minimum': 1},
'ccxt_config': {'type': 'object'},
'ccxt_async_config': {'type': 'object'}
},
'required': ['name', 'key', 'secret', 'pair_whitelist']
},
'edge': {
'type': 'object',
'properties': {
"enabled": {'type': 'boolean'},
"process_throttle_secs": {'type': 'integer', 'minimum': 600},
"calculate_since_number_of_days": {'type': 'integer'},
"allowed_risk": {'type': 'number'},
"capital_available_percentage": {'type': 'number'},
"stoploss_range_min": {'type': 'number'},
"stoploss_range_max": {'type': 'number'},
"stoploss_range_step": {'type': 'number'},
"minimum_winrate": {'type': 'number'},
"minimum_expectancy": {'type': 'number'},
"min_trade_number": {'type': 'number'},
"max_trade_duration_minute": {'type': 'integer'},
"remove_pumps": {'type': 'boolean'}
},
'required': ['process_throttle_secs', 'allowed_risk', 'capital_available_percentage']
}
},
'anyOf': [
{'required': ['exchange']}
],
'required': [
'max_open_trades',
'stake_currency',
'stake_amount',
'dry_run',
'bid_strategy',
'telegram'
]
}
+8
View File
@@ -0,0 +1,8 @@
"""
Module to handle data operations for freqtrade
"""
# limit what's imported when using `from freqtrad.data import *``
__all__ = [
'converter'
]
+60
View File
@@ -0,0 +1,60 @@
"""
Functions to convert data from one format to another
"""
import logging
import pandas as pd
from pandas import DataFrame, to_datetime
logger = logging.getLogger(__name__)
def parse_ticker_dataframe(ticker: list) -> DataFrame:
"""
Converts a ticker-list (format ccxt.fetch_ohlcv) to a Dataframe
:param ticker: ticker list, as returned by exchange.async_get_candle_history
:return: DataFrame
"""
logger.debug("Parsing tickerlist to dataframe")
cols = ['date', 'open', 'high', 'low', 'close', 'volume']
frame = DataFrame(ticker, columns=cols)
frame['date'] = to_datetime(frame['date'],
unit='ms',
utc=True,
infer_datetime_format=True)
# group by index and aggregate results to eliminate duplicate ticks
frame = frame.groupby(by='date', as_index=False, sort=True).agg({
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'max',
})
frame.drop(frame.tail(1).index, inplace=True) # eliminate partial candle
logger.debug('Dropping last candle')
return frame
def order_book_to_dataframe(bids: list, asks: list) -> DataFrame:
"""
Gets order book list, returns dataframe with below format per suggested by creslin
-------------------------------------------------------------------
b_sum b_size bids asks a_size a_sum
-------------------------------------------------------------------
"""
cols = ['bids', 'b_size']
bids_frame = DataFrame(bids, columns=cols)
# add cumulative sum column
bids_frame['b_sum'] = bids_frame['b_size'].cumsum()
cols2 = ['asks', 'a_size']
asks_frame = DataFrame(asks, columns=cols2)
# add cumulative sum column
asks_frame['a_sum'] = asks_frame['a_size'].cumsum()
frame = pd.concat([bids_frame['b_sum'], bids_frame['b_size'], bids_frame['bids'],
asks_frame['asks'], asks_frame['a_size'], asks_frame['a_sum']], axis=1,
keys=['b_sum', 'b_size', 'bids', 'asks', 'a_size', 'a_sum'])
# logger.info('order book %s', frame )
return frame
+251
View File
@@ -0,0 +1,251 @@
"""
Handle historic data (ohlcv).
includes:
* load data for a pair (or a list of pairs) from disk
* download data from exchange and store to disk
"""
import gzip
import logging
from pathlib import Path
from typing import Optional, List, Dict, Tuple, Any
import arrow
from pandas import DataFrame
import ujson
from freqtrade import misc, constants, OperationalException
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.exchange import Exchange
from freqtrade.arguments import TimeRange
logger = logging.getLogger(__name__)
def json_load(data):
"""
load data with ujson
Use this to have a consistent experience,
otherwise "precise_float" needs to be passed to all load operations
"""
return ujson.load(data, precise_float=True)
def trim_tickerlist(tickerlist: List[Dict], timerange: TimeRange) -> List[Dict]:
"""
Trim tickerlist based on given timerange
"""
if not tickerlist:
return tickerlist
start_index = 0
stop_index = len(tickerlist)
if timerange.starttype == 'line':
stop_index = timerange.startts
if timerange.starttype == 'index':
start_index = timerange.startts
elif timerange.starttype == 'date':
while (start_index < len(tickerlist) and
tickerlist[start_index][0] < timerange.startts * 1000):
start_index += 1
if timerange.stoptype == 'line':
start_index = len(tickerlist) + timerange.stopts
if timerange.stoptype == 'index':
stop_index = timerange.stopts
elif timerange.stoptype == 'date':
while (stop_index > 0 and
tickerlist[stop_index-1][0] > timerange.stopts * 1000):
stop_index -= 1
if start_index > stop_index:
raise ValueError(f'The timerange [{timerange.startts},{timerange.stopts}] is incorrect')
return tickerlist[start_index:stop_index]
def load_tickerdata_file(
datadir: Optional[Path], pair: str,
ticker_interval: str,
timerange: Optional[TimeRange] = None) -> Optional[list]:
"""
Load a pair from file, either .json.gz or .json
:return tickerlist or None if unsuccesful
"""
path = make_testdata_path(datadir)
pair_s = pair.replace('/', '_')
file = path.joinpath(f'{pair_s}-{ticker_interval}.json')
gzipfile = file.with_suffix(file.suffix + '.gz')
# Try gzip file first, otherwise regular json file.
if gzipfile.is_file():
logger.debug('Loading ticker data from file %s', gzipfile)
with gzip.open(gzipfile) as tickerdata:
pairdata = json_load(tickerdata)
elif file.is_file():
logger.debug('Loading ticker data from file %s', file)
with open(file) as tickerdata:
pairdata = json_load(tickerdata)
else:
return None
if timerange:
pairdata = trim_tickerlist(pairdata, timerange)
return pairdata
def load_pair_history(pair: str,
ticker_interval: str,
datadir: Optional[Path],
timerange: TimeRange = TimeRange(None, None, 0, 0),
refresh_pairs: bool = False,
exchange: Optional[Exchange] = None,
) -> DataFrame:
"""
Loads cached ticker history for the given pair.
:return: DataFrame with ohlcv data
"""
pairdata = load_tickerdata_file(datadir, pair, ticker_interval, timerange=timerange)
# If the user force the refresh of pairs
if refresh_pairs:
if not exchange:
raise OperationalException("Exchange needs to be initialized when "
"calling load_data with refresh_pairs=True")
logger.info('Download data for all pairs and store them in %s', datadir)
download_pair_history(datadir=datadir,
exchange=exchange,
pair=pair,
tick_interval=ticker_interval,
timerange=timerange)
if pairdata:
if timerange.starttype == 'date' and pairdata[0][0] > timerange.startts * 1000:
logger.warning('Missing data at start for pair %s, data starts at %s',
pair, arrow.get(pairdata[0][0] // 1000).strftime('%Y-%m-%d %H:%M:%S'))
if timerange.stoptype == 'date' and pairdata[-1][0] < timerange.stopts * 1000:
logger.warning('Missing data at end for pair %s, data ends at %s',
pair,
arrow.get(pairdata[-1][0] // 1000).strftime('%Y-%m-%d %H:%M:%S'))
return parse_ticker_dataframe(pairdata)
else:
logger.warning('No data for pair: "%s", Interval: %s. '
'Use --refresh-pairs-cached to download the data',
pair, ticker_interval)
return None
def load_data(datadir: Optional[Path],
ticker_interval: str,
pairs: List[str],
refresh_pairs: bool = False,
exchange: Optional[Exchange] = None,
timerange: TimeRange = TimeRange(None, None, 0, 0)) -> Dict[str, DataFrame]:
"""
Loads ticker history data for a list of pairs the given parameters
:return: dict(<pair>:<tickerlist>)
"""
result = {}
for pair in pairs:
hist = load_pair_history(pair=pair, ticker_interval=ticker_interval,
datadir=datadir, timerange=timerange,
refresh_pairs=refresh_pairs,
exchange=exchange)
if hist is not None:
result[pair] = hist
return result
def make_testdata_path(datadir: Optional[Path]) -> Path:
"""Return the path where testdata files are stored"""
return datadir or (Path(__file__).parent.parent / "tests" / "testdata").resolve()
def load_cached_data_for_updating(filename: Path, tick_interval: str,
timerange: Optional[TimeRange]) -> Tuple[List[Any],
Optional[int]]:
"""
Load cached data and choose what part of the data should be updated
"""
since_ms = None
# user sets timerange, so find the start time
if timerange:
if timerange.starttype == 'date':
since_ms = timerange.startts * 1000
elif timerange.stoptype == 'line':
num_minutes = timerange.stopts * constants.TICKER_INTERVAL_MINUTES[tick_interval]
since_ms = arrow.utcnow().shift(minutes=num_minutes).timestamp * 1000
# read the cached file
if filename.is_file():
with open(filename, "rt") as file:
data = json_load(file)
# remove the last item, could be incomplete candle
if data:
data.pop()
else:
data = []
if data:
if since_ms and since_ms < data[0][0]:
# Earlier data than existing data requested, redownload all
data = []
else:
# a part of the data was already downloaded, so download unexist data only
since_ms = data[-1][0] + 1
return (data, since_ms)
def download_pair_history(datadir: Optional[Path],
exchange: Exchange,
pair: str,
tick_interval: str = '5m',
timerange: Optional[TimeRange] = None) -> bool:
"""
Download the latest ticker intervals from the exchange for the pair passed in parameters
The data is downloaded starting from the last correct ticker interval data that
exists in a cache. If timerange starts earlier than the data in the cache,
the full data will be redownloaded
Based on @Rybolov work: https://github.com/rybolov/freqtrade-data
:param pair: pair to download
:param tick_interval: ticker interval
:param timerange: range of time to download
:return: bool with success state
"""
try:
path = make_testdata_path(datadir)
filepair = pair.replace("/", "_")
filename = path.joinpath(f'{filepair}-{tick_interval}.json')
logger.info('Download the pair: "%s", Interval: %s', pair, tick_interval)
data, since_ms = load_cached_data_for_updating(filename, tick_interval, timerange)
logger.debug("Current Start: %s", misc.format_ms_time(data[1][0]) if data else 'None')
logger.debug("Current End: %s", misc.format_ms_time(data[-1][0]) if data else 'None')
# Default since_ms to 30 days if nothing is given
new_data = exchange.get_history(pair=pair, tick_interval=tick_interval,
since_ms=since_ms if since_ms
else
int(arrow.utcnow().shift(days=-30).float_timestamp) * 1000)
data.extend(new_data)
logger.debug("New Start: %s", misc.format_ms_time(data[0][0]))
logger.debug("New End: %s", misc.format_ms_time(data[-1][0]))
misc.file_dump_json(filename, data)
return True
except BaseException:
logger.info('Failed to download the pair: "%s", Interval: %s',
pair, tick_interval)
return False
+434
View File
@@ -0,0 +1,434 @@
# pragma pylint: disable=W0603
""" Edge positioning package """
import logging
from pathlib import Path
from typing import Any, Dict, NamedTuple
import arrow
import numpy as np
import utils_find_1st as utf1st
from pandas import DataFrame
from freqtrade import constants, OperationalException
from freqtrade.arguments import Arguments
from freqtrade.arguments import TimeRange
from freqtrade.data import history
from freqtrade.optimize import get_timeframe
from freqtrade.strategy.interface import SellType
logger = logging.getLogger(__name__)
class PairInfo(NamedTuple):
stoploss: float
winrate: float
risk_reward_ratio: float
required_risk_reward: float
expectancy: float
nb_trades: int
avg_trade_duration: float
class Edge():
"""
Calculates Win Rate, Risk Reward Ratio, Expectancy
against historical data for a give set of markets and a strategy
it then adjusts stoploss and position size accordingly
and force it into the strategy
Author: https://github.com/mishaker
"""
config: Dict = {}
_cached_pairs: Dict[str, Any] = {} # Keeps a list of pairs
def __init__(self, config: Dict[str, Any], exchange, strategy) -> None:
self.config = config
self.exchange = exchange
self.strategy = strategy
self.ticker_interval = self.strategy.ticker_interval
self.tickerdata_to_dataframe = self.strategy.tickerdata_to_dataframe
self.get_timeframe = get_timeframe
self.advise_sell = self.strategy.advise_sell
self.advise_buy = self.strategy.advise_buy
self.edge_config = self.config.get('edge', {})
self._cached_pairs: Dict[str, Any] = {} # Keeps a list of pairs
self._final_pairs: list = []
# checking max_open_trades. it should be -1 as with Edge
# the number of trades is determined by position size
if self.config['max_open_trades'] != -1:
logger.critical('max_open_trades should be -1 in config !')
if self.config['stake_amount'] != constants.UNLIMITED_STAKE_AMOUNT:
raise OperationalException('Edge works only with unlimited stake amount')
self._capital_percentage: float = self.edge_config.get('capital_available_percentage')
self._allowed_risk: float = self.edge_config.get('allowed_risk')
self._since_number_of_days: int = self.edge_config.get('calculate_since_number_of_days', 14)
self._last_updated: int = 0 # Timestamp of pairs last updated time
self._refresh_pairs = True
self._stoploss_range_min = float(self.edge_config.get('stoploss_range_min', -0.01))
self._stoploss_range_max = float(self.edge_config.get('stoploss_range_max', -0.05))
self._stoploss_range_step = float(self.edge_config.get('stoploss_range_step', -0.001))
# calculating stoploss range
self._stoploss_range = np.arange(
self._stoploss_range_min,
self._stoploss_range_max,
self._stoploss_range_step
)
self._timerange: TimeRange = Arguments.parse_timerange("%s-" % arrow.now().shift(
days=-1 * self._since_number_of_days).format('YYYYMMDD'))
self.fee = self.exchange.get_fee()
def calculate(self) -> bool:
pairs = self.config['exchange']['pair_whitelist']
heartbeat = self.edge_config.get('process_throttle_secs')
if (self._last_updated > 0) and (
self._last_updated + heartbeat > arrow.utcnow().timestamp):
return False
data: Dict[str, Any] = {}
logger.info('Using stake_currency: %s ...', self.config['stake_currency'])
logger.info('Using local backtesting data (using whitelist in given config) ...')
data = history.load_data(
datadir=Path(self.config['datadir']) if self.config.get('datadir') else None,
pairs=pairs,
ticker_interval=self.ticker_interval,
refresh_pairs=self._refresh_pairs,
exchange=self.exchange,
timerange=self._timerange
)
if not data:
# Reinitializing cached pairs
self._cached_pairs = {}
logger.critical("No data found. Edge is stopped ...")
return False
preprocessed = self.tickerdata_to_dataframe(data)
# Print timeframe
min_date, max_date = self.get_timeframe(preprocessed)
logger.info(
'Measuring data from %s up to %s (%s days) ...',
min_date.isoformat(),
max_date.isoformat(),
(max_date - min_date).days
)
headers = ['date', 'buy', 'open', 'close', 'sell', 'high', 'low']
trades: list = []
for pair, pair_data in preprocessed.items():
# Sorting dataframe by date and reset index
pair_data = pair_data.sort_values(by=['date'])
pair_data = pair_data.reset_index(drop=True)
ticker_data = self.advise_sell(
self.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy()
trades += self._find_trades_for_stoploss_range(ticker_data, pair, self._stoploss_range)
# If no trade found then exit
if len(trades) == 0:
return False
# Fill missing, calculable columns, profit, duration , abs etc.
trades_df = self._fill_calculable_fields(DataFrame(trades))
self._cached_pairs = self._process_expectancy(trades_df)
self._last_updated = arrow.utcnow().timestamp
return True
def stake_amount(self, pair: str, free_capital: float,
total_capital: float, capital_in_trade: float) -> float:
stoploss = self.stoploss(pair)
available_capital = (total_capital + capital_in_trade) * self._capital_percentage
allowed_capital_at_risk = available_capital * self._allowed_risk
max_position_size = abs(allowed_capital_at_risk / stoploss)
position_size = min(max_position_size, free_capital)
if pair in self._cached_pairs:
logger.info(
'winrate: %s, expectancy: %s, position size: %s, pair: %s,'
' capital in trade: %s, free capital: %s, total capital: %s,'
' stoploss: %s, available capital: %s.',
self._cached_pairs[pair].winrate,
self._cached_pairs[pair].expectancy,
position_size, pair,
capital_in_trade, free_capital, total_capital,
stoploss, available_capital
)
return round(position_size, 15)
def stoploss(self, pair: str) -> float:
if pair in self._cached_pairs:
return self._cached_pairs[pair].stoploss
else:
logger.warning('tried to access stoploss of a non-existing pair, '
'strategy stoploss is returned instead.')
return self.strategy.stoploss
def adjust(self, pairs) -> list:
"""
Filters out and sorts "pairs" according to Edge calculated pairs
"""
final = []
for pair, info in self._cached_pairs.items():
if info.expectancy > float(self.edge_config.get('minimum_expectancy', 0.2)) and \
info.winrate > float(self.edge_config.get('minimum_winrate', 0.60)) and \
pair in pairs:
final.append(pair)
if self._final_pairs != final:
self._final_pairs = final
if self._final_pairs:
logger.info('Edge validated only %s', self._final_pairs)
else:
logger.info('Edge removed all pairs as no pair with minimum expectancy was found !')
return self._final_pairs
def _fill_calculable_fields(self, result: DataFrame) -> DataFrame:
"""
The result frame contains a number of columns that are calculable
from other columns. These are left blank till all rows are added,
to be populated in single vector calls.
Columns to be populated are:
- Profit
- trade duration
- profit abs
:param result Dataframe
:return: result Dataframe
"""
# stake and fees
# stake = 0.015
# 0.05% is 0.0005
# fee = 0.001
# we set stake amount to an arbitrary amount.
# as it doesn't change the calculation.
# all returned values are relative. they are percentages.
stake = 0.015
fee = self.fee
open_fee = fee / 2
close_fee = fee / 2
result['trade_duration'] = result['close_time'] - result['open_time']
result['trade_duration'] = result['trade_duration'].map(
lambda x: int(x.total_seconds() / 60))
# Spends, Takes, Profit, Absolute Profit
# Buy Price
result['buy_vol'] = stake / result['open_rate'] # How many target are we buying
result['buy_fee'] = stake * open_fee
result['buy_spend'] = stake + result['buy_fee'] # How much we're spending
# Sell price
result['sell_sum'] = result['buy_vol'] * result['close_rate']
result['sell_fee'] = result['sell_sum'] * close_fee
result['sell_take'] = result['sell_sum'] - result['sell_fee']
# profit_percent
result['profit_percent'] = (result['sell_take'] - result['buy_spend']) / result['buy_spend']
# Absolute profit
result['profit_abs'] = result['sell_take'] - result['buy_spend']
return result
def _process_expectancy(self, results: DataFrame) -> Dict[str, Any]:
"""
This calculates WinRate, Required Risk Reward, Risk Reward and Expectancy of all pairs
The calulation will be done per pair and per strategy.
"""
# Removing pairs having less than min_trades_number
min_trades_number = self.edge_config.get('min_trade_number', 10)
results = results.groupby(['pair', 'stoploss']).filter(lambda x: len(x) > min_trades_number)
###################################
# Removing outliers (Only Pumps) from the dataset
# The method to detect outliers is to calculate standard deviation
# Then every value more than (standard deviation + 2*average) is out (pump)
#
# Removing Pumps
if self.edge_config.get('remove_pumps', False):
results = results.groupby(['pair', 'stoploss']).apply(
lambda x: x[x['profit_abs'] < 2 * x['profit_abs'].std() + x['profit_abs'].mean()])
##########################################################################
# Removing trades having a duration more than X minutes (set in config)
max_trade_duration = self.edge_config.get('max_trade_duration_minute', 1440)
results = results[results.trade_duration < max_trade_duration]
#######################################################################
if results.empty:
return {}
groupby_aggregator = {
'profit_abs': [
('nb_trades', 'count'), # number of all trades
('profit_sum', lambda x: x[x > 0].sum()), # cumulative profit of all winning trades
('loss_sum', lambda x: abs(x[x < 0].sum())), # cumulative loss of all losing trades
('nb_win_trades', lambda x: x[x > 0].count()) # number of winning trades
],
'trade_duration': [('avg_trade_duration', 'mean')]
}
# Group by (pair and stoploss) by applying above aggregator
df = results.groupby(['pair', 'stoploss'])['profit_abs', 'trade_duration'].agg(
groupby_aggregator).reset_index(col_level=1)
# Dropping level 0 as we don't need it
df.columns = df.columns.droplevel(0)
# Calculating number of losing trades, average win and average loss
df['nb_loss_trades'] = df['nb_trades'] - df['nb_win_trades']
df['average_win'] = df['profit_sum'] / df['nb_win_trades']
df['average_loss'] = df['loss_sum'] / df['nb_loss_trades']
# Win rate = number of profitable trades / number of trades
df['winrate'] = df['nb_win_trades'] / df['nb_trades']
# risk_reward_ratio = average win / average loss
df['risk_reward_ratio'] = df['average_win'] / df['average_loss']
# required_risk_reward = (1 / winrate) - 1
df['required_risk_reward'] = (1 / df['winrate']) - 1
# expectancy = (risk_reward_ratio * winrate) - (lossrate)
df['expectancy'] = (df['risk_reward_ratio'] * df['winrate']) - (1 - df['winrate'])
# sort by expectancy and stoploss
df = df.sort_values(by=['expectancy', 'stoploss'], ascending=False).groupby(
'pair').first().sort_values(by=['expectancy'], ascending=False).reset_index()
final = {}
for x in df.itertuples():
final[x.pair] = PairInfo(
x.stoploss,
x.winrate,
x.risk_reward_ratio,
x.required_risk_reward,
x.expectancy,
x.nb_trades,
x.avg_trade_duration
)
# Returning a list of pairs in order of "expectancy"
return final
def _find_trades_for_stoploss_range(self, ticker_data, pair, stoploss_range):
buy_column = ticker_data['buy'].values
sell_column = ticker_data['sell'].values
date_column = ticker_data['date'].values
ohlc_columns = ticker_data[['open', 'high', 'low', 'close']].values
result: list = []
for stoploss in stoploss_range:
result += self._detect_next_stop_or_sell_point(
buy_column, sell_column, date_column, ohlc_columns, round(stoploss, 6), pair
)
return result
def _detect_next_stop_or_sell_point(self, buy_column, sell_column, date_column,
ohlc_columns, stoploss, pair, start_point=0):
"""
Iterate through ohlc_columns recursively in order to find the next trade
Next trade opens from the first buy signal noticed to
The sell or stoploss signal after it.
It then calls itself cutting OHLC, buy_column, sell_colum and date_column
Cut from (the exit trade index) + 1
Author: https://github.com/mishaker
"""
result: list = []
open_trade_index = utf1st.find_1st(buy_column, 1, utf1st.cmp_equal)
# return empty if we don't find trade entry (i.e. buy==1) or
# we find a buy but at the of array
if open_trade_index == -1 or open_trade_index == len(buy_column) - 1:
return []
else:
open_trade_index += 1 # when a buy signal is seen,
# trade opens in reality on the next candle
stop_price_percentage = stoploss + 1
open_price = ohlc_columns[open_trade_index, 0]
stop_price = (open_price * stop_price_percentage)
# Searching for the index where stoploss is hit
stop_index = utf1st.find_1st(
ohlc_columns[open_trade_index:, 2], stop_price, utf1st.cmp_smaller)
# If we don't find it then we assume stop_index will be far in future (infinite number)
if stop_index == -1:
stop_index = float('inf')
# Searching for the index where sell is hit
sell_index = utf1st.find_1st(sell_column[open_trade_index:], 1, utf1st.cmp_equal)
# If we don't find it then we assume sell_index will be far in future (infinite number)
if sell_index == -1:
sell_index = float('inf')
# Check if we don't find any stop or sell point (in that case trade remains open)
# It is not interesting for Edge to consider it so we simply ignore the trade
# And stop iterating there is no more entry
if stop_index == sell_index == float('inf'):
return []
if stop_index <= sell_index:
exit_index = open_trade_index + stop_index
exit_type = SellType.STOP_LOSS
exit_price = stop_price
elif stop_index > sell_index:
# if exit is SELL then we exit at the next candle
exit_index = open_trade_index + sell_index + 1
# check if we have the next candle
if len(ohlc_columns) - 1 < exit_index:
return []
exit_type = SellType.SELL_SIGNAL
exit_price = ohlc_columns[exit_index, 0]
trade = {'pair': pair,
'stoploss': stoploss,
'profit_percent': '',
'profit_abs': '',
'open_time': date_column[open_trade_index],
'close_time': date_column[exit_index],
'open_index': start_point + open_trade_index,
'close_index': start_point + exit_index,
'trade_duration': '',
'open_rate': round(open_price, 15),
'close_rate': round(exit_price, 15),
'exit_type': exit_type
}
result.append(trade)
# Calling again the same function recursively but giving
# it a view of exit_index till the end of array
return result + self._detect_next_stop_or_sell_point(
buy_column[exit_index:],
sell_column[exit_index:],
date_column[exit_index:],
ohlc_columns[exit_index:],
stoploss,
pair,
(start_point + exit_index)
)
+664 -126
View File
@@ -1,175 +1,713 @@
import enum
# pragma pylint: disable=W0603
""" Cryptocurrency Exchanges support """
import logging
import inspect
from random import randint
from typing import List, Dict, Any, Optional
from typing import List, Dict, Tuple, Any, Optional
from datetime import datetime
from math import floor, ceil
import arrow
from cachetools import cached, TTLCache
import asyncio
import ccxt
import ccxt.async_support as ccxt_async
from pandas import DataFrame
from freqtrade.exchange.bittrex import Bittrex
from freqtrade.exchange.interface import Exchange
from freqtrade import constants, OperationalException, DependencyException, TemporaryError
from freqtrade.data.converter import parse_ticker_dataframe
logger = logging.getLogger(__name__)
# Current selected exchange
_API: Exchange = None
_CONF: dict = {}
# Holds all open sell orders for dry_run
_DRY_RUN_OPEN_ORDERS: Dict[str, Any] = {}
API_RETRY_COUNT = 4
class Exchanges(enum.Enum):
"""
Maps supported exchange names to correspondent classes.
"""
BITTREX = Bittrex
# Urls to exchange markets, insert quote and base with .format()
_EXCHANGE_URLS = {
ccxt.bittrex.__name__: '/Market/Index?MarketName={quote}-{base}',
ccxt.binance.__name__: '/tradeDetail.html?symbol={base}_{quote}'
}
def init(config: dict) -> None:
"""
Initializes this module with the given config,
it does basic validation whether the specified
exchange and pairs are valid.
:param config: config to use
:return: None
"""
global _CONF, _API
_CONF.update(config)
if config['dry_run']:
logger.info('Instance is running with dry_run enabled')
exchange_config = config['exchange']
# Find matching class for the given exchange name
name = exchange_config['name']
try:
exchange_class = Exchanges[name.upper()].value
except KeyError:
raise RuntimeError('Exchange {} is not supported'.format(name))
_API = exchange_class(exchange_config)
# Check if all pairs are available
validate_pairs(config['exchange']['pair_whitelist'])
def retrier_async(f):
async def wrapper(*args, **kwargs):
count = kwargs.pop('count', API_RETRY_COUNT)
try:
return await f(*args, **kwargs)
except (TemporaryError, DependencyException) as ex:
logger.warning('%s() returned exception: "%s"', f.__name__, ex)
if count > 0:
count -= 1
kwargs.update({'count': count})
logger.warning('retrying %s() still for %s times', f.__name__, count)
return await wrapper(*args, **kwargs)
else:
logger.warning('Giving up retrying: %s()', f.__name__)
raise ex
return wrapper
def validate_pairs(pairs: List[str]) -> None:
"""
Checks if all given pairs are tradable on the current exchange.
Raises RuntimeError if one pair is not available.
:param pairs: list of pairs
:return: None
"""
markets = _API.get_markets()
stake_cur = _CONF['stake_currency']
for pair in pairs:
if not pair.startswith(stake_cur):
raise RuntimeError(
'Pair {} not compatible with stake_currency: {}'.format(pair, stake_cur)
)
if pair not in markets:
raise RuntimeError('Pair {} is not available at {}'.format(pair, _API.name.lower()))
def retrier(f):
def wrapper(*args, **kwargs):
count = kwargs.pop('count', API_RETRY_COUNT)
try:
return f(*args, **kwargs)
except (TemporaryError, DependencyException) as ex:
logger.warning('%s() returned exception: "%s"', f.__name__, ex)
if count > 0:
count -= 1
kwargs.update({'count': count})
logger.warning('retrying %s() still for %s times', f.__name__, count)
return wrapper(*args, **kwargs)
else:
logger.warning('Giving up retrying: %s()', f.__name__)
raise ex
return wrapper
def buy(pair: str, rate: float, amount: float) -> str:
if _CONF['dry_run']:
global _DRY_RUN_OPEN_ORDERS
order_id = 'dry_run_buy_{}'.format(randint(0, 1e6))
_DRY_RUN_OPEN_ORDERS[order_id] = {
'pair': pair,
'rate': rate,
'amount': amount,
'type': 'LIMIT_BUY',
'remaining': 0.0,
'opened': arrow.utcnow().datetime,
'closed': arrow.utcnow().datetime,
class Exchange(object):
_conf: Dict = {}
def __init__(self, config: dict) -> None:
"""
Initializes this module with the given config,
it does basic validation whether the specified
exchange and pairs are valid.
:return: None
"""
self._conf.update(config)
self._cached_ticker: Dict[str, Any] = {}
# Holds last candle refreshed time of each pair
self._pairs_last_refresh_time: Dict[str, int] = {}
# Holds candles
self._klines: Dict[str, DataFrame] = {}
# Holds all open sell orders for dry_run
self._dry_run_open_orders: Dict[str, Any] = {}
if config['dry_run']:
logger.info('Instance is running with dry_run enabled')
exchange_config = config['exchange']
self._api: ccxt.Exchange = self._init_ccxt(
exchange_config, ccxt_kwargs=exchange_config.get('ccxt_config'))
self._api_async: ccxt_async.Exchange = self._init_ccxt(
exchange_config, ccxt_async, ccxt_kwargs=exchange_config.get('ccxt_async_config'))
logger.info('Using Exchange "%s"', self.name)
self.markets = self._load_markets()
# Check if all pairs are available
self.validate_pairs(config['exchange']['pair_whitelist'])
self.validate_ordertypes(config.get('order_types', {}))
self.validate_order_time_in_force(config.get('order_time_in_force', {}))
if config.get('ticker_interval'):
# Check if timeframe is available
self.validate_timeframes(config['ticker_interval'])
def __del__(self):
"""
Destructor - clean up async stuff
"""
logger.debug("Exchange object destroyed, closing async loop")
if self._api_async and inspect.iscoroutinefunction(self._api_async.close):
asyncio.get_event_loop().run_until_complete(self._api_async.close())
def _init_ccxt(self, exchange_config: dict, ccxt_module=ccxt,
ccxt_kwargs: dict = None) -> ccxt.Exchange:
"""
Initialize ccxt with given config and return valid
ccxt instance.
"""
# Find matching class for the given exchange name
name = exchange_config['name']
if name not in ccxt_module.exchanges:
raise OperationalException(f'Exchange {name} is not supported')
ex_config = {
'apiKey': exchange_config.get('key'),
'secret': exchange_config.get('secret'),
'password': exchange_config.get('password'),
'uid': exchange_config.get('uid', ''),
'enableRateLimit': exchange_config.get('ccxt_rate_limit', True)
}
return order_id
if ccxt_kwargs:
logger.info('Applying additional ccxt config: %s', ccxt_kwargs)
ex_config.update(ccxt_kwargs)
try:
return _API.buy(pair, rate, amount)
api = getattr(ccxt_module, name.lower())(ex_config)
except (KeyError, AttributeError):
raise OperationalException(f'Exchange {name} is not supported')
self.set_sandbox(api, exchange_config, name)
def sell(pair: str, rate: float, amount: float) -> str:
if _CONF['dry_run']:
global _DRY_RUN_OPEN_ORDERS
order_id = 'dry_run_sell_{}'.format(randint(0, 1e6))
_DRY_RUN_OPEN_ORDERS[order_id] = {
'pair': pair,
'rate': rate,
'amount': amount,
'type': 'LIMIT_SELL',
'remaining': 0.0,
'opened': arrow.utcnow().datetime,
'closed': arrow.utcnow().datetime,
}
return order_id
return api
return _API.sell(pair, rate, amount)
@property
def name(self) -> str:
"""exchange Name (from ccxt)"""
return self._api.name
@property
def id(self) -> str:
"""exchange ccxt id"""
return self._api.id
def get_balance(currency: str) -> float:
if _CONF['dry_run']:
return 999.9
def klines(self, pair: str, copy=True) -> DataFrame:
if pair in self._klines:
return self._klines[pair].copy() if copy else self._klines[pair]
else:
return None
return _API.get_balance(currency)
def set_sandbox(self, api, exchange_config: dict, name: str):
if exchange_config.get('sandbox'):
if api.urls.get('test'):
api.urls['api'] = api.urls['test']
logger.info("Enabled Sandbox API on %s", name)
else:
logger.warning(name, "No Sandbox URL in CCXT, exiting. "
"Please check your config.json")
raise OperationalException(f'Exchange {name} does not provide a sandbox api')
def _load_async_markets(self) -> None:
try:
if self._api_async:
asyncio.get_event_loop().run_until_complete(self._api_async.load_markets())
def get_balances():
if _CONF['dry_run']:
return []
except ccxt.BaseError as e:
logger.warning('Could not load async markets. Reason: %s', e)
return
return _API.get_balances()
def _load_markets(self) -> Dict[str, Any]:
""" Initialize markets both sync and async """
try:
markets = self._api.load_markets()
self._load_async_markets()
return markets
except ccxt.BaseError as e:
logger.warning('Unable to initialize markets. Reason: %s', e)
return {}
def validate_pairs(self, pairs: List[str]) -> None:
"""
Checks if all given pairs are tradable on the current exchange.
Raises OperationalException if one pair is not available.
:param pairs: list of pairs
:return: None
"""
def get_ticker(pair: str) -> dict:
return _API.get_ticker(pair)
if not self.markets:
logger.warning('Unable to validate pairs (assuming they are correct).')
# return
stake_cur = self._conf['stake_currency']
for pair in pairs:
# Note: ccxt has BaseCurrency/QuoteCurrency format for pairs
# TODO: add a support for having coins in BTC/USDT format
if not pair.endswith(stake_cur):
raise OperationalException(
f'Pair {pair} not compatible with stake_currency: {stake_cur}')
if self.markets and pair not in self.markets:
raise OperationalException(
f'Pair {pair} is not available at {self.name}'
f'Please remove {pair} from your whitelist.')
@cached(TTLCache(maxsize=100, ttl=30))
def get_ticker_history(pair: str, tick_interval: Optional[int] = 5) -> List[Dict]:
return _API.get_ticker_history(pair, tick_interval)
def validate_timeframes(self, timeframe: List[str]) -> None:
"""
Checks if ticker interval from config is a supported timeframe on the exchange
"""
timeframes = self._api.timeframes
if timeframe not in timeframes:
raise OperationalException(
f'Invalid ticker {timeframe}, this Exchange supports {timeframes}')
def validate_ordertypes(self, order_types: Dict) -> None:
"""
Checks if order-types configured in strategy/config are supported
"""
if any(v == 'market' for k, v in order_types.items()):
if not self.exchange_has('createMarketOrder'):
raise OperationalException(
f'Exchange {self.name} does not support market orders.')
def cancel_order(order_id: str) -> None:
if _CONF['dry_run']:
return
if order_types.get('stoploss_on_exchange'):
if self.name is not 'Binance':
raise OperationalException(
'On exchange stoploss is not supported for %s.' % self.name
)
return _API.cancel_order(order_id)
def validate_order_time_in_force(self, order_time_in_force: Dict) -> None:
"""
Checks if order time in force configured in strategy/config are supported
"""
if any(v != 'gtc' for k, v in order_time_in_force.items()):
if self.name is not 'Binance':
raise OperationalException(
f'Time in force policies are not supporetd for {self.name} yet.')
def exchange_has(self, endpoint: str) -> bool:
"""
Checks if exchange implements a specific API endpoint.
Wrapper around ccxt 'has' attribute
:param endpoint: Name of endpoint (e.g. 'fetchOHLCV', 'fetchTickers')
:return: bool
"""
return endpoint in self._api.has and self._api.has[endpoint]
def get_order(order_id: str) -> Dict:
if _CONF['dry_run']:
order = _DRY_RUN_OPEN_ORDERS[order_id]
order.update({
'id': order_id
})
return order
def symbol_amount_prec(self, pair, amount: float):
'''
Returns the amount to buy or sell to a precision the Exchange accepts
Rounded down
'''
if self._api.markets[pair]['precision']['amount']:
symbol_prec = self._api.markets[pair]['precision']['amount']
big_amount = amount * pow(10, symbol_prec)
amount = floor(big_amount) / pow(10, symbol_prec)
return amount
return _API.get_order(order_id)
def symbol_price_prec(self, pair, price: float):
'''
Returns the price buying or selling with to the precision the Exchange accepts
Rounds up
'''
if self._api.markets[pair]['precision']['price']:
symbol_prec = self._api.markets[pair]['precision']['price']
big_price = price * pow(10, symbol_prec)
price = ceil(big_price) / pow(10, symbol_prec)
return price
def buy(self, pair: str, ordertype: str, amount: float,
rate: float, time_in_force) -> Dict:
if self._conf['dry_run']:
order_id = f'dry_run_buy_{randint(0, 10**6)}'
self._dry_run_open_orders[order_id] = {
'pair': pair,
'price': rate,
'amount': amount,
'type': ordertype,
'side': 'buy',
'remaining': 0.0,
'datetime': arrow.utcnow().isoformat(),
'status': 'closed',
'fee': None
}
return {'id': order_id}
def get_pair_detail_url(pair: str) -> str:
return _API.get_pair_detail_url(pair)
try:
# Set the precision for amount and price(rate) as accepted by the exchange
amount = self.symbol_amount_prec(pair, amount)
rate = self.symbol_price_prec(pair, rate) if ordertype != 'market' else None
if time_in_force == 'gtc':
return self._api.create_order(pair, ordertype, 'buy', amount, rate)
else:
return self._api.create_order(pair, ordertype, 'buy',
amount, rate, {'timeInForce': time_in_force})
def get_markets() -> List[str]:
return _API.get_markets()
except ccxt.InsufficientFunds as e:
raise DependencyException(
f'Insufficient funds to create limit buy order on market {pair}.'
f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
f'Message: {e}')
except ccxt.InvalidOrder as e:
raise DependencyException(
f'Could not create limit buy order on market {pair}.'
f'Tried to buy amount {amount} at rate {rate} (total {rate*amount}).'
f'Message: {e}')
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not place buy order due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
def sell(self, pair: str, ordertype: str, amount: float,
rate: float, time_in_force='gtc') -> Dict:
if self._conf['dry_run']:
order_id = f'dry_run_sell_{randint(0, 10**6)}'
self._dry_run_open_orders[order_id] = {
'pair': pair,
'price': rate,
'amount': amount,
'type': ordertype,
'side': 'sell',
'remaining': 0.0,
'datetime': arrow.utcnow().isoformat(),
'status': 'closed'
}
return {'id': order_id}
def get_market_summaries() -> List[Dict]:
return _API.get_market_summaries()
try:
# Set the precision for amount and price(rate) as accepted by the exchange
amount = self.symbol_amount_prec(pair, amount)
rate = self.symbol_price_prec(pair, rate) if ordertype != 'market' else None
if time_in_force == 'gtc':
return self._api.create_order(pair, ordertype, 'sell', amount, rate)
else:
return self._api.create_order(pair, ordertype, 'sell',
amount, rate, {'timeInForce': time_in_force})
def get_name() -> str:
return _API.name
except ccxt.InsufficientFunds as e:
raise DependencyException(
f'Insufficient funds to create limit sell order on market {pair}.'
f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
f'Message: {e}')
except ccxt.InvalidOrder as e:
raise DependencyException(
f'Could not create limit sell order on market {pair}.'
f'Tried to sell amount {amount} at rate {rate} (total {rate*amount}).'
f'Message: {e}')
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not place sell order due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
def stoploss_limit(self, pair: str, amount: float, stop_price: float, rate: float) -> Dict:
"""
creates a stoploss limit order.
NOTICE: it is not supported by all exchanges. only binance is tested for now.
"""
def get_fee() -> float:
return _API.fee
# Set the precision for amount and price(rate) as accepted by the exchange
amount = self.symbol_amount_prec(pair, amount)
rate = self.symbol_price_prec(pair, rate)
stop_price = self.symbol_price_prec(pair, stop_price)
# Ensure rate is less than stop price
if stop_price <= rate:
raise OperationalException(
'In stoploss limit order, stop price should be more than limit price')
def get_wallet_health() -> List[Dict]:
return _API.get_wallet_health()
if self._conf['dry_run']:
order_id = f'dry_run_buy_{randint(0, 10**6)}'
self._dry_run_open_orders[order_id] = {
'info': {},
'id': order_id,
'pair': pair,
'price': stop_price,
'amount': amount,
'type': 'stop_loss_limit',
'side': 'sell',
'remaining': amount,
'datetime': arrow.utcnow().isoformat(),
'status': 'open',
'fee': None
}
return self._dry_run_open_orders[order_id]
try:
return self._api.create_order(pair, 'stop_loss_limit', 'sell',
amount, rate, {'stopPrice': stop_price})
except ccxt.InsufficientFunds as e:
raise DependencyException(
f'Insufficient funds to place stoploss limit order on market {pair}. '
f'Tried to put a stoploss amount {amount} with '
f'stop {stop_price} and limit {rate} (total {rate*amount}).'
f'Message: {e}')
except ccxt.InvalidOrder as e:
raise DependencyException(
f'Could not place stoploss limit order on market {pair}.'
f'Tried to place stoploss amount {amount} with '
f'stop {stop_price} and limit {rate} (total {rate*amount}).'
f'Message: {e}')
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not place stoploss limit order due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
@retrier
def get_balance(self, currency: str) -> float:
if self._conf['dry_run']:
return 999.9
# ccxt exception is already handled by get_balances
balances = self.get_balances()
balance = balances.get(currency)
if balance is None:
raise TemporaryError(
f'Could not get {currency} balance due to malformed exchange response: {balances}')
return balance['free']
@retrier
def get_balances(self) -> dict:
if self._conf['dry_run']:
return {}
try:
balances = self._api.fetch_balance()
# Remove additional info from ccxt results
balances.pop("info", None)
balances.pop("free", None)
balances.pop("total", None)
balances.pop("used", None)
return balances
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not get balance due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
@retrier
def get_tickers(self) -> Dict:
try:
return self._api.fetch_tickers()
except ccxt.NotSupported as e:
raise OperationalException(
f'Exchange {self._api.name} does not support fetching tickers in batch.'
f'Message: {e}')
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not load tickers due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
@retrier
def get_ticker(self, pair: str, refresh: Optional[bool] = True) -> dict:
if refresh or pair not in self._cached_ticker.keys():
try:
if pair not in self._api.markets:
raise DependencyException(f"Pair {pair} not available")
data = self._api.fetch_ticker(pair)
try:
self._cached_ticker[pair] = {
'bid': float(data['bid']),
'ask': float(data['ask']),
}
except KeyError:
logger.debug("Could not cache ticker data for %s", pair)
return data
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not load ticker due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
else:
logger.info("returning cached ticker-data for %s", pair)
return self._cached_ticker[pair]
def get_history(self, pair: str, tick_interval: str,
since_ms: int) -> List:
"""
Gets candle history using asyncio and returns the list of candles.
Handles all async doing.
"""
return asyncio.get_event_loop().run_until_complete(
self._async_get_history(pair=pair, tick_interval=tick_interval,
since_ms=since_ms))
async def _async_get_history(self, pair: str,
tick_interval: str,
since_ms: int) -> List:
# Assume exchange returns 500 candles
_LIMIT = 500
one_call = constants.TICKER_INTERVAL_MINUTES[tick_interval] * 60 * _LIMIT * 1000
logger.debug("one_call: %s", one_call)
input_coroutines = [self._async_get_candle_history(
pair, tick_interval, since) for since in
range(since_ms, arrow.utcnow().timestamp * 1000, one_call)]
tickers = await asyncio.gather(*input_coroutines, return_exceptions=True)
# Combine tickers
data: List = []
for p, ticker in tickers:
if p == pair:
data.extend(ticker)
# Sort data again after extending the result - above calls return in "async order" order
data = sorted(data, key=lambda x: x[0])
logger.info("downloaded %s with length %s.", pair, len(data))
return data
def refresh_tickers(self, pair_list: List[str], ticker_interval: str) -> None:
"""
Refresh tickers asyncronously and set `_klines` of this object with the result
"""
logger.debug("Refreshing klines for %d pairs", len(pair_list))
asyncio.get_event_loop().run_until_complete(
self.async_get_candles_history(pair_list, ticker_interval))
async def async_get_candles_history(self, pairs: List[str],
tick_interval: str) -> List[Tuple[str, List]]:
"""Download ohlcv history for pair-list asyncronously """
# Calculating ticker interval in second
interval_in_sec = constants.TICKER_INTERVAL_MINUTES[tick_interval] * 60
input_coroutines = []
# Gather corotines to run
for pair in pairs:
if not (self._pairs_last_refresh_time.get(pair, 0) + interval_in_sec >=
arrow.utcnow().timestamp and pair in self._klines):
input_coroutines.append(self._async_get_candle_history(pair, tick_interval))
else:
logger.debug("Using cached klines data for %s ...", pair)
tickers = await asyncio.gather(*input_coroutines, return_exceptions=True)
# handle caching
for pair, ticks in tickers:
# keeping last candle time as last refreshed time of the pair
if ticks:
self._pairs_last_refresh_time[pair] = ticks[-1][0] // 1000
# keeping parsed dataframe in cache
self._klines[pair] = parse_ticker_dataframe(ticks)
return tickers
@retrier_async
async def _async_get_candle_history(self, pair: str, tick_interval: str,
since_ms: Optional[int] = None) -> Tuple[str, List]:
try:
# fetch ohlcv asynchronously
logger.debug("fetching %s since %s ...", pair, since_ms)
data = await self._api_async.fetch_ohlcv(pair, timeframe=tick_interval,
since=since_ms)
# Because some exchange sort Tickers ASC and other DESC.
# Ex: Bittrex returns a list of tickers ASC (oldest first, newest last)
# when GDAX returns a list of tickers DESC (newest first, oldest last)
# Only sort if necessary to save computing time
if data and data[0][0] > data[-1][0]:
data = sorted(data, key=lambda x: x[0])
logger.debug("done fetching %s ...", pair)
return pair, data
except ccxt.NotSupported as e:
raise OperationalException(
f'Exchange {self._api.name} does not support fetching historical candlestick data.'
f'Message: {e}')
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not load ticker history due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(f'Could not fetch ticker data. Msg: {e}')
@retrier
def cancel_order(self, order_id: str, pair: str) -> None:
if self._conf['dry_run']:
return
try:
return self._api.cancel_order(order_id, pair)
except ccxt.InvalidOrder as e:
raise DependencyException(
f'Could not cancel order. Message: {e}')
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not cancel order due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
@retrier
def get_order(self, order_id: str, pair: str) -> Dict:
if self._conf['dry_run']:
order = self._dry_run_open_orders[order_id]
order.update({
'id': order_id
})
return order
try:
return self._api.fetch_order(order_id, pair)
except ccxt.InvalidOrder as e:
raise DependencyException(
f'Could not get order. Message: {e}')
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not get order due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
@retrier
def get_order_book(self, pair: str, limit: int = 100) -> dict:
"""
get order book level 2 from exchange
Notes:
20180619: bittrex doesnt support limits -.-
20180619: binance support limits but only on specific range
"""
try:
if self._api.name == 'Binance':
limit_range = [5, 10, 20, 50, 100, 500, 1000]
# get next-higher step in the limit_range list
limit = min(list(filter(lambda x: limit <= x, limit_range)))
# above script works like loop below (but with slightly better performance):
# for limitx in limit_range:
# if limit <= limitx:
# limit = limitx
# break
return self._api.fetch_l2_order_book(pair, limit)
except ccxt.NotSupported as e:
raise OperationalException(
f'Exchange {self._api.name} does not support fetching order book.'
f'Message: {e}')
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not get order book due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
@retrier
def get_trades_for_order(self, order_id: str, pair: str, since: datetime) -> List:
if self._conf['dry_run']:
return []
if not self.exchange_has('fetchMyTrades'):
return []
try:
# Allow 5s offset to catch slight time offsets (discovered in #1185)
my_trades = self._api.fetch_my_trades(pair, since.timestamp() - 5)
matched_trades = [trade for trade in my_trades if trade['order'] == order_id]
return matched_trades
except ccxt.NetworkError as e:
raise TemporaryError(
f'Could not get trades due to networking error. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
def get_pair_detail_url(self, pair: str) -> str:
try:
url_base = self._api.urls.get('www')
base, quote = pair.split('/')
return url_base + _EXCHANGE_URLS[self._api.id].format(base=base, quote=quote)
except KeyError:
logger.warning('Could not get exchange url for %s', self.name)
return ""
@retrier
def get_markets(self) -> List[dict]:
try:
return self._api.fetch_markets()
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not load markets due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
@retrier
def get_fee(self, symbol='ETH/BTC', type='', side='', amount=1,
price=1, taker_or_maker='maker') -> float:
try:
# validate that markets are loaded before trying to get fee
if self._api.markets is None or len(self._api.markets) == 0:
self._api.load_markets()
return self._api.calculate_fee(symbol=symbol, type=type, side=side, amount=amount,
price=price, takerOrMaker=taker_or_maker)['rate']
except (ccxt.NetworkError, ccxt.ExchangeError) as e:
raise TemporaryError(
f'Could not get fee info due to {e.__class__.__name__}. Message: {e}')
except ccxt.BaseError as e:
raise OperationalException(e)
-171
View File
@@ -1,171 +0,0 @@
import logging
from typing import List, Dict
from bittrex.bittrex import Bittrex as _Bittrex, API_V2_0, API_V1_1
from freqtrade.exchange.interface import Exchange
logger = logging.getLogger(__name__)
_API: _Bittrex = None
_API_V2: _Bittrex = None
_EXCHANGE_CONF: dict = {}
class Bittrex(Exchange):
"""
Bittrex API wrapper.
"""
# Base URL and API endpoints
BASE_URL: str = 'https://www.bittrex.com'
PAIR_DETAIL_METHOD: str = BASE_URL + '/Market/Index'
def __init__(self, config: dict) -> None:
global _API, _API_V2, _EXCHANGE_CONF
_EXCHANGE_CONF.update(config)
_API = _Bittrex(
api_key=_EXCHANGE_CONF['key'],
api_secret=_EXCHANGE_CONF['secret'],
calls_per_second=1,
api_version=API_V1_1,
)
_API_V2 = _Bittrex(
api_key=_EXCHANGE_CONF['key'],
api_secret=_EXCHANGE_CONF['secret'],
calls_per_second=1,
api_version=API_V2_0,
)
@property
def fee(self) -> float:
# See https://bittrex.com/fees
return 0.0025
def buy(self, pair: str, rate: float, amount: float) -> str:
data = _API.buy_limit(pair.replace('_', '-'), amount, rate)
if not data['success']:
raise RuntimeError('{message} params=({pair}, {rate}, {amount})'.format(
message=data['message'],
pair=pair,
rate=rate,
amount=amount))
return data['result']['uuid']
def sell(self, pair: str, rate: float, amount: float) -> str:
data = _API.sell_limit(pair.replace('_', '-'), amount, rate)
if not data['success']:
raise RuntimeError('{message} params=({pair}, {rate}, {amount})'.format(
message=data['message'],
pair=pair,
rate=rate,
amount=amount))
return data['result']['uuid']
def get_balance(self, currency: str) -> float:
data = _API.get_balance(currency)
if not data['success']:
raise RuntimeError('{message} params=({currency})'.format(
message=data['message'],
currency=currency))
return float(data['result']['Balance'] or 0.0)
def get_balances(self):
data = _API.get_balances()
if not data['success']:
raise RuntimeError('{message}'.format(message=data['message']))
return data['result']
def get_ticker(self, pair: str) -> dict:
data = _API.get_ticker(pair.replace('_', '-'))
if not data['success']:
raise RuntimeError('{message} params=({pair})'.format(
message=data['message'],
pair=pair))
if not data['result']['Bid'] or not data['result']['Ask'] or not data['result']['Last']:
raise RuntimeError('{message} params=({pair})'.format(
message=data['message'],
pair=pair))
return {
'bid': float(data['result']['Bid']),
'ask': float(data['result']['Ask']),
'last': float(data['result']['Last']),
}
def get_ticker_history(self, pair: str, tick_interval: int) -> List[Dict]:
if tick_interval == 1:
interval = 'oneMin'
elif tick_interval == 5:
interval = 'fiveMin'
else:
raise ValueError('Cannot parse tick_interval: {}'.format(tick_interval))
data = _API_V2.get_candles(pair.replace('_', '-'), interval)
# These sanity check are necessary because bittrex cannot keep their API stable.
if not data.get('result'):
return []
for prop in ['C', 'V', 'O', 'H', 'L', 'T']:
for tick in data['result']:
if prop not in tick.keys():
logger.warning('Required property %s not present in response', prop)
return []
if not data['success']:
raise RuntimeError('{message} params=({pair})'.format(
message=data['message'],
pair=pair))
return data['result']
def get_order(self, order_id: str) -> Dict:
data = _API.get_order(order_id)
if not data['success']:
raise RuntimeError('{message} params=({order_id})'.format(
message=data['message'],
order_id=order_id))
data = data['result']
return {
'id': data['OrderUuid'],
'type': data['Type'],
'pair': data['Exchange'].replace('-', '_'),
'opened': data['Opened'],
'rate': data['PricePerUnit'],
'amount': data['Quantity'],
'remaining': data['QuantityRemaining'],
'closed': data['Closed'],
}
def cancel_order(self, order_id: str) -> None:
data = _API.cancel(order_id)
if not data['success']:
raise RuntimeError('{message} params=({order_id})'.format(
message=data['message'],
order_id=order_id))
def get_pair_detail_url(self, pair: str) -> str:
return self.PAIR_DETAIL_METHOD + '?MarketName={}'.format(pair.replace('_', '-'))
def get_markets(self) -> List[str]:
data = _API.get_markets()
if not data['success']:
raise RuntimeError('{message}'.format(message=data['message']))
return [m['MarketName'].replace('-', '_') for m in data['result']]
def get_market_summaries(self) -> List[Dict]:
data = _API.get_market_summaries()
if not data['success']:
raise RuntimeError('{message}'.format(message=data['message']))
return data['result']
def get_wallet_health(self) -> List[Dict]:
data = _API_V2.get_wallet_health()
if not data['success']:
raise RuntimeError('{message}'.format(message=data['message']))
return [{
'Currency': entry['Health']['Currency'],
'IsActive': entry['Health']['IsActive'],
'LastChecked': entry['Health']['LastChecked'],
'Notice': entry['Currency'].get('Notice'),
} for entry in data['result']]
-171
View File
@@ -1,171 +0,0 @@
from abc import ABC, abstractmethod
from typing import List, Dict
class Exchange(ABC):
@property
def name(self) -> str:
"""
Name of the exchange.
:return: str representation of the class name
"""
return self.__class__.__name__
@property
def fee(self) -> float:
"""
Fee for placing an order
:return: percentage in float
"""
@abstractmethod
def buy(self, pair: str, rate: float, amount: float) -> str:
"""
Places a limit buy order.
:param pair: Pair as str, format: BTC_ETH
:param rate: Rate limit for order
:param amount: The amount to purchase
:return: order_id of the placed buy order
"""
@abstractmethod
def sell(self, pair: str, rate: float, amount: float) -> str:
"""
Places a limit sell order.
:param pair: Pair as str, format: BTC_ETH
:param rate: Rate limit for order
:param amount: The amount to sell
:return: order_id of the placed sell order
"""
@abstractmethod
def get_balance(self, currency: str) -> float:
"""
Gets account balance.
:param currency: Currency as str, format: BTC
:return: float
"""
@abstractmethod
def get_balances(self) -> List[dict]:
"""
Gets account balances across currencies
:return: List of dicts, format: [
{
'Currency': str,
'Balance': float,
'Available': float,
'Pending': float,
}
...
]
"""
@abstractmethod
def get_ticker(self, pair: str) -> dict:
"""
Gets ticker for given pair.
:param pair: Pair as str, format: BTC_ETC
:return: dict, format: {
'bid': float,
'ask': float,
'last': float
}
"""
@abstractmethod
def get_ticker_history(self, pair: str, tick_interval: int) -> List[Dict]:
"""
Gets ticker history for given pair.
:param pair: Pair as str, format: BTC_ETC
:param tick_interval: ticker interval in minutes
:return: list, format: [
{
'O': float, (Open)
'H': float, (High)
'L': float, (Low)
'C': float, (Close)
'V': float, (Volume)
'T': datetime, (Time)
'BV': float, (Base Volume)
},
...
]
"""
def get_order(self, order_id: str) -> Dict:
"""
Get order details for the given order_id.
:param order_id: ID as str
:return: dict, format: {
'id': str,
'type': str,
'pair': str,
'opened': str ISO 8601 datetime,
'closed': str ISO 8601 datetime,
'rate': float,
'amount': float,
'remaining': int
}
"""
@abstractmethod
def cancel_order(self, order_id: str) -> None:
"""
Cancels order for given order_id.
:param order_id: ID as str
:return: None
"""
@abstractmethod
def get_pair_detail_url(self, pair: str) -> str:
"""
Returns the market detail url for the given pair.
:param pair: Pair as str, format: BTC_ETC
:return: URL as str
"""
@abstractmethod
def get_markets(self) -> List[str]:
"""
Returns all available markets.
:return: List of all available pairs
"""
@abstractmethod
def get_market_summaries(self) -> List[Dict]:
"""
Returns a 24h market summary for all available markets
:return: list, format: [
{
'MarketName': str,
'High': float,
'Low': float,
'Volume': float,
'Last': float,
'TimeStamp': datetime,
'BaseVolume': float,
'Bid': float,
'Ask': float,
'OpenBuyOrders': int,
'OpenSellOrders': int,
'PrevDay': float,
'Created': datetime
},
...
]
"""
@abstractmethod
def get_wallet_health(self) -> List[Dict]:
"""
Returns a list of all wallet health information
:return: list, format: [
{
'Currency': str,
'IsActive': bool,
'LastChecked': str,
'Notice': str
},
...
"""
+832
View File
@@ -0,0 +1,832 @@
"""
Freqtrade is the main module of this bot. It contains the class Freqtrade()
"""
import copy
import logging
import time
import traceback
from datetime import datetime
from typing import Any, Callable, Dict, List, Optional
import arrow
from requests.exceptions import RequestException
from freqtrade import (DependencyException, OperationalException,
TemporaryError, __version__, constants, persistence)
from freqtrade.data.converter import order_book_to_dataframe
from freqtrade.edge import Edge
from freqtrade.exchange import Exchange
from freqtrade.persistence import Trade
from freqtrade.rpc import RPCManager, RPCMessageType
from freqtrade.resolvers import StrategyResolver, PairListResolver
from freqtrade.state import State
from freqtrade.strategy.interface import SellType, IStrategy
from freqtrade.wallets import Wallets
logger = logging.getLogger(__name__)
class FreqtradeBot(object):
"""
Freqtrade is the main class of the bot.
This is from here the bot start its logic.
"""
def __init__(self, config: Dict[str, Any])-> None:
"""
Init all variables and object the bot need to work
:param config: configuration dict, you can use the Configuration.get_config()
method to get the config dict.
"""
logger.info(
'Starting freqtrade %s',
__version__,
)
# Init bot states
self.state = State.STOPPED
# Init objects
self.config = config
self.strategy: IStrategy = StrategyResolver(self.config).strategy
self.rpc: RPCManager = RPCManager(self)
self.persistence = None
self.exchange = Exchange(self.config)
self.wallets = Wallets(self.exchange)
pairlistname = self.config.get('pairlist', {}).get('method', 'StaticPairList')
self.pairlists = PairListResolver(pairlistname, self, self.config).pairlist
# Initializing Edge only if enabled
self.edge = Edge(self.config, self.exchange, self.strategy) if \
self.config.get('edge', {}).get('enabled', False) else None
self.active_pair_whitelist: List[str] = self.config['exchange']['pair_whitelist']
self._init_modules()
def _init_modules(self) -> None:
"""
Initializes all modules and updates the config
:return: None
"""
# Initialize all modules
persistence.init(self.config)
# Set initial application state
initial_state = self.config.get('initial_state')
if initial_state:
self.state = State[initial_state.upper()]
else:
self.state = State.STOPPED
def cleanup(self) -> None:
"""
Cleanup pending resources on an already stopped bot
:return: None
"""
logger.info('Cleaning up modules ...')
self.rpc.cleanup()
persistence.cleanup()
def worker(self, old_state: State = None) -> State:
"""
Trading routine that must be run at each loop
:param old_state: the previous service state from the previous call
:return: current service state
"""
# Log state transition
state = self.state
if state != old_state:
self.rpc.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': f'{state.name.lower()}'
})
logger.info('Changing state to: %s', state.name)
if state == State.RUNNING:
self.rpc.startup_messages(self.config, self.pairlists)
if state == State.STOPPED:
time.sleep(1)
elif state == State.RUNNING:
min_secs = self.config.get('internals', {}).get(
'process_throttle_secs',
constants.PROCESS_THROTTLE_SECS
)
self._throttle(func=self._process,
min_secs=min_secs)
return state
def _throttle(self, func: Callable[..., Any], min_secs: float, *args, **kwargs) -> Any:
"""
Throttles the given callable that it
takes at least `min_secs` to finish execution.
:param func: Any callable
:param min_secs: minimum execution time in seconds
:return: Any
"""
start = time.time()
result = func(*args, **kwargs)
end = time.time()
duration = max(min_secs - (end - start), 0.0)
logger.debug('Throttling %s for %.2f seconds', func.__name__, duration)
time.sleep(duration)
return result
def _process(self) -> bool:
"""
Queries the persistence layer for open trades and handles them,
otherwise a new trade is created.
:return: True if one or more trades has been created or closed, False otherwise
"""
state_changed = False
try:
# Refresh whitelist
self.pairlists.refresh_pairlist()
self.active_pair_whitelist = self.pairlists.whitelist
# Calculating Edge positiong
# Should be called before refresh_tickers
# Otherwise it will override cached klines in exchange
# with delta value (klines only from last refresh_pairs)
if self.edge:
self.edge.calculate()
self.active_pair_whitelist = self.edge.adjust(self.active_pair_whitelist)
# Query trades from persistence layer
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
# Extend active-pair whitelist with pairs from open trades
# ensures that tickers are downloaded for open trades
self.active_pair_whitelist.extend([trade.pair for trade in trades
if trade.pair not in self.active_pair_whitelist])
# Refreshing candles
self.exchange.refresh_tickers(self.active_pair_whitelist, self.strategy.ticker_interval)
# First process current opened trades
for trade in trades:
state_changed |= self.process_maybe_execute_sell(trade)
# Then looking for buy opportunities
if len(trades) < self.config['max_open_trades']:
state_changed = self.process_maybe_execute_buy()
if 'unfilledtimeout' in self.config:
# Check and handle any timed out open orders
self.check_handle_timedout()
Trade.session.flush()
except TemporaryError as error:
logger.warning('%s, retrying in 30 seconds...', error)
time.sleep(constants.RETRY_TIMEOUT)
except OperationalException:
tb = traceback.format_exc()
hint = 'Issue `/start` if you think it is safe to restart.'
self.rpc.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': f'OperationalException:\n```\n{tb}```{hint}'
})
logger.exception('OperationalException. Stopping trader ...')
self.state = State.STOPPED
return state_changed
def get_target_bid(self, pair: str, ticker: Dict[str, float]) -> float:
"""
Calculates bid target between current ask price and last price
:param ticker: Ticker to use for getting Ask and Last Price
:return: float: Price
"""
if ticker['ask'] < ticker['last']:
ticker_rate = ticker['ask']
else:
balance = self.config['bid_strategy']['ask_last_balance']
ticker_rate = ticker['ask'] + balance * (ticker['last'] - ticker['ask'])
used_rate = ticker_rate
config_bid_strategy = self.config.get('bid_strategy', {})
if 'use_order_book' in config_bid_strategy and\
config_bid_strategy.get('use_order_book', False):
logger.info('Getting price from order book')
order_book_top = config_bid_strategy.get('order_book_top', 1)
order_book = self.exchange.get_order_book(pair, order_book_top)
logger.debug('order_book %s', order_book)
# top 1 = index 0
order_book_rate = order_book['bids'][order_book_top - 1][0]
# if ticker has lower rate, then use ticker ( usefull if down trending )
logger.info('...top %s order book buy rate %0.8f', order_book_top, order_book_rate)
if ticker_rate < order_book_rate:
logger.info('...using ticker rate instead %0.8f', ticker_rate)
used_rate = ticker_rate
else:
used_rate = order_book_rate
else:
logger.info('Using Last Ask / Last Price')
used_rate = ticker_rate
return used_rate
def _get_trade_stake_amount(self, pair) -> Optional[float]:
"""
Check if stake amount can be fulfilled with the available balance
for the stake currency
:return: float: Stake Amount
"""
if self.edge:
return self.edge.stake_amount(
pair,
self.wallets.get_free(self.config['stake_currency']),
self.wallets.get_total(self.config['stake_currency']),
Trade.total_open_trades_stakes()
)
else:
stake_amount = self.config['stake_amount']
avaliable_amount = self.wallets.get_free(self.config['stake_currency'])
if stake_amount == constants.UNLIMITED_STAKE_AMOUNT:
open_trades = len(Trade.query.filter(Trade.is_open.is_(True)).all())
if open_trades >= self.config['max_open_trades']:
logger.warning('Can\'t open a new trade: max number of trades is reached')
return None
return avaliable_amount / (self.config['max_open_trades'] - open_trades)
# Check if stake_amount is fulfilled
if avaliable_amount < stake_amount:
raise DependencyException(
'Available balance(%f %s) is lower than stake amount(%f %s)' % (
avaliable_amount, self.config['stake_currency'],
stake_amount, self.config['stake_currency'])
)
return stake_amount
def _get_min_pair_stake_amount(self, pair: str, price: float) -> Optional[float]:
markets = self.exchange.get_markets()
markets = [m for m in markets if m['symbol'] == pair]
if not markets:
raise ValueError(f'Can\'t get market information for symbol {pair}')
market = markets[0]
if 'limits' not in market:
return None
min_stake_amounts = []
limits = market['limits']
if ('cost' in limits and 'min' in limits['cost']
and limits['cost']['min'] is not None):
min_stake_amounts.append(limits['cost']['min'])
if ('amount' in limits and 'min' in limits['amount']
and limits['amount']['min'] is not None):
min_stake_amounts.append(limits['amount']['min'] * price)
if not min_stake_amounts:
return None
amount_reserve_percent = 1 - 0.05 # reserve 5% + stoploss
if self.strategy.stoploss is not None:
amount_reserve_percent += self.strategy.stoploss
# it should not be more than 50%
amount_reserve_percent = max(amount_reserve_percent, 0.5)
return min(min_stake_amounts) / amount_reserve_percent
def create_trade(self) -> bool:
"""
Checks the implemented trading indicator(s) for a randomly picked pair,
if one pair triggers the buy_signal a new trade record gets created
:return: True if a trade object has been created and persisted, False otherwise
"""
interval = self.strategy.ticker_interval
whitelist = copy.deepcopy(self.active_pair_whitelist)
# Remove currently opened and latest pairs from whitelist
for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
if trade.pair in whitelist:
whitelist.remove(trade.pair)
logger.debug('Ignoring %s in pair whitelist', trade.pair)
if not whitelist:
raise DependencyException('No currency pairs in whitelist')
# running get_signal on historical data fetched
for _pair in whitelist:
(buy, sell) = self.strategy.get_signal(_pair, interval, self.exchange.klines(_pair))
if buy and not sell:
stake_amount = self._get_trade_stake_amount(_pair)
if not stake_amount:
return False
logger.info(
'Buy signal found: about create a new trade with stake_amount: %f ...',
stake_amount
)
bidstrat_check_depth_of_market = self.config.get('bid_strategy', {}).\
get('check_depth_of_market', {})
if (bidstrat_check_depth_of_market.get('enabled', False)) and\
(bidstrat_check_depth_of_market.get('bids_to_ask_delta', 0) > 0):
if self._check_depth_of_market_buy(_pair, bidstrat_check_depth_of_market):
return self.execute_buy(_pair, stake_amount)
else:
return False
return self.execute_buy(_pair, stake_amount)
return False
def _check_depth_of_market_buy(self, pair: str, conf: Dict) -> bool:
"""
Checks depth of market before executing a buy
"""
conf_bids_to_ask_delta = conf.get('bids_to_ask_delta', 0)
logger.info('checking depth of market for %s', pair)
order_book = self.exchange.get_order_book(pair, 1000)
order_book_data_frame = order_book_to_dataframe(order_book['bids'], order_book['asks'])
order_book_bids = order_book_data_frame['b_size'].sum()
order_book_asks = order_book_data_frame['a_size'].sum()
bids_ask_delta = order_book_bids / order_book_asks
logger.info('bids: %s, asks: %s, delta: %s', order_book_bids,
order_book_asks, bids_ask_delta)
if bids_ask_delta >= conf_bids_to_ask_delta:
return True
return False
def execute_buy(self, pair: str, stake_amount: float, price: Optional[float] = None) -> bool:
"""
Executes a limit buy for the given pair
:param pair: pair for which we want to create a LIMIT_BUY
:return: None
"""
pair_s = pair.replace('_', '/')
pair_url = self.exchange.get_pair_detail_url(pair)
stake_currency = self.config['stake_currency']
fiat_currency = self.config.get('fiat_display_currency', None)
time_in_force = self.strategy.order_time_in_force['buy']
if price:
buy_limit_requested = price
else:
# Calculate amount
buy_limit_requested = self.get_target_bid(pair, self.exchange.get_ticker(pair))
min_stake_amount = self._get_min_pair_stake_amount(pair_s, buy_limit_requested)
if min_stake_amount is not None and min_stake_amount > stake_amount:
logger.warning(
f'Can\'t open a new trade for {pair_s}: stake amount'
f' is too small ({stake_amount} < {min_stake_amount})'
)
return False
amount = stake_amount / buy_limit_requested
order = self.exchange.buy(pair=pair, ordertype=self.strategy.order_types['buy'],
amount=amount, rate=buy_limit_requested,
time_in_force=time_in_force)
order_id = order['id']
order_status = order.get('status', None)
# we assume the order is executed at the price requested
buy_limit_filled_price = buy_limit_requested
if order_status == 'expired' or order_status == 'rejected':
order_type = self.strategy.order_types['buy']
order_tif = self.strategy.order_time_in_force['buy']
# return false if the order is not filled
if float(order['filled']) == 0:
logger.warning('Buy %s order with time in force %s for %s is %s by %s.'
' zero amount is fulfilled.',
order_tif, order_type, pair_s, order_status, self.exchange.name)
return False
else:
# the order is partially fulfilled
# in case of IOC orders we can check immediately
# if the order is fulfilled fully or partially
logger.warning('Buy %s order with time in force %s for %s is %s by %s.'
' %s amount fulfilled out of %s (%s remaining which is canceled).',
order_tif, order_type, pair_s, order_status, self.exchange.name,
order['filled'], order['amount'], order['remaining']
)
stake_amount = order['cost']
amount = order['amount']
buy_limit_filled_price = order['price']
order_id = None
# in case of FOK the order may be filled immediately and fully
elif order_status == 'closed':
stake_amount = order['cost']
amount = order['amount']
buy_limit_filled_price = order['price']
order_id = None
self.rpc.send_msg({
'type': RPCMessageType.BUY_NOTIFICATION,
'exchange': self.exchange.name.capitalize(),
'pair': pair_s,
'market_url': pair_url,
'limit': buy_limit_filled_price,
'stake_amount': stake_amount,
'stake_currency': stake_currency,
'fiat_currency': fiat_currency
})
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker')
trade = Trade(
pair=pair,
stake_amount=stake_amount,
amount=amount,
fee_open=fee,
fee_close=fee,
open_rate=buy_limit_filled_price,
open_rate_requested=buy_limit_requested,
open_date=datetime.utcnow(),
exchange=self.exchange.id,
open_order_id=order_id,
strategy=self.strategy.get_strategy_name(),
ticker_interval=constants.TICKER_INTERVAL_MINUTES[self.config['ticker_interval']]
)
Trade.session.add(trade)
Trade.session.flush()
# Updating wallets
self.wallets.update()
return True
def process_maybe_execute_buy(self) -> bool:
"""
Tries to execute a buy trade in a safe way
:return: True if executed
"""
try:
# Create entity and execute trade
if self.create_trade():
return True
logger.info('Found no buy signals for whitelisted currencies. Trying again..')
return False
except DependencyException as exception:
logger.warning('Unable to create trade: %s', exception)
return False
def process_maybe_execute_sell(self, trade: Trade) -> bool:
"""
Tries to execute a sell trade
:return: True if executed
"""
try:
# Get order details for actual price per unit
if trade.open_order_id:
# Update trade with order values
logger.info('Found open order for %s', trade)
order = self.exchange.get_order(trade.open_order_id, trade.pair)
# Try update amount (binance-fix)
try:
new_amount = self.get_real_amount(trade, order)
if order['amount'] != new_amount:
order['amount'] = new_amount
# Fee was applied, so set to 0
trade.fee_open = 0
except OperationalException as exception:
logger.warning("could not update trade amount: %s", exception)
trade.update(order)
if self.strategy.order_types.get('stoploss_on_exchange') and trade.is_open:
result = self.handle_stoploss_on_exchange(trade)
if result:
self.wallets.update()
return result
if trade.is_open and trade.open_order_id is None:
# Check if we can sell our current pair
result = self.handle_trade(trade)
# Updating wallets if any trade occured
if result:
self.wallets.update()
return result
except DependencyException as exception:
logger.warning('Unable to sell trade: %s', exception)
return False
def get_real_amount(self, trade: Trade, order: Dict) -> float:
"""
Get real amount for the trade
Necessary for self.exchanges which charge fees in base currency (e.g. binance)
"""
order_amount = order['amount']
# Only run for closed orders
if trade.fee_open == 0 or order['status'] == 'open':
return order_amount
# use fee from order-dict if possible
if 'fee' in order and order['fee'] and (order['fee'].keys() >= {'currency', 'cost'}):
if trade.pair.startswith(order['fee']['currency']):
new_amount = order_amount - order['fee']['cost']
logger.info("Applying fee on amount for %s (from %s to %s) from Order",
trade, order['amount'], new_amount)
return new_amount
# Fallback to Trades
trades = self.exchange.get_trades_for_order(trade.open_order_id, trade.pair,
trade.open_date)
if len(trades) == 0:
logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade)
return order_amount
amount = 0
fee_abs = 0
for exectrade in trades:
amount += exectrade['amount']
if "fee" in exectrade and (exectrade['fee'].keys() >= {'currency', 'cost'}):
# only applies if fee is in quote currency!
if trade.pair.startswith(exectrade['fee']['currency']):
fee_abs += exectrade['fee']['cost']
if amount != order_amount:
logger.warning(f"amount {amount} does not match amount {trade.amount}")
raise OperationalException("Half bought? Amounts don't match")
real_amount = amount - fee_abs
if fee_abs != 0:
logger.info(f"""Applying fee on amount for {trade} \
(from {order_amount} to {real_amount}) from Trades""")
return real_amount
def handle_trade(self, trade: Trade) -> bool:
"""
Sells the current pair if the threshold is reached and updates the trade record.
:return: True if trade has been sold, False otherwise
"""
if not trade.is_open:
raise ValueError(f'attempt to handle closed trade: {trade}')
logger.debug('Handling %s ...', trade)
sell_rate = self.exchange.get_ticker(trade.pair)['bid']
(buy, sell) = (False, False)
experimental = self.config.get('experimental', {})
if experimental.get('use_sell_signal') or experimental.get('ignore_roi_if_buy_signal'):
(buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.ticker_interval,
self.exchange.klines(trade.pair))
config_ask_strategy = self.config.get('ask_strategy', {})
if config_ask_strategy.get('use_order_book', False):
logger.info('Using order book for selling...')
# logger.debug('Order book %s',orderBook)
order_book_min = config_ask_strategy.get('order_book_min', 1)
order_book_max = config_ask_strategy.get('order_book_max', 1)
order_book = self.exchange.get_order_book(trade.pair, order_book_max)
for i in range(order_book_min, order_book_max + 1):
order_book_rate = order_book['asks'][i - 1][0]
# if orderbook has higher rate (high profit),
# use orderbook, otherwise just use bids rate
logger.info(' order book asks top %s: %0.8f', i, order_book_rate)
if sell_rate < order_book_rate:
sell_rate = order_book_rate
if self.check_sell(trade, sell_rate, buy, sell):
return True
break
else:
logger.debug('checking sell')
if self.check_sell(trade, sell_rate, buy, sell):
return True
logger.debug('Found no sell signal for %s.', trade)
return False
def handle_stoploss_on_exchange(self, trade: Trade) -> bool:
"""
Check if trade is fulfilled in which case the stoploss
on exchange should be added immediately if stoploss on exchnage
is enabled.
"""
result = False
# If trade is open and the buy order is fulfilled but there is no stoploss,
# then we add a stoploss on exchange
if not trade.open_order_id and not trade.stoploss_order_id:
if self.edge:
stoploss = self.edge.stoploss(pair=trade.pair)
else:
stoploss = self.strategy.stoploss
stop_price = trade.open_rate * (1 + stoploss)
# limit price should be less than stop price.
# 0.98 is arbitrary here.
limit_price = stop_price * 0.98
stoploss_order_id = self.exchange.stoploss_limit(
pair=trade.pair, amount=trade.amount, stop_price=stop_price, rate=limit_price
)['id']
trade.stoploss_order_id = str(stoploss_order_id)
# Or the trade open and there is already a stoploss on exchange.
# so we check if it is hit ...
elif trade.stoploss_order_id:
logger.debug('Handling stoploss on exchange %s ...', trade)
order = self.exchange.get_order(trade.stoploss_order_id, trade.pair)
if order['status'] == 'closed':
trade.sell_reason = SellType.STOPLOSS_ON_EXCHANGE.value
trade.update(order)
result = True
else:
result = False
return result
def check_sell(self, trade: Trade, sell_rate: float, buy: bool, sell: bool) -> bool:
if self.edge:
stoploss = self.edge.stoploss(trade.pair)
should_sell = self.strategy.should_sell(
trade, sell_rate, datetime.utcnow(), buy, sell, force_stoploss=stoploss)
else:
should_sell = self.strategy.should_sell(trade, sell_rate, datetime.utcnow(), buy, sell)
if should_sell.sell_flag:
self.execute_sell(trade, sell_rate, should_sell.sell_type)
logger.info('executed sell, reason: %s', should_sell.sell_type)
return True
return False
def check_handle_timedout(self) -> None:
"""
Check if any orders are timed out and cancel if neccessary
:param timeoutvalue: Number of minutes until order is considered timed out
:return: None
"""
buy_timeout = self.config['unfilledtimeout']['buy']
sell_timeout = self.config['unfilledtimeout']['sell']
buy_timeoutthreashold = arrow.utcnow().shift(minutes=-buy_timeout).datetime
sell_timeoutthreashold = arrow.utcnow().shift(minutes=-sell_timeout).datetime
for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all():
try:
# FIXME: Somehow the query above returns results
# where the open_order_id is in fact None.
# This is probably because the record got
# updated via /forcesell in a different thread.
if not trade.open_order_id:
continue
order = self.exchange.get_order(trade.open_order_id, trade.pair)
except (RequestException, DependencyException):
logger.info(
'Cannot query order for %s due to %s',
trade,
traceback.format_exc())
continue
ordertime = arrow.get(order['datetime']).datetime
# Check if trade is still actually open
if float(order['remaining']) == 0.0:
self.wallets.update()
continue
# Check if trade is still actually open
if order['status'] == 'open':
if order['side'] == 'buy' and ordertime < buy_timeoutthreashold:
self.handle_timedout_limit_buy(trade, order)
self.wallets.update()
elif order['side'] == 'sell' and ordertime < sell_timeoutthreashold:
self.handle_timedout_limit_sell(trade, order)
self.wallets.update()
# FIX: 20180110, why is cancel.order unconditionally here, whereas
# it is conditionally called in the
# handle_timedout_limit_sell()?
def handle_timedout_limit_buy(self, trade: Trade, order: Dict) -> bool:
"""Buy timeout - cancel order
:return: True if order was fully cancelled
"""
pair_s = trade.pair.replace('_', '/')
self.exchange.cancel_order(trade.open_order_id, trade.pair)
if order['remaining'] == order['amount']:
# if trade is not partially completed, just delete the trade
Trade.session.delete(trade)
Trade.session.flush()
logger.info('Buy order timeout for %s.', trade)
self.rpc.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': f'Unfilled buy order for {pair_s} cancelled due to timeout'
})
return True
# if trade is partially complete, edit the stake details for the trade
# and close the order
trade.amount = order['amount'] - order['remaining']
trade.stake_amount = trade.amount * trade.open_rate
trade.open_order_id = None
logger.info('Partial buy order timeout for %s.', trade)
self.rpc.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': f'Remaining buy order for {pair_s} cancelled due to timeout'
})
return False
# FIX: 20180110, should cancel_order() be cond. or unconditionally called?
def handle_timedout_limit_sell(self, trade: Trade, order: Dict) -> bool:
"""
Sell timeout - cancel order and update trade
:return: True if order was fully cancelled
"""
pair_s = trade.pair.replace('_', '/')
if order['remaining'] == order['amount']:
# if trade is not partially completed, just cancel the trade
self.exchange.cancel_order(trade.open_order_id, trade.pair)
trade.close_rate = None
trade.close_profit = None
trade.close_date = None
trade.is_open = True
trade.open_order_id = None
self.rpc.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': f'Unfilled sell order for {pair_s} cancelled due to timeout'
})
logger.info('Sell order timeout for %s.', trade)
return True
# TODO: figure out how to handle partially complete sell orders
return False
def execute_sell(self, trade: Trade, limit: float, sell_reason: SellType) -> None:
"""
Executes a limit sell for the given trade and limit
:param trade: Trade instance
:param limit: limit rate for the sell order
:param sellreason: Reason the sell was triggered
:return: None
"""
sell_type = 'sell'
if sell_reason in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
sell_type = 'stoploss'
# if stoploss is on exchange and we are on dry_run mode,
# we consider the sell price stop price
if self.config.get('dry_run', False) and sell_type == 'stoploss' \
and self.strategy.order_types['stoploss_on_exchange']:
limit = trade.stop_loss
# First cancelling stoploss on exchange ...
if self.strategy.order_types.get('stoploss_on_exchange') and trade.stoploss_order_id:
self.exchange.cancel_order(trade.stoploss_order_id, trade.pair)
# Execute sell and update trade record
order_id = self.exchange.sell(pair=str(trade.pair),
ordertype=self.strategy.order_types[sell_type],
amount=trade.amount, rate=limit,
time_in_force=self.strategy.order_time_in_force['sell']
)['id']
trade.open_order_id = order_id
trade.close_rate_requested = limit
trade.sell_reason = sell_reason.value
profit_trade = trade.calc_profit(rate=limit)
current_rate = self.exchange.get_ticker(trade.pair)['bid']
profit_percent = trade.calc_profit_percent(limit)
pair_url = self.exchange.get_pair_detail_url(trade.pair)
gain = "profit" if profit_percent > 0 else "loss"
msg = {
'type': RPCMessageType.SELL_NOTIFICATION,
'exchange': trade.exchange.capitalize(),
'pair': trade.pair,
'gain': gain,
'market_url': pair_url,
'limit': limit,
'amount': trade.amount,
'open_rate': trade.open_rate,
'current_rate': current_rate,
'profit_amount': profit_trade,
'profit_percent': profit_percent,
'sell_reason': sell_reason.value
}
# For regular case, when the configuration exists
if 'stake_currency' in self.config and 'fiat_display_currency' in self.config:
stake_currency = self.config['stake_currency']
fiat_currency = self.config['fiat_display_currency']
msg.update({
'stake_currency': stake_currency,
'fiat_currency': fiat_currency,
})
# Send the message
self.rpc.send_msg(msg)
Trade.session.flush()
+40
View File
@@ -0,0 +1,40 @@
from math import cos, exp, pi, sqrt
import numpy as np
import talib as ta
from pandas import Series
def went_up(series: Series) -> bool:
return series > series.shift(1)
def went_down(series: Series) -> bool:
return series < series.shift(1)
def ehlers_super_smoother(series: Series, smoothing: float = 6) -> Series:
magic = pi * sqrt(2) / smoothing
a1 = exp(-magic)
coeff2 = 2 * a1 * cos(magic)
coeff3 = -a1 * a1
coeff1 = (1 - coeff2 - coeff3) / 2
filtered = series.copy()
for i in range(2, len(series)):
filtered.iloc[i] = coeff1 * (series.iloc[i] + series.iloc[i-1]) + \
coeff2 * filtered.iloc[i-1] + coeff3 * filtered.iloc[i-2]
return filtered
def fishers_inverse(series: Series, smoothing: float = 0) -> np.ndarray:
""" Does a smoothed fishers inverse transformation.
Can be used with any oscillator that goes from 0 to 100 like RSI or MFI """
v1 = 0.1 * (series - 50)
if smoothing > 0:
v2 = ta.WMA(v1.values, timeperiod=smoothing)
else:
v2 = v1
return (np.exp(2 * v2)-1) / (np.exp(2 * v2) + 1)
+65 -337
View File
@@ -1,361 +1,89 @@
#!/usr/bin/env python3
import copy
import json
"""
Main Freqtrade bot script.
Read the documentation to know what cli arguments you need.
"""
import logging
import time
import traceback
from datetime import datetime
from signal import signal, SIGINT, SIGABRT, SIGTERM
from typing import Dict, Optional, List
import sys
from argparse import Namespace
from typing import List
import requests
from cachetools import cached, TTLCache
from jsonschema import validate
from freqtrade import __version__, exchange, persistence
from freqtrade.analyze import get_buy_signal
from freqtrade.misc import CONF_SCHEMA, State, get_state, update_state, build_arg_parser, throttle
from freqtrade.persistence import Trade
from freqtrade.rpc import telegram
from freqtrade import OperationalException
from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration, set_loggers
from freqtrade.freqtradebot import FreqtradeBot
from freqtrade.state import State
from freqtrade.rpc import RPCMessageType
logger = logging.getLogger('freqtrade')
_CONF = {}
def refresh_whitelist(whitelist: Optional[List[str]] = None) -> None:
def main(sysargv: List[str]) -> None:
"""
Check wallet health and remove pair from whitelist if necessary
:param whitelist: a new whitelist (optional)
This function will initiate the bot and start the trading loop.
:return: None
"""
whitelist = whitelist or _CONF['exchange']['pair_whitelist']
arguments = Arguments(
sysargv,
'Simple High Frequency Trading Bot for crypto currencies'
)
args = arguments.get_parsed_arg()
sanitized_whitelist = []
health = exchange.get_wallet_health()
for status in health:
pair = '{}_{}'.format(_CONF['stake_currency'], status['Currency'])
if pair not in whitelist:
continue
if status['IsActive']:
sanitized_whitelist.append(pair)
else:
logger.info(
'Ignoring %s from whitelist (reason: %s).',
pair, status.get('Notice') or 'wallet is not active'
)
if _CONF['exchange']['pair_whitelist'] != sanitized_whitelist:
logger.debug('Using refreshed pair whitelist: %s ...', sanitized_whitelist)
_CONF['exchange']['pair_whitelist'] = sanitized_whitelist
# A subcommand has been issued.
# Means if Backtesting or Hyperopt have been called we exit the bot
if hasattr(args, 'func'):
args.func(args)
return
def _process(dynamic_whitelist: Optional[bool] = False) -> bool:
"""
Queries the persistence layer for open trades and handles them,
otherwise a new trade is created.
:param: dynamic_whitelist: True is a dynamic whitelist should be generated (optional)
:return: True if a trade has been created or closed, False otherwise
"""
state_changed = False
freqtrade = None
return_code = 1
try:
# Refresh whitelist based on wallet maintenance
refresh_whitelist(
gen_pair_whitelist(_CONF['stake_currency']) if dynamic_whitelist else None
)
# Query trades from persistence layer
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
if len(trades) < _CONF['max_open_trades']:
try:
# Create entity and execute trade
trade = create_trade(float(_CONF['stake_amount']))
if trade:
Trade.session.add(trade)
state_changed = True
else:
logger.info(
'Checked all whitelisted currencies. '
'Found no suitable entry positions for buying. Will keep looking ...'
)
except ValueError:
logger.exception('Unable to create trade')
# Load and validate configuration
config = Configuration(args).get_config()
for trade in trades:
# Get order details for actual price per unit
if trade.open_order_id:
# Update trade with order values
logger.info('Got open order for %s', trade)
trade.update(exchange.get_order(trade.open_order_id))
# Init the bot
freqtrade = FreqtradeBot(config)
if not close_trade_if_fulfilled(trade):
# Check if we can sell our current pair
state_changed = handle_trade(trade) or state_changed
state = None
while 1:
state = freqtrade.worker(old_state=state)
if state == State.RELOAD_CONF:
freqtrade = reconfigure(freqtrade, args)
Trade.session.flush()
except (requests.exceptions.RequestException, json.JSONDecodeError) as error:
msg = 'Got {} in _process(), retrying in 30 seconds...'.format(error.__class__.__name__)
logger.exception(msg)
time.sleep(30)
except RuntimeError:
telegram.send_msg('*Status:* Got RuntimeError:\n```\n{traceback}```{hint}'.format(
traceback=traceback.format_exc(),
hint='Issue `/start` if you think it is safe to restart.'
))
logger.exception('Got RuntimeError. Stopping trader ...')
update_state(State.STOPPED)
return state_changed
except KeyboardInterrupt:
logger.info('SIGINT received, aborting ...')
return_code = 0
except OperationalException as e:
logger.error(str(e))
return_code = 2
except BaseException:
logger.exception('Fatal exception!')
finally:
if freqtrade:
freqtrade.rpc.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': 'process died'
})
freqtrade.cleanup()
sys.exit(return_code)
def close_trade_if_fulfilled(trade: Trade) -> bool:
def reconfigure(freqtrade: FreqtradeBot, args: Namespace) -> FreqtradeBot:
"""
Checks if the trade is closable, and if so it is being closed.
:param trade: Trade
:return: True if trade has been closed else False
Cleans up current instance, reloads the configuration and returns the new instance
"""
# If we don't have an open order and the close rate is already set,
# we can close this trade.
if trade.close_profit is not None \
and trade.close_date is not None \
and trade.close_rate is not None \
and trade.open_order_id is None:
trade.is_open = False
logger.info(
'Marking %s as closed as the trade is fulfilled and found no open orders for it.',
trade
)
return True
return False
# Clean up current modules
freqtrade.cleanup()
def execute_sell(trade: Trade, limit: float) -> None:
"""
Executes a limit sell for the given trade and limit
:param trade: Trade instance
:param limit: limit rate for the sell order
:return: None
"""
# Execute sell and update trade record
order_id = exchange.sell(str(trade.pair), limit, trade.amount)
trade.open_order_id = order_id
fmt_exp_profit = round(trade.calc_profit(limit) * 100, 2)
message = '*{}:* Selling [{}]({}) with limit `{:.8f} (profit: ~{:.2f}%)`'.format(
trade.exchange,
trade.pair.replace('_', '/'),
exchange.get_pair_detail_url(trade.pair),
limit,
fmt_exp_profit
)
logger.info(message)
telegram.send_msg(message)
def should_sell(trade: Trade, current_rate: float, current_time: datetime) -> bool:
"""
Based an earlier trade and current price and configuration, decides whether bot should sell
:return True if bot should sell at current rate
"""
current_profit = trade.calc_profit(current_rate)
if 'stoploss' in _CONF and current_profit < float(_CONF['stoploss']):
logger.debug('Stop loss hit.')
return True
for duration, threshold in sorted(_CONF['minimal_roi'].items()):
# Check if time matches and current rate is above threshold
time_diff = (current_time - trade.open_date).total_seconds() / 60
if time_diff > float(duration) and current_profit > threshold:
return True
logger.debug('Threshold not reached. (cur_profit: %1.2f%%)', current_profit * 100.0)
return False
def handle_trade(trade: Trade) -> bool:
"""
Sells the current pair if the threshold is reached and updates the trade record.
:return: True if trade has been sold, False otherwise
"""
if not trade.is_open:
raise ValueError('attempt to handle closed trade: {}'.format(trade))
logger.debug('Handling %s ...', trade)
current_rate = exchange.get_ticker(trade.pair)['bid']
if should_sell(trade, current_rate, datetime.utcnow()):
execute_sell(trade, current_rate)
return True
return False
def get_target_bid(ticker: Dict[str, float]) -> float:
""" Calculates bid target between current ask price and last price """
if ticker['ask'] < ticker['last']:
return ticker['ask']
balance = _CONF['bid_strategy']['ask_last_balance']
return ticker['ask'] + balance * (ticker['last'] - ticker['ask'])
def create_trade(stake_amount: float) -> Optional[Trade]:
"""
Checks the implemented trading indicator(s) for a randomly picked pair,
if one pair triggers the buy_signal a new trade record gets created
:param stake_amount: amount of btc to spend
"""
logger.info(
'Checking buy signals to create a new trade with stake_amount: %f ...',
stake_amount
)
whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist'])
# Check if stake_amount is fulfilled
if exchange.get_balance(_CONF['stake_currency']) < stake_amount:
raise ValueError(
'stake amount is not fulfilled (currency={})'.format(_CONF['stake_currency'])
)
# Remove currently opened and latest pairs from whitelist
for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
if trade.pair in whitelist:
whitelist.remove(trade.pair)
logger.debug('Ignoring %s in pair whitelist', trade.pair)
if not whitelist:
raise ValueError('No pair in whitelist')
# Pick pair based on StochRSI buy signals
for _pair in whitelist:
if get_buy_signal(_pair):
pair = _pair
break
else:
return None
# Calculate amount and subtract fee
fee = exchange.get_fee()
buy_limit = get_target_bid(exchange.get_ticker(pair))
amount = (1 - fee) * stake_amount / buy_limit
order_id = exchange.buy(pair, buy_limit, amount)
# Create trade entity and return
message = '*{}:* Buying [{}]({}) with limit `{:.8f}`'.format(
exchange.get_name().upper(),
pair.replace('_', '/'),
exchange.get_pair_detail_url(pair),
buy_limit
)
logger.info(message)
telegram.send_msg(message)
# Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
return Trade(pair=pair,
stake_amount=stake_amount,
amount=amount,
fee=fee * 2,
open_rate=buy_limit,
open_date=datetime.utcnow(),
exchange=exchange.get_name().upper(),
open_order_id=order_id)
def init(config: dict, db_url: Optional[str] = None) -> None:
"""
Initializes all modules and updates the config
:param config: config as dict
:param db_url: database connector string for sqlalchemy (Optional)
:return: None
"""
# Initialize all modules
telegram.init(config)
persistence.init(config, db_url)
exchange.init(config)
# Set initial application state
initial_state = config.get('initial_state')
if initial_state:
update_state(State[initial_state.upper()])
else:
update_state(State.STOPPED)
# Register signal handlers
for sig in (SIGINT, SIGTERM, SIGABRT):
signal(sig, cleanup)
@cached(TTLCache(maxsize=1, ttl=1800))
def gen_pair_whitelist(base_currency: str, topn: int = 20, key: str = 'BaseVolume') -> List[str]:
"""
Updates the whitelist with with a dynamically generated list
:param base_currency: base currency as str
:param topn: maximum number of returned results
:param key: sort key (defaults to 'BaseVolume')
:return: List of pairs
"""
summaries = sorted(
(s for s in exchange.get_market_summaries() if s['MarketName'].startswith(base_currency)),
key=lambda s: s.get(key) or 0.0,
reverse=True
)
return [s['MarketName'].replace('-', '_') for s in summaries[:topn]]
def cleanup(*args, **kwargs) -> None:
"""
Cleanup the application state und finish all pending tasks
:return: None
"""
telegram.send_msg('*Status:* `Stopping trader...`')
logger.info('Stopping trader and cleaning up modules...')
update_state(State.STOPPED)
persistence.cleanup()
telegram.cleanup()
exit(0)
def main():
"""
Loads and validates the config and handles the main loop
:return: None
"""
global _CONF
args = build_arg_parser().parse_args()
# Initialize logger
logging.basicConfig(
level=args.loglevel,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
)
logger.info(
'Starting freqtrade %s (loglevel=%s)',
__version__,
logging.getLevelName(args.loglevel)
)
# Load and validate configuration
with open(args.config) as file:
_CONF = json.load(file)
if 'internals' not in _CONF:
_CONF['internals'] = {}
logger.info('Validating configuration ...')
validate(_CONF, CONF_SCHEMA)
# Initialize all modules and start main loop
if args.dynamic_whitelist:
logger.info('Using dynamically generated whitelist. (--dynamic-whitelist detected)')
init(_CONF)
old_state = None
while True:
new_state = get_state()
# Log state transition
if new_state != old_state:
telegram.send_msg('*Status:* `{}`'.format(new_state.name.lower()))
logger.info('Changing state to: %s', new_state.name)
if new_state == State.STOPPED:
time.sleep(1)
elif new_state == State.RUNNING:
throttle(
_process,
min_secs=_CONF['internals'].get('process_throttle_secs', 10),
dynamic_whitelist=args.dynamic_whitelist,
)
old_state = new_state
# Create new instance
freqtrade = FreqtradeBot(Configuration(args).get_config())
freqtrade.rpc.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': 'config reloaded'
})
return freqtrade
if __name__ == '__main__':
main()
set_loggers()
main(sys.argv[1:])
+74 -151
View File
@@ -1,168 +1,91 @@
import argparse
import enum
"""
Various tool function for Freqtrade and scripts
"""
import gzip
import json
import logging
from typing import Any, Callable
import re
from datetime import datetime
from typing import Dict
import time
from wrapt import synchronized
from freqtrade import __version__
import numpy as np
from pandas import DataFrame
logger = logging.getLogger(__name__)
class State(enum.Enum):
RUNNING = 0
STOPPED = 1
# Current application state
_STATE = State.STOPPED
@synchronized
def update_state(state: State) -> None:
def shorten_date(_date: str) -> str:
"""
Updates the application state
:param state: new state
:return: None
Trim the date so it fits on small screens
"""
global _STATE
_STATE = state
new_date = re.sub('seconds?', 'sec', _date)
new_date = re.sub('minutes?', 'min', new_date)
new_date = re.sub('hours?', 'h', new_date)
new_date = re.sub('days?', 'd', new_date)
new_date = re.sub('^an?', '1', new_date)
return new_date
@synchronized
def get_state() -> State:
############################################
# Used by scripts #
# Matplotlib doesn't support ::datetime64, #
# so we need to convert it into ::datetime #
############################################
def datesarray_to_datetimearray(dates: np.ndarray) -> np.ndarray:
"""
Gets the current application state
Convert an pandas-array of timestamps into
An numpy-array of datetimes
:return: numpy-array of datetime
"""
times = []
dates = dates.astype(datetime)
for index in range(0, dates.size):
date = dates[index].to_pydatetime()
times.append(date)
return np.array(times)
def common_datearray(dfs: Dict[str, DataFrame]) -> np.ndarray:
"""
Return dates from Dataframe
:param dfs: Dict with format pair: pair_data
:return: List of dates
"""
alldates = {}
for pair, pair_data in dfs.items():
dates = datesarray_to_datetimearray(pair_data['date'])
for date in dates:
alldates[date] = 1
lst = []
for date, _ in alldates.items():
lst.append(date)
arr = np.array(lst)
return np.sort(arr, axis=0)
def file_dump_json(filename, data, is_zip=False) -> None:
"""
Dump JSON data into a file
:param filename: file to create
:param data: JSON Data to save
:return:
"""
return _STATE
print(f'dumping json to "{filename}"')
if is_zip:
if not filename.endswith('.gz'):
filename = filename + '.gz'
with gzip.open(filename, 'w') as fp:
json.dump(data, fp, default=str)
else:
with open(filename, 'w') as fp:
json.dump(data, fp, default=str)
def throttle(func: Callable[..., Any], min_secs: float, *args, **kwargs) -> Any:
def format_ms_time(date: int) -> str:
"""
Throttles the given callable that it
takes at least `min_secs` to finish execution.
:param func: Any callable
:param min_secs: minimum execution time in seconds
:return: Any
convert MS date to readable format.
: epoch-string in ms
"""
start = time.time()
result = func(*args, **kwargs)
end = time.time()
duration = max(min_secs - (end - start), 0.0)
logger.debug('Throttling %s for %.2f seconds', func.__name__, duration)
time.sleep(duration)
return result
def build_arg_parser() -> argparse.ArgumentParser:
""" Builds and returns an ArgumentParser instance """
parser = argparse.ArgumentParser(
description='Simple High Frequency Trading Bot for crypto currencies'
)
parser.add_argument(
'-c', '--config',
help='specify configuration file (default: config.json)',
dest='config',
default='config.json',
type=str,
metavar='PATH',
)
parser.add_argument(
'-v', '--verbose',
help='be verbose',
action='store_const',
dest='loglevel',
const=logging.DEBUG,
default=logging.INFO,
)
parser.add_argument(
'--version',
action='version',
version='%(prog)s {}'.format(__version__),
)
parser.add_argument(
'--dynamic-whitelist',
help='dynamically generate and update whitelist based on 24h BaseVolume',
action='store_true',
)
return parser
# Required json-schema for user specified config
CONF_SCHEMA = {
'type': 'object',
'properties': {
'max_open_trades': {'type': 'integer', 'minimum': 1},
'stake_currency': {'type': 'string', 'enum': ['BTC', 'ETH', 'USDT']},
'stake_amount': {'type': 'number', 'minimum': 0.0005},
'dry_run': {'type': 'boolean'},
'minimal_roi': {
'type': 'object',
'patternProperties': {
'^[0-9.]+$': {'type': 'number'}
},
'minProperties': 1
},
'stoploss': {'type': 'number', 'maximum': 0, 'exclusiveMaximum': True},
'bid_strategy': {
'type': 'object',
'properties': {
'ask_last_balance': {
'type': 'number',
'minimum': 0,
'maximum': 1,
'exclusiveMaximum': False
},
},
'required': ['ask_last_balance']
},
'exchange': {'$ref': '#/definitions/exchange'},
'telegram': {
'type': 'object',
'properties': {
'enabled': {'type': 'boolean'},
'token': {'type': 'string'},
'chat_id': {'type': 'string'},
},
'required': ['enabled', 'token', 'chat_id']
},
'initial_state': {'type': 'string', 'enum': ['running', 'stopped']},
'internals': {
'type': 'object',
'properties': {
'process_throttle_secs': {'type': 'number'}
}
}
},
'definitions': {
'exchange': {
'type': 'object',
'properties': {
'name': {'type': 'string'},
'key': {'type': 'string'},
'secret': {'type': 'string'},
'pair_whitelist': {
'type': 'array',
'items': {'type': 'string'},
'uniqueItems': True
}
},
'required': ['name', 'key', 'secret', 'pair_whitelist']
}
},
'anyOf': [
{'required': ['exchange']}
],
'required': [
'max_open_trades',
'stake_currency',
'stake_amount',
'dry_run',
'minimal_roi',
'bid_strategy',
'telegram'
]
}
return datetime.fromtimestamp(date/1000.0).strftime('%Y-%m-%dT%H:%M:%S')
+49
View File
@@ -0,0 +1,49 @@
# pragma pylint: disable=missing-docstring
import logging
from datetime import datetime
from typing import Dict, Tuple
import operator
import arrow
from pandas import DataFrame
from freqtrade.optimize.default_hyperopt import DefaultHyperOpts # noqa: F401
logger = logging.getLogger(__name__)
def get_timeframe(data: Dict[str, DataFrame]) -> Tuple[arrow.Arrow, arrow.Arrow]:
"""
Get the maximum timeframe for the given backtest data
:param data: dictionary with preprocessed backtesting data
:return: tuple containing min_date, max_date
"""
timeframe = [
(arrow.get(frame['date'].min()), arrow.get(frame['date'].max()))
for frame in data.values()
]
return min(timeframe, key=operator.itemgetter(0))[0], \
max(timeframe, key=operator.itemgetter(1))[1]
def validate_backtest_data(data: Dict[str, DataFrame], min_date: datetime,
max_date: datetime, ticker_interval_mins: int) -> bool:
"""
Validates preprocessed backtesting data for missing values and shows warnings about it that.
:param data: dictionary with preprocessed backtesting data
:param min_date: start-date of the data
:param max_date: end-date of the data
:param ticker_interval_mins: ticker interval in minutes
"""
# total difference in minutes / interval-minutes
expected_frames = int((max_date - min_date).total_seconds() // 60 // ticker_interval_mins)
found_missing = False
for pair, df in data.items():
dflen = len(df)
if dflen < expected_frames:
found_missing = True
logger.warning("%s has missing frames: expected %s, got %s, that's %s missing values",
pair, expected_frames, dflen, expected_frames - dflen)
return found_missing
+477
View File
@@ -0,0 +1,477 @@
# pragma pylint: disable=missing-docstring, W0212, too-many-arguments
"""
This module contains the backtesting logic
"""
import logging
from argparse import Namespace
from copy import deepcopy
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any, Dict, List, NamedTuple, Optional
from pandas import DataFrame
from tabulate import tabulate
import freqtrade.optimize as optimize
from freqtrade import DependencyException, constants
from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration
from freqtrade.exchange import Exchange
from freqtrade.data import history
from freqtrade.misc import file_dump_json
from freqtrade.persistence import Trade
from freqtrade.resolvers import StrategyResolver
from freqtrade.strategy.interface import SellType, IStrategy
logger = logging.getLogger(__name__)
class BacktestResult(NamedTuple):
"""
NamedTuple Defining BacktestResults inputs.
"""
pair: str
profit_percent: float
profit_abs: float
open_time: datetime
close_time: datetime
open_index: int
close_index: int
trade_duration: float
open_at_end: bool
open_rate: float
close_rate: float
sell_reason: SellType
class Backtesting(object):
"""
Backtesting class, this class contains all the logic to run a backtest
To run a backtest:
backtesting = Backtesting(config)
backtesting.start()
"""
def __init__(self, config: Dict[str, Any]) -> None:
self.config = config
# Reset keys for backtesting
self.config['exchange']['key'] = ''
self.config['exchange']['secret'] = ''
self.config['exchange']['password'] = ''
self.config['exchange']['uid'] = ''
self.config['dry_run'] = True
self.strategylist: List[IStrategy] = []
if self.config.get('strategy_list', None):
# Force one interval
self.ticker_interval = str(self.config.get('ticker_interval'))
self.ticker_interval_mins = constants.TICKER_INTERVAL_MINUTES[self.ticker_interval]
for strat in list(self.config['strategy_list']):
stratconf = deepcopy(self.config)
stratconf['strategy'] = strat
self.strategylist.append(StrategyResolver(stratconf).strategy)
else:
# only one strategy
self.strategylist.append(StrategyResolver(self.config).strategy)
# Load one strategy
self._set_strategy(self.strategylist[0])
self.exchange = Exchange(self.config)
self.fee = self.exchange.get_fee()
def _set_strategy(self, strategy):
"""
Load strategy into backtesting
"""
self.strategy = strategy
self.ticker_interval = self.config.get('ticker_interval')
self.ticker_interval_mins = constants.TICKER_INTERVAL_MINUTES[self.ticker_interval]
self.tickerdata_to_dataframe = strategy.tickerdata_to_dataframe
self.advise_buy = strategy.advise_buy
self.advise_sell = strategy.advise_sell
def _generate_text_table(self, data: Dict[str, Dict], results: DataFrame,
skip_nan: bool = False) -> str:
"""
Generates and returns a text table for the given backtest data and the results dataframe
:return: pretty printed table with tabulate as str
"""
stake_currency = str(self.config.get('stake_currency'))
floatfmt = ('s', 'd', '.2f', '.2f', '.8f', 'd', '.1f', '.1f')
tabular_data = []
headers = ['pair', 'buy count', 'avg profit %', 'cum profit %',
'total profit ' + stake_currency, 'avg duration', 'profit', 'loss']
for pair in data:
result = results[results.pair == pair]
if skip_nan and result.profit_abs.isnull().all():
continue
tabular_data.append([
pair,
len(result.index),
result.profit_percent.mean() * 100.0,
result.profit_percent.sum() * 100.0,
result.profit_abs.sum(),
str(timedelta(
minutes=round(result.trade_duration.mean()))) if not result.empty else '0:00',
len(result[result.profit_abs > 0]),
len(result[result.profit_abs < 0])
])
# Append Total
tabular_data.append([
'TOTAL',
len(results.index),
results.profit_percent.mean() * 100.0,
results.profit_percent.sum() * 100.0,
results.profit_abs.sum(),
str(timedelta(
minutes=round(results.trade_duration.mean()))) if not results.empty else '0:00',
len(results[results.profit_abs > 0]),
len(results[results.profit_abs < 0])
])
return tabulate(tabular_data, headers=headers, floatfmt=floatfmt, tablefmt="pipe")
def _generate_text_table_sell_reason(self, data: Dict[str, Dict], results: DataFrame) -> str:
"""
Generate small table outlining Backtest results
"""
tabular_data = []
headers = ['Sell Reason', 'Count']
for reason, count in results['sell_reason'].value_counts().iteritems():
tabular_data.append([reason.value, count])
return tabulate(tabular_data, headers=headers, tablefmt="pipe")
def _generate_text_table_strategy(self, all_results: dict) -> str:
"""
Generate summary table per strategy
"""
stake_currency = str(self.config.get('stake_currency'))
floatfmt = ('s', 'd', '.2f', '.2f', '.8f', 'd', '.1f', '.1f')
tabular_data = []
headers = ['Strategy', 'buy count', 'avg profit %', 'cum profit %',
'total profit ' + stake_currency, 'avg duration', 'profit', 'loss']
for strategy, results in all_results.items():
tabular_data.append([
strategy,
len(results.index),
results.profit_percent.mean() * 100.0,
results.profit_percent.sum() * 100.0,
results.profit_abs.sum(),
str(timedelta(
minutes=round(results.trade_duration.mean()))) if not results.empty else '0:00',
len(results[results.profit_abs > 0]),
len(results[results.profit_abs < 0])
])
return tabulate(tabular_data, headers=headers, floatfmt=floatfmt, tablefmt="pipe")
def _store_backtest_result(self, recordfilename: str, results: DataFrame,
strategyname: Optional[str] = None) -> None:
records = [(t.pair, t.profit_percent, t.open_time.timestamp(),
t.close_time.timestamp(), t.open_index - 1, t.trade_duration,
t.open_rate, t.close_rate, t.open_at_end, t.sell_reason.value)
for index, t in results.iterrows()]
if records:
if strategyname:
# Inject strategyname to filename
recname = Path(recordfilename)
recordfilename = str(Path.joinpath(
recname.parent, f'{recname.stem}-{strategyname}').with_suffix(recname.suffix))
logger.info('Dumping backtest results to %s', recordfilename)
file_dump_json(recordfilename, records)
def _get_sell_trade_entry(
self, pair: str, buy_row: DataFrame,
partial_ticker: List, trade_count_lock: Dict, args: Dict) -> Optional[BacktestResult]:
stake_amount = args['stake_amount']
max_open_trades = args.get('max_open_trades', 0)
trade = Trade(
open_rate=buy_row.open,
open_date=buy_row.date,
stake_amount=stake_amount,
amount=stake_amount / buy_row.open,
fee_open=self.fee,
fee_close=self.fee
)
# calculate win/lose forwards from buy point
for sell_row in partial_ticker:
if max_open_trades > 0:
# Increase trade_count_lock for every iteration
trade_count_lock[sell_row.date] = trade_count_lock.get(sell_row.date, 0) + 1
buy_signal = sell_row.buy
sell = self.strategy.should_sell(trade, sell_row.open, sell_row.date, buy_signal,
sell_row.sell, low=sell_row.low, high=sell_row.high)
if sell.sell_flag:
trade_dur = int((sell_row.date - buy_row.date).total_seconds() // 60)
# Special handling if high or low hit STOP_LOSS or ROI
if sell.sell_type in (SellType.STOP_LOSS, SellType.TRAILING_STOP_LOSS):
# Set close_rate to stoploss
closerate = trade.stop_loss
elif sell.sell_type == (SellType.ROI):
# get entry in min_roi >= to trade duration
roi_entry = max(list(filter(lambda x: trade_dur >= x,
self.strategy.minimal_roi.keys())))
roi = self.strategy.minimal_roi[roi_entry]
# - (Expected abs profit + open_rate + open_fee) / (fee_close -1)
closerate = - (trade.open_rate * roi + trade.open_rate *
(1 + trade.fee_open)) / (trade.fee_close - 1)
else:
closerate = sell_row.open
return BacktestResult(pair=pair,
profit_percent=trade.calc_profit_percent(rate=closerate),
profit_abs=trade.calc_profit(rate=closerate),
open_time=buy_row.date,
close_time=sell_row.date,
trade_duration=trade_dur,
open_index=buy_row.Index,
close_index=sell_row.Index,
open_at_end=False,
open_rate=buy_row.open,
close_rate=closerate,
sell_reason=sell.sell_type
)
if partial_ticker:
# no sell condition found - trade stil open at end of backtest period
sell_row = partial_ticker[-1]
btr = BacktestResult(pair=pair,
profit_percent=trade.calc_profit_percent(rate=sell_row.open),
profit_abs=trade.calc_profit(rate=sell_row.open),
open_time=buy_row.date,
close_time=sell_row.date,
trade_duration=int((
sell_row.date - buy_row.date).total_seconds() // 60),
open_index=buy_row.Index,
close_index=sell_row.Index,
open_at_end=True,
open_rate=buy_row.open,
close_rate=sell_row.open,
sell_reason=SellType.FORCE_SELL
)
logger.debug('Force_selling still open trade %s with %s perc - %s', btr.pair,
btr.profit_percent, btr.profit_abs)
return btr
return None
def backtest(self, args: Dict) -> DataFrame:
"""
Implements backtesting functionality
NOTE: This method is used by Hyperopt at each iteration. Please keep it optimized.
Of course try to not have ugly code. By some accessor are sometime slower than functions.
Avoid, logging on this method
:param args: a dict containing:
stake_amount: btc amount to use for each trade
processed: a processed dictionary with format {pair, data}
max_open_trades: maximum number of concurrent trades (default: 0, disabled)
position_stacking: do we allow position stacking? (default: False)
:return: DataFrame
"""
headers = ['date', 'buy', 'open', 'close', 'sell', 'low', 'high']
processed = args['processed']
max_open_trades = args.get('max_open_trades', 0)
position_stacking = args.get('position_stacking', False)
start_date = args['start_date']
end_date = args['end_date']
trades = []
trade_count_lock: Dict = {}
ticker: Dict = {}
pairs = []
# Create ticker dict
for pair, pair_data in processed.items():
pair_data['buy'], pair_data['sell'] = 0, 0 # cleanup from previous run
ticker_data = self.advise_sell(
self.advise_buy(pair_data, {'pair': pair}), {'pair': pair})[headers].copy()
# to avoid using data from future, we buy/sell with signal from previous candle
ticker_data.loc[:, 'buy'] = ticker_data['buy'].shift(1)
ticker_data.loc[:, 'sell'] = ticker_data['sell'].shift(1)
ticker_data.drop(ticker_data.head(1).index, inplace=True)
# Convert from Pandas to list for performance reasons
# (Looping Pandas is slow.)
ticker[pair] = [x for x in ticker_data.itertuples()]
pairs.append(pair)
lock_pair_until: Dict = {}
tmp = start_date + timedelta(minutes=self.ticker_interval_mins)
index = 0
# Loop timerange and test per pair
while tmp < end_date:
# print(f"time: {tmp}")
for i, pair in enumerate(ticker):
try:
row = ticker[pair][index]
except IndexError:
# missing Data for one pair ...
# Warnings for this are shown by `validate_backtest_data`
continue
if row.buy == 0 or row.sell == 1:
continue # skip rows where no buy signal or that would immediately sell off
if not position_stacking:
if pair in lock_pair_until and row.date <= lock_pair_until[pair]:
continue
if max_open_trades > 0:
# Check if max_open_trades has already been reached for the given date
if not trade_count_lock.get(row.date, 0) < max_open_trades:
continue
trade_count_lock[row.date] = trade_count_lock.get(row.date, 0) + 1
trade_entry = self._get_sell_trade_entry(pair, row, ticker[pair][index + 1:],
trade_count_lock, args)
if trade_entry:
lock_pair_until[pair] = trade_entry.close_time
trades.append(trade_entry)
else:
# Set lock_pair_until to end of testing period if trade could not be closed
# This happens only if the buy-signal was with the last candle
lock_pair_until[pair] = end_date
tmp += timedelta(minutes=self.ticker_interval_mins)
index += 1
return DataFrame.from_records(trades, columns=BacktestResult._fields)
def start(self) -> None:
"""
Run a backtesting end-to-end
:return: None
"""
data: Dict[str, Any] = {}
pairs = self.config['exchange']['pair_whitelist']
logger.info('Using stake_currency: %s ...', self.config['stake_currency'])
logger.info('Using stake_amount: %s ...', self.config['stake_amount'])
if self.config.get('live'):
logger.info('Downloading data for all pairs in whitelist ...')
self.exchange.refresh_tickers(pairs, self.ticker_interval)
data = self.exchange._klines
else:
logger.info('Using local backtesting data (using whitelist in given config) ...')
timerange = Arguments.parse_timerange(None if self.config.get(
'timerange') is None else str(self.config.get('timerange')))
data = history.load_data(
datadir=Path(self.config['datadir']) if self.config.get('datadir') else None,
pairs=pairs,
ticker_interval=self.ticker_interval,
refresh_pairs=self.config.get('refresh_pairs', False),
exchange=self.exchange,
timerange=timerange
)
if not data:
logger.critical("No data found. Terminating.")
return
# Use max_open_trades in backtesting, except --disable-max-market-positions is set
if self.config.get('use_max_market_positions', True):
max_open_trades = self.config['max_open_trades']
else:
logger.info('Ignoring max_open_trades (--disable-max-market-positions was used) ...')
max_open_trades = 0
all_results = {}
for strat in self.strategylist:
logger.info("Running backtesting for Strategy %s", strat.get_strategy_name())
self._set_strategy(strat)
# need to reprocess data every time to populate signals
preprocessed = self.strategy.tickerdata_to_dataframe(data)
min_date, max_date = optimize.get_timeframe(preprocessed)
# Validate dataframe for missing values
optimize.validate_backtest_data(preprocessed, min_date, max_date,
constants.TICKER_INTERVAL_MINUTES[self.ticker_interval])
logger.info(
'Measuring data from %s up to %s (%s days)..',
min_date.isoformat(),
max_date.isoformat(),
(max_date - min_date).days
)
# Execute backtest and print results
all_results[self.strategy.get_strategy_name()] = self.backtest(
{
'stake_amount': self.config.get('stake_amount'),
'processed': preprocessed,
'max_open_trades': max_open_trades,
'position_stacking': self.config.get('position_stacking', False),
'start_date': min_date,
'end_date': max_date,
}
)
for strategy, results in all_results.items():
if self.config.get('export', False):
self._store_backtest_result(self.config['exportfilename'], results,
strategy if len(self.strategylist) > 1 else None)
print(f"Result for strategy {strategy}")
print(' BACKTESTING REPORT '.center(119, '='))
print(self._generate_text_table(data, results))
print(' SELL REASON STATS '.center(119, '='))
print(self._generate_text_table_sell_reason(data, results))
print(' LEFT OPEN TRADES REPORT '.center(119, '='))
print(self._generate_text_table(data, results.loc[results.open_at_end], True))
print()
if len(all_results) > 1:
# Print Strategy summary table
print(' Strategy Summary '.center(119, '='))
print(self._generate_text_table_strategy(all_results))
print('\nFor more details, please look at the detail tables above')
def setup_configuration(args: Namespace) -> Dict[str, Any]:
"""
Prepare the configuration for the backtesting
:param args: Cli args from Arguments()
:return: Configuration
"""
configuration = Configuration(args)
config = configuration.get_config()
# Ensure we do not use Exchange credentials
config['exchange']['key'] = ''
config['exchange']['secret'] = ''
if config['stake_amount'] == constants.UNLIMITED_STAKE_AMOUNT:
raise DependencyException('stake amount could not be "%s" for backtesting' %
constants.UNLIMITED_STAKE_AMOUNT)
return config
def start(args: Namespace) -> None:
"""
Start Backtesting script
:param args: Cli args from Arguments()
:return: None
"""
# Initialize configuration
config = setup_configuration(args)
logger.info('Starting freqtrade in Backtesting mode')
# Initialize backtesting object
backtesting = Backtesting(config)
backtesting.start()
+130
View File
@@ -0,0 +1,130 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
import talib.abstract as ta
from pandas import DataFrame
from typing import Dict, Any, Callable, List
from functools import reduce
from skopt.space import Categorical, Dimension, Integer, Real
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.optimize.hyperopt_interface import IHyperOpt
class_name = 'DefaultHyperOpts'
class DefaultHyperOpts(IHyperOpt):
"""
Default hyperopt provided by freqtrade bot.
You can override it with your own hyperopt
"""
@staticmethod
def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe['adx'] = ta.ADX(dataframe)
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['mfi'] = ta.MFI(dataframe)
dataframe['rsi'] = ta.RSI(dataframe)
stoch_fast = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch_fast['fastd']
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
# Bollinger bands
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['sar'] = ta.SAR(dataframe)
return dataframe
@staticmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Define the buy strategy parameters to be used by hyperopt
"""
def populate_buy_trend(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Buy strategy Hyperopt will build and use
"""
conditions = []
# GUARDS AND TRENDS
if 'mfi-enabled' in params and params['mfi-enabled']:
conditions.append(dataframe['mfi'] < params['mfi-value'])
if 'fastd-enabled' in params and params['fastd-enabled']:
conditions.append(dataframe['fastd'] < params['fastd-value'])
if 'adx-enabled' in params and params['adx-enabled']:
conditions.append(dataframe['adx'] > params['adx-value'])
if 'rsi-enabled' in params and params['rsi-enabled']:
conditions.append(dataframe['rsi'] < params['rsi-value'])
# TRIGGERS
if params['trigger'] == 'bb_lower':
conditions.append(dataframe['close'] < dataframe['bb_lowerband'])
if params['trigger'] == 'macd_cross_signal':
conditions.append(qtpylib.crossed_above(
dataframe['macd'], dataframe['macdsignal']
))
if params['trigger'] == 'sar_reversal':
conditions.append(qtpylib.crossed_above(
dataframe['close'], dataframe['sar']
))
dataframe.loc[
reduce(lambda x, y: x & y, conditions),
'buy'] = 1
return dataframe
return populate_buy_trend
@staticmethod
def indicator_space() -> List[Dimension]:
"""
Define your Hyperopt space for searching strategy parameters
"""
return [
Integer(10, 25, name='mfi-value'),
Integer(15, 45, name='fastd-value'),
Integer(20, 50, name='adx-value'),
Integer(20, 40, name='rsi-value'),
Categorical([True, False], name='mfi-enabled'),
Categorical([True, False], name='fastd-enabled'),
Categorical([True, False], name='adx-enabled'),
Categorical([True, False], name='rsi-enabled'),
Categorical(['bb_lower', 'macd_cross_signal', 'sar_reversal'], name='trigger')
]
@staticmethod
def generate_roi_table(params: Dict) -> Dict[int, float]:
"""
Generate the ROI table that will be used by Hyperopt
"""
roi_table = {}
roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3']
roi_table[params['roi_t3']] = params['roi_p1'] + params['roi_p2']
roi_table[params['roi_t3'] + params['roi_t2']] = params['roi_p1']
roi_table[params['roi_t3'] + params['roi_t2'] + params['roi_t1']] = 0
return roi_table
@staticmethod
def stoploss_space() -> List[Dimension]:
"""
Stoploss Value to search
"""
return [
Real(-0.5, -0.02, name='stoploss'),
]
@staticmethod
def roi_space() -> List[Dimension]:
"""
Values to search for each ROI steps
"""
return [
Integer(10, 120, name='roi_t1'),
Integer(10, 60, name='roi_t2'),
Integer(10, 40, name='roi_t3'),
Real(0.01, 0.04, name='roi_p1'),
Real(0.01, 0.07, name='roi_p2'),
Real(0.01, 0.20, name='roi_p3'),
]
+106
View File
@@ -0,0 +1,106 @@
# pragma pylint: disable=missing-docstring, W0212, too-many-arguments
"""
This module contains the edge backtesting interface
"""
import logging
from argparse import Namespace
from typing import Dict, Any
from tabulate import tabulate
from freqtrade.edge import Edge
from freqtrade.configuration import Configuration
from freqtrade.arguments import Arguments
from freqtrade.exchange import Exchange
from freqtrade.resolvers import StrategyResolver
logger = logging.getLogger(__name__)
class EdgeCli(object):
"""
EdgeCli class, this class contains all the logic to run edge backtesting
To run a edge backtest:
edge = EdgeCli(config)
edge.start()
"""
def __init__(self, config: Dict[str, Any]) -> None:
self.config = config
# Reset keys for edge
self.config['exchange']['key'] = ''
self.config['exchange']['secret'] = ''
self.config['exchange']['password'] = ''
self.config['exchange']['uid'] = ''
self.config['dry_run'] = True
self.exchange = Exchange(self.config)
self.strategy = StrategyResolver(self.config).strategy
self.edge = Edge(config, self.exchange, self.strategy)
self.edge._refresh_pairs = self.config.get('refresh_pairs', False)
self.timerange = Arguments.parse_timerange(None if self.config.get(
'timerange') is None else str(self.config.get('timerange')))
self.edge._timerange = self.timerange
def _generate_edge_table(self, results: dict) -> str:
floatfmt = ('s', '.10g', '.2f', '.2f', '.2f', '.2f', 'd', '.d')
tabular_data = []
headers = ['pair', 'stoploss', 'win rate', 'risk reward ratio',
'required risk reward', 'expectancy', 'total number of trades',
'average duration (min)']
for result in results.items():
if result[1].nb_trades > 0:
tabular_data.append([
result[0],
result[1].stoploss,
result[1].winrate,
result[1].risk_reward_ratio,
result[1].required_risk_reward,
result[1].expectancy,
result[1].nb_trades,
round(result[1].avg_trade_duration)
])
return tabulate(tabular_data, headers=headers, floatfmt=floatfmt, tablefmt="pipe")
def start(self) -> None:
self.edge.calculate()
print('') # blank like for readability
print(self._generate_edge_table(self.edge._cached_pairs))
def setup_configuration(args: Namespace) -> Dict[str, Any]:
"""
Prepare the configuration for edge backtesting
:param args: Cli args from Arguments()
:return: Configuration
"""
configuration = Configuration(args)
config = configuration.get_config()
# Ensure we do not use Exchange credentials
config['exchange']['key'] = ''
config['exchange']['secret'] = ''
return config
def start(args: Namespace) -> None:
"""
Start Edge script
:param args: Cli args from Arguments()
:return: None
"""
# Initialize configuration
config = setup_configuration(args)
logger.info('Starting freqtrade in Edge mode')
# Initialize Edge object
edge_cli = EdgeCli(config)
edge_cli.start()
+312
View File
@@ -0,0 +1,312 @@
# pragma pylint: disable=too-many-instance-attributes, pointless-string-statement
"""
This module contains the hyperopt logic
"""
import logging
from argparse import Namespace
import os
import sys
from pathlib import Path
from math import exp
import multiprocessing
from operator import itemgetter
from typing import Any, Dict, List
from pandas import DataFrame
from joblib import Parallel, delayed, dump, load, wrap_non_picklable_objects
from skopt import Optimizer
from skopt.space import Dimension
from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration
from freqtrade.data.history import load_data
from freqtrade.optimize import get_timeframe
from freqtrade.optimize.backtesting import Backtesting
from freqtrade.resolvers import HyperOptResolver
logger = logging.getLogger(__name__)
MAX_LOSS = 100000 # just a big enough number to be bad result in loss optimization
TICKERDATA_PICKLE = os.path.join('user_data', 'hyperopt_tickerdata.pkl')
class Hyperopt(Backtesting):
"""
Hyperopt class, this class contains all the logic to run a hyperopt simulation
To run a backtest:
hyperopt = Hyperopt(config)
hyperopt.start()
"""
def __init__(self, config: Dict[str, Any]) -> None:
super().__init__(config)
self.config = config
self.custom_hyperopt = HyperOptResolver(self.config).hyperopt
# set TARGET_TRADES to suit your number concurrent trades so its realistic
# to the number of days
self.target_trades = 600
self.total_tries = config.get('epochs', 0)
self.current_best_loss = 100
# max average trade duration in minutes
# if eval ends with higher value, we consider it a failed eval
self.max_accepted_trade_duration = 300
# this is expexted avg profit * expected trade count
# for example 3.5%, 1100 trades, self.expected_max_profit = 3.85
# check that the reported Σ% values do not exceed this!
self.expected_max_profit = 3.0
# Previous evaluations
self.trials_file = os.path.join('user_data', 'hyperopt_results.pickle')
self.trials: List = []
def get_args(self, params):
dimensions = self.hyperopt_space()
# Ensure the number of dimensions match
# the number of parameters in the list x.
if len(params) != len(dimensions):
raise ValueError('Mismatch in number of search-space dimensions. '
f'len(dimensions)=={len(dimensions)} and len(x)=={len(params)}')
# Create a dict where the keys are the names of the dimensions
# and the values are taken from the list of parameters x.
arg_dict = {dim.name: value for dim, value in zip(dimensions, params)}
return arg_dict
def save_trials(self) -> None:
"""
Save hyperopt trials to file
"""
if self.trials:
logger.info('Saving %d evaluations to \'%s\'', len(self.trials), self.trials_file)
dump(self.trials, self.trials_file)
def read_trials(self) -> List:
"""
Read hyperopt trials file
"""
logger.info('Reading Trials from \'%s\'', self.trials_file)
trials = load(self.trials_file)
os.remove(self.trials_file)
return trials
def log_trials_result(self) -> None:
"""
Display Best hyperopt result
"""
results = sorted(self.trials, key=itemgetter('loss'))
best_result = results[0]
logger.info(
'Best result:\n%s\nwith values:\n%s',
best_result['result'],
best_result['params']
)
if 'roi_t1' in best_result['params']:
logger.info('ROI table:\n%s',
self.custom_hyperopt.generate_roi_table(best_result['params']))
def log_results(self, results) -> None:
"""
Log results if it is better than any previous evaluation
"""
if results['loss'] < self.current_best_loss:
current = results['current_tries']
total = results['total_tries']
res = results['result']
loss = results['loss']
self.current_best_loss = results['loss']
log_msg = f'\n{current:5d}/{total}: {res}. Loss {loss:.5f}'
print(log_msg)
else:
print('.', end='')
sys.stdout.flush()
def calculate_loss(self, total_profit: float, trade_count: int, trade_duration: float) -> float:
"""
Objective function, returns smaller number for more optimal results
"""
trade_loss = 1 - 0.25 * exp(-(trade_count - self.target_trades) ** 2 / 10 ** 5.8)
profit_loss = max(0, 1 - total_profit / self.expected_max_profit)
duration_loss = 0.4 * min(trade_duration / self.max_accepted_trade_duration, 1)
result = trade_loss + profit_loss + duration_loss
return result
def has_space(self, space: str) -> bool:
"""
Tell if a space value is contained in the configuration
"""
if space in self.config['spaces'] or 'all' in self.config['spaces']:
return True
return False
def hyperopt_space(self) -> List[Dimension]:
"""
Return the space to use during Hyperopt
"""
spaces: List[Dimension] = []
if self.has_space('buy'):
spaces += self.custom_hyperopt.indicator_space()
if self.has_space('roi'):
spaces += self.custom_hyperopt.roi_space()
if self.has_space('stoploss'):
spaces += self.custom_hyperopt.stoploss_space()
return spaces
def generate_optimizer(self, _params: Dict) -> Dict:
params = self.get_args(_params)
if self.has_space('roi'):
self.strategy.minimal_roi = self.custom_hyperopt.generate_roi_table(params)
if self.has_space('buy'):
self.advise_buy = self.custom_hyperopt.buy_strategy_generator(params)
if self.has_space('stoploss'):
self.strategy.stoploss = params['stoploss']
processed = load(TICKERDATA_PICKLE)
min_date, max_date = get_timeframe(processed)
results = self.backtest(
{
'stake_amount': self.config['stake_amount'],
'processed': processed,
'position_stacking': self.config.get('position_stacking', True),
'start_date': min_date,
'end_date': max_date,
}
)
result_explanation = self.format_results(results)
total_profit = results.profit_percent.sum()
trade_count = len(results.index)
trade_duration = results.trade_duration.mean()
if trade_count == 0:
return {
'loss': MAX_LOSS,
'params': params,
'result': result_explanation,
}
loss = self.calculate_loss(total_profit, trade_count, trade_duration)
return {
'loss': loss,
'params': params,
'result': result_explanation,
}
def format_results(self, results: DataFrame) -> str:
"""
Return the format result in a string
"""
trades = len(results.index)
avg_profit = results.profit_percent.mean() * 100.0
total_profit = results.profit_abs.sum()
stake_cur = self.config['stake_currency']
profit = results.profit_percent.sum()
duration = results.trade_duration.mean()
return (f'{trades:6d} trades. Avg profit {avg_profit: 5.2f}%. '
f'Total profit {total_profit: 11.8f} {stake_cur} '
f'({profit:.4f}Σ%). Avg duration {duration:5.1f} mins.')
def get_optimizer(self, cpu_count) -> Optimizer:
return Optimizer(
self.hyperopt_space(),
base_estimator="ET",
acq_optimizer="auto",
n_initial_points=30,
acq_optimizer_kwargs={'n_jobs': cpu_count}
)
def run_optimizer_parallel(self, parallel, asked) -> List:
return parallel(delayed(
wrap_non_picklable_objects(self.generate_optimizer))(v) for v in asked)
def load_previous_results(self):
""" read trials file if we have one """
if os.path.exists(self.trials_file) and os.path.getsize(self.trials_file) > 0:
self.trials = self.read_trials()
logger.info(
'Loaded %d previous evaluations from disk.',
len(self.trials)
)
def start(self) -> None:
timerange = Arguments.parse_timerange(None if self.config.get(
'timerange') is None else str(self.config.get('timerange')))
data = load_data(
datadir=Path(self.config['datadir']) if self.config.get('datadir') else None,
pairs=self.config['exchange']['pair_whitelist'],
ticker_interval=self.ticker_interval,
timerange=timerange
)
if self.has_space('buy'):
self.strategy.advise_indicators = \
self.custom_hyperopt.populate_indicators # type: ignore
dump(self.strategy.tickerdata_to_dataframe(data), TICKERDATA_PICKLE)
self.exchange = None # type: ignore
self.load_previous_results()
cpus = multiprocessing.cpu_count()
logger.info(f'Found {cpus} CPU cores. Let\'s make them scream!')
opt = self.get_optimizer(cpus)
EVALS = max(self.total_tries // cpus, 1)
try:
with Parallel(n_jobs=cpus) as parallel:
for i in range(EVALS):
asked = opt.ask(n_points=cpus)
f_val = self.run_optimizer_parallel(parallel, asked)
opt.tell(asked, [i['loss'] for i in f_val])
self.trials += f_val
for j in range(cpus):
self.log_results({
'loss': f_val[j]['loss'],
'current_tries': i * cpus + j,
'total_tries': self.total_tries,
'result': f_val[j]['result'],
})
except KeyboardInterrupt:
print('User interrupted..')
self.save_trials()
self.log_trials_result()
def start(args: Namespace) -> None:
"""
Start Backtesting script
:param args: Cli args from Arguments()
:return: None
"""
# Remove noisy log messages
logging.getLogger('hyperopt.tpe').setLevel(logging.WARNING)
# Initialize configuration
# Monkey patch the configuration with hyperopt_conf.py
configuration = Configuration(args)
logger.info('Starting freqtrade in Hyperopt mode')
config = configuration.load_config()
config['exchange']['key'] = ''
config['exchange']['secret'] = ''
if config.get('strategy') and config.get('strategy') != 'DefaultStrategy':
logger.error("Please don't use --strategy for hyperopt.")
logger.error(
"Read the documentation at "
"https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md "
"to understand how to configure hyperopt.")
raise ValueError("--strategy configured but not supported for hyperopt")
# Initialize backtesting object
hyperopt = Hyperopt(config)
hyperopt.start()
+66
View File
@@ -0,0 +1,66 @@
"""
IHyperOpt interface
This module defines the interface to apply for hyperopts
"""
from abc import ABC, abstractmethod
from typing import Dict, Any, Callable, List
from pandas import DataFrame
from skopt.space import Dimension
class IHyperOpt(ABC):
"""
Interface for freqtrade hyperopts
Defines the mandatory structure must follow any custom strategies
Attributes you can use:
minimal_roi -> Dict: Minimal ROI designed for the strategy
stoploss -> float: optimal stoploss designed for the strategy
ticker_interval -> int: value of the ticker interval to use for the strategy
"""
@staticmethod
@abstractmethod
def populate_indicators(dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Populate indicators that will be used in the Buy and Sell strategy
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe()
:return: a Dataframe with all mandatory indicators for the strategies
"""
@staticmethod
@abstractmethod
def buy_strategy_generator(params: Dict[str, Any]) -> Callable:
"""
Create a buy strategy generator
"""
@staticmethod
@abstractmethod
def indicator_space() -> List[Dimension]:
"""
Create an indicator space
"""
@staticmethod
@abstractmethod
def generate_roi_table(params: Dict) -> Dict[int, float]:
"""
Create an roi table
"""
@staticmethod
@abstractmethod
def stoploss_space() -> List[Dimension]:
"""
Create a stoploss space
"""
@staticmethod
@abstractmethod
def roi_space() -> List[Dimension]:
"""
Create a roi space
"""
+91
View File
@@ -0,0 +1,91 @@
"""
Static List provider
Provides lists as configured in config.json
"""
import logging
from abc import ABC, abstractmethod
from typing import List
logger = logging.getLogger(__name__)
class IPairList(ABC):
def __init__(self, freqtrade, config: dict) -> None:
self._freqtrade = freqtrade
self._config = config
self._whitelist = self._config['exchange']['pair_whitelist']
self._blacklist = self._config['exchange'].get('pair_blacklist', [])
@property
def name(self) -> str:
"""
Gets name of the class
-> no need to overwrite in subclasses
"""
return self.__class__.__name__
@property
def whitelist(self) -> List[str]:
"""
Has the current whitelist
-> no need to overwrite in subclasses
"""
return self._whitelist
@property
def blacklist(self) -> List[str]:
"""
Has the current blacklist
-> no need to overwrite in subclasses
"""
return self._blacklist
@abstractmethod
def short_desc(self) -> str:
"""
Short whitelist method description - used for startup-messages
-> Please overwrite in subclasses
"""
@abstractmethod
def refresh_pairlist(self) -> None:
"""
Refreshes pairlists and assigns them to self._whitelist and self._blacklist respectively
-> Please overwrite in subclasses
"""
def _validate_whitelist(self, whitelist: List[str]) -> List[str]:
"""
Check available markets and remove pair from whitelist if necessary
:param whitelist: the sorted list (based on BaseVolume) of pairs the user might want to
trade
:return: the list of pairs the user wants to trade without the one unavailable or
black_listed
"""
sanitized_whitelist = whitelist
markets = self._freqtrade.exchange.get_markets()
# Filter to markets in stake currency
markets = [m for m in markets if m['quote'] == self._config['stake_currency']]
known_pairs = set()
for market in markets:
pair = market['symbol']
# pair is not int the generated dynamic market, or in the blacklist ... ignore it
if pair not in whitelist or pair in self.blacklist:
continue
# else the pair is valid
known_pairs.add(pair)
# Market is not active
if not market['active']:
sanitized_whitelist.remove(pair)
logger.info(
'Ignoring %s from whitelist. Market is not active.',
pair
)
# We need to remove pairs that are unknown
return [x for x in sanitized_whitelist if x in known_pairs]
+30
View File
@@ -0,0 +1,30 @@
"""
Static List provider
Provides lists as configured in config.json
"""
import logging
from freqtrade.pairlist.IPairList import IPairList
logger = logging.getLogger(__name__)
class StaticPairList(IPairList):
def __init__(self, freqtrade, config: dict) -> None:
super().__init__(freqtrade, config)
def short_desc(self) -> str:
"""
Short whitelist method description - used for startup-messages
-> Please overwrite in subclasses
"""
return f"{self.name}: {self.whitelist}"
def refresh_pairlist(self) -> None:
"""
Refreshes pairlists and assigns them to self._whitelist and self._blacklist respectively
"""
self._whitelist = self._validate_whitelist(self._config['exchange']['pair_whitelist'])
+75
View File
@@ -0,0 +1,75 @@
"""
Static List provider
Provides lists as configured in config.json
"""
import logging
from typing import List
from cachetools import TTLCache, cached
from freqtrade.pairlist.IPairList import IPairList
from freqtrade import OperationalException
logger = logging.getLogger(__name__)
SORT_VALUES = ['askVolume', 'bidVolume', 'quoteVolume']
class VolumePairList(IPairList):
def __init__(self, freqtrade, config: dict) -> None:
super().__init__(freqtrade, config)
self._whitelistconf = self._config.get('pairlist', {}).get('config')
if 'number_assets' not in self._whitelistconf:
raise OperationalException(
f'`number_assets` not specified. Please check your configuration '
'for "pairlist.config.number_assets"')
self._number_pairs = self._whitelistconf['number_assets']
self._sort_key = self._whitelistconf.get('sort_key', 'quoteVolume')
if not self._freqtrade.exchange.exchange_has('fetchTickers'):
raise OperationalException(
'Exchange does not support dynamic whitelist.'
'Please edit your config and restart the bot'
)
if not self._validate_keys(self._sort_key):
raise OperationalException(
f'key {self._sort_key} not in {SORT_VALUES}')
def _validate_keys(self, key):
return key in SORT_VALUES
def short_desc(self) -> str:
"""
Short whitelist method description - used for startup-messages
-> Please overwrite in subclasses
"""
return f"{self.name} - top {self._whitelistconf['number_assets']} volume pairs."
def refresh_pairlist(self) -> None:
"""
Refreshes pairlists and assigns them to self._whitelist and self._blacklist respectively
-> Please overwrite in subclasses
"""
# Generate dynamic whitelist
pairs = self._gen_pair_whitelist(self._config['stake_currency'], self._sort_key)
# Validate whitelist to only have active market pairs
self._whitelist = self._validate_whitelist(pairs)[:self._number_pairs]
@cached(TTLCache(maxsize=1, ttl=1800))
def _gen_pair_whitelist(self, base_currency: str, key: str) -> List[str]:
"""
Updates the whitelist with with a dynamically generated list
:param base_currency: base currency as str
:param key: sort key (defaults to 'quoteVolume')
:return: List of pairs
"""
tickers = self._freqtrade.exchange.get_tickers()
# check length so that we make sure that '/' is actually in the string
tickers = [v for k, v in tickers.items()
if len(k.split('/')) == 2 and k.split('/')[1] == base_currency]
sorted_tickers = sorted(tickers, reverse=True, key=lambda t: t[key])
pairs = [s['symbol'] for s in sorted_tickers]
return pairs
View File
+297 -42
View File
@@ -1,45 +1,141 @@
"""
This module contains the class to persist trades into SQLite
"""
import logging
from datetime import datetime
from decimal import Decimal, getcontext
from typing import Optional, Dict
from decimal import Decimal
from typing import Any, Dict, Optional
import arrow
from sqlalchemy import Boolean, Column, DateTime, Float, Integer, String, create_engine
from sqlalchemy.engine import Engine
from sqlalchemy import (Boolean, Column, DateTime, Float, Integer, String,
create_engine, inspect)
from sqlalchemy.exc import NoSuchModuleError
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm.scoping import scoped_session
from sqlalchemy.orm.session import sessionmaker
from sqlalchemy import func
from sqlalchemy.pool import StaticPool
from freqtrade import OperationalException
logger = logging.getLogger(__name__)
_CONF = {}
_DECL_BASE = declarative_base()
_DECL_BASE: Any = declarative_base()
_SQL_DOCS_URL = 'http://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls'
def init(config: dict, engine: Optional[Engine] = None) -> None:
def init(config: Dict) -> None:
"""
Initializes this module with the given config,
registers all known command handlers
and starts polling for message updates
:param config: config to use
:param engine: database engine for sqlalchemy (Optional)
:return: None
"""
_CONF.update(config)
if not engine:
if _CONF.get('dry_run', False):
engine = create_engine('sqlite://',
connect_args={'check_same_thread': False},
poolclass=StaticPool,
echo=False)
else:
engine = create_engine('sqlite:///tradesv3.sqlite')
db_url = config.get('db_url', None)
kwargs = {}
# Take care of thread ownership if in-memory db
if db_url == 'sqlite://':
kwargs.update({
'connect_args': {'check_same_thread': False},
'poolclass': StaticPool,
'echo': False,
})
try:
engine = create_engine(db_url, **kwargs)
except NoSuchModuleError:
raise OperationalException(f'Given value for db_url: \'{db_url}\' '
f'is no valid database URL! (See {_SQL_DOCS_URL})')
session = scoped_session(sessionmaker(bind=engine, autoflush=True, autocommit=True))
Trade.session = session()
Trade.query = session.query_property()
_DECL_BASE.metadata.create_all(engine)
check_migrate(engine)
# Clean dry_run DB if the db is not in-memory
if config.get('dry_run', False) and db_url != 'sqlite://':
clean_dry_run_db()
def has_column(columns, searchname: str) -> bool:
return len(list(filter(lambda x: x["name"] == searchname, columns))) == 1
def get_column_def(columns, column: str, default: str) -> str:
return default if not has_column(columns, column) else column
def check_migrate(engine) -> None:
"""
Checks if migration is necessary and migrates if necessary
"""
inspector = inspect(engine)
cols = inspector.get_columns('trades')
tabs = inspector.get_table_names()
table_back_name = 'trades_bak'
for i, table_back_name in enumerate(tabs):
table_back_name = f'trades_bak{i}'
logger.debug(f'trying {table_back_name}')
# Check for latest column
if not has_column(cols, 'stoploss_order_id'):
logger.info(f'Running database migration - backup available as {table_back_name}')
fee_open = get_column_def(cols, 'fee_open', 'fee')
fee_close = get_column_def(cols, 'fee_close', 'fee')
open_rate_requested = get_column_def(cols, 'open_rate_requested', 'null')
close_rate_requested = get_column_def(cols, 'close_rate_requested', 'null')
stop_loss = get_column_def(cols, 'stop_loss', '0.0')
initial_stop_loss = get_column_def(cols, 'initial_stop_loss', '0.0')
stoploss_order_id = get_column_def(cols, 'stoploss_order_id', 'null')
max_rate = get_column_def(cols, 'max_rate', '0.0')
sell_reason = get_column_def(cols, 'sell_reason', 'null')
strategy = get_column_def(cols, 'strategy', 'null')
ticker_interval = get_column_def(cols, 'ticker_interval', 'null')
# Schema migration necessary
engine.execute(f"alter table trades rename to {table_back_name}")
# drop indexes on backup table
for index in inspector.get_indexes(table_back_name):
engine.execute(f"drop index {index['name']}")
# let SQLAlchemy create the schema as required
_DECL_BASE.metadata.create_all(engine)
# Copy data back - following the correct schema
engine.execute(f"""insert into trades
(id, exchange, pair, is_open, fee_open, fee_close, open_rate,
open_rate_requested, close_rate, close_rate_requested, close_profit,
stake_amount, amount, open_date, close_date, open_order_id,
stop_loss, initial_stop_loss, stoploss_order_id, max_rate, sell_reason, strategy,
ticker_interval
)
select id, lower(exchange),
case
when instr(pair, '_') != 0 then
substr(pair, instr(pair, '_') + 1) || '/' ||
substr(pair, 1, instr(pair, '_') - 1)
else pair
end
pair,
is_open, {fee_open} fee_open, {fee_close} fee_close,
open_rate, {open_rate_requested} open_rate_requested, close_rate,
{close_rate_requested} close_rate_requested, close_profit,
stake_amount, amount, open_date, close_date, open_order_id,
{stop_loss} stop_loss, {initial_stop_loss} initial_stop_loss,
{stoploss_order_id} stoploss_order_id, {max_rate} max_rate,
{sell_reason} sell_reason, {strategy} strategy,
{ticker_interval} ticker_interval
from {table_back_name}
""")
# Reread columns - the above recreated the table!
inspector = inspect(engine)
cols = inspector.get_columns('trades')
def cleanup() -> None:
@@ -50,31 +146,95 @@ def cleanup() -> None:
Trade.session.flush()
def clean_dry_run_db() -> None:
"""
Remove open_order_id from a Dry_run DB
:return: None
"""
for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all():
# Check we are updating only a dry_run order not a prod one
if 'dry_run' in trade.open_order_id:
trade.open_order_id = None
class Trade(_DECL_BASE):
"""
Class used to define a trade structure
"""
__tablename__ = 'trades'
id = Column(Integer, primary_key=True)
exchange = Column(String, nullable=False)
pair = Column(String, nullable=False)
is_open = Column(Boolean, nullable=False, default=True)
fee = Column(Float, nullable=False, default=0.0)
pair = Column(String, nullable=False, index=True)
is_open = Column(Boolean, nullable=False, default=True, index=True)
fee_open = Column(Float, nullable=False, default=0.0)
fee_close = Column(Float, nullable=False, default=0.0)
open_rate = Column(Float)
open_rate_requested = Column(Float)
close_rate = Column(Float)
close_rate_requested = Column(Float)
close_profit = Column(Float)
stake_amount = Column(Float, nullable=False)
amount = Column(Float)
open_date = Column(DateTime, nullable=False, default=datetime.utcnow)
close_date = Column(DateTime)
open_order_id = Column(String)
# absolute value of the stop loss
stop_loss = Column(Float, nullable=True, default=0.0)
# absolute value of the initial stop loss
initial_stop_loss = Column(Float, nullable=True, default=0.0)
# stoploss order id which is on exchange
stoploss_order_id = Column(String, nullable=True, index=True)
# absolute value of the highest reached price
max_rate = Column(Float, nullable=True, default=0.0)
sell_reason = Column(String, nullable=True)
strategy = Column(String, nullable=True)
ticker_interval = Column(Integer, nullable=True)
def __repr__(self):
return 'Trade(id={}, pair={}, amount={}, open_rate={}, open_since={})'.format(
self.id,
self.pair,
self.amount,
self.open_rate,
arrow.get(self.open_date).humanize() if self.is_open else 'closed'
)
open_since = arrow.get(self.open_date).humanize() if self.is_open else 'closed'
return (f'Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, '
f'open_rate={self.open_rate:.8f}, open_since={open_since})')
def adjust_stop_loss(self, current_price: float, stoploss: float, initial: bool = False):
"""this adjusts the stop loss to it's most recently observed setting"""
if initial and not (self.stop_loss is None or self.stop_loss == 0):
# Don't modify if called with initial and nothing to do
return
new_loss = float(current_price * (1 - abs(stoploss)))
# keeping track of the highest observed rate for this trade
if self.max_rate is None:
self.max_rate = current_price
else:
if current_price > self.max_rate:
self.max_rate = current_price
# no stop loss assigned yet
if not self.stop_loss:
logger.debug("assigning new stop loss")
self.stop_loss = new_loss
self.initial_stop_loss = new_loss
# evaluate if the stop loss needs to be updated
else:
if new_loss > self.stop_loss: # stop losses only walk up, never down!
self.stop_loss = new_loss
logger.debug("adjusted stop loss")
else:
logger.debug("keeping current stop loss")
logger.debug(
f"{self.pair} - current price {current_price:.8f}, "
f"bought at {self.open_rate:.8f} and calculated "
f"stop loss is at: {self.initial_stop_loss:.8f} initial "
f"stop at {self.stop_loss:.8f}. "
f"trailing stop loss saved us: "
f"{float(self.stop_loss) - float(self.initial_stop_loss):.8f} "
f"and max observed rate was {self.max_rate:.8f}")
def update(self, order: Dict) -> None:
"""
@@ -82,31 +242,126 @@ class Trade(_DECL_BASE):
:param order: order retrieved by exchange.get_order()
:return: None
"""
if not order['closed']:
order_type = order['type']
# Ignore open and cancelled orders
if order['status'] == 'open' or order['price'] is None:
return
logger.debug('Updating trade (id=%d) ...', self.id)
if order['type'] == 'LIMIT_BUY':
logger.info('Updating trade (id=%s) ...', self.id)
if order_type in ('market', 'limit') and order['side'] == 'buy':
# Update open rate and actual amount
self.open_rate = order['rate']
self.amount = order['amount']
elif order['type'] == 'LIMIT_SELL':
# Set close rate and set actual profit
self.close_rate = order['rate']
self.close_profit = self.calc_profit()
self.close_date = datetime.utcnow()
self.open_rate = Decimal(order['price'])
self.amount = Decimal(order['amount'])
logger.info('%s_BUY has been fulfilled for %s.', order_type.upper(), self)
self.open_order_id = None
elif order_type in ('market', 'limit') and order['side'] == 'sell':
self.close(order['price'])
logger.info('%s_SELL has been fulfilled for %s.', order_type.upper(), self)
elif order_type == 'stop_loss_limit':
self.stoploss_order_id = None
logger.info('STOP_LOSS_LIMIT is hit for %s.', self)
self.close(order['average'])
else:
raise ValueError('Unknown order type: {}'.format(order['type']))
raise ValueError(f'Unknown order type: {order_type}')
cleanup()
def close(self, rate: float) -> None:
"""
Sets close_rate to the given rate, calculates total profit
and marks trade as closed
"""
self.close_rate = Decimal(rate)
self.close_profit = self.calc_profit_percent()
self.close_date = datetime.utcnow()
self.is_open = False
self.open_order_id = None
logger.info(
'Marking %s as closed as the trade is fulfilled and found no open orders for it.',
self
)
def calc_profit(self, rate: Optional[float] = None) -> float:
def calc_open_trade_price(
self,
fee: Optional[float] = None) -> float:
"""
Calculate the open_rate including fee.
:param fee: fee to use on the open rate (optional).
If rate is not set self.fee will be used
:return: Price in of the open trade incl. Fees
"""
buy_trade = (Decimal(self.amount) * Decimal(self.open_rate))
fees = buy_trade * Decimal(fee or self.fee_open)
return float(buy_trade + fees)
def calc_close_trade_price(
self,
rate: Optional[float] = None,
fee: Optional[float] = None) -> float:
"""
Calculate the close_rate including fee
:param fee: fee to use on the close rate (optional).
If rate is not set self.fee will be used
:param rate: rate to compare with (optional).
If rate is not set self.close_rate will be used
:return: Price in BTC of the open trade
"""
if rate is None and not self.close_rate:
return 0.0
sell_trade = (Decimal(self.amount) * Decimal(rate or self.close_rate))
fees = sell_trade * Decimal(fee or self.fee_close)
return float(sell_trade - fees)
def calc_profit(
self,
rate: Optional[float] = None,
fee: Optional[float] = None) -> float:
"""
Calculate the absolute profit in stake currency between Close and Open trade
:param fee: fee to use on the close rate (optional).
If rate is not set self.fee will be used
:param rate: close rate to compare with (optional).
If rate is not set self.close_rate will be used
:return: profit in stake currency as float
"""
open_trade_price = self.calc_open_trade_price()
close_trade_price = self.calc_close_trade_price(
rate=(rate or self.close_rate),
fee=(fee or self.fee_close)
)
profit = close_trade_price - open_trade_price
return float(f"{profit:.8f}")
def calc_profit_percent(
self,
rate: Optional[float] = None,
fee: Optional[float] = None) -> float:
"""
Calculates the profit in percentage (including fee).
:param rate: rate to compare with (optional).
If rate is not set self.close_rate will be used
:param fee: fee to use on the close rate (optional).
:return: profit in percentage as float
"""
getcontext().prec = 8
return float((Decimal(rate or self.close_rate) - Decimal(self.open_rate))
/ Decimal(self.open_rate) - Decimal(self.fee))
open_trade_price = self.calc_open_trade_price()
close_trade_price = self.calc_close_trade_price(
rate=(rate or self.close_rate),
fee=(fee or self.fee_close)
)
profit_percent = (close_trade_price / open_trade_price) - 1
return float(f"{profit_percent:.8f}")
@staticmethod
def total_open_trades_stakes() -> float:
"""
Calculates total invested amount in open trades
in stake currency
"""
total_open_stake_amount = Trade.session.query(func.sum(Trade.stake_amount))\
.filter(Trade.is_open.is_(True))\
.scalar()
return total_open_stake_amount or 0
+4
View File
@@ -0,0 +1,4 @@
from freqtrade.resolvers.iresolver import IResolver # noqa: F401
from freqtrade.resolvers.hyperopt_resolver import HyperOptResolver # noqa: F401
from freqtrade.resolvers.pairlist_resolver import PairListResolver # noqa: F401
from freqtrade.resolvers.strategy_resolver import StrategyResolver # noqa: F401
+67
View File
@@ -0,0 +1,67 @@
# pragma pylint: disable=attribute-defined-outside-init
"""
This module load custom hyperopts
"""
import logging
from pathlib import Path
from typing import Optional, Dict
from freqtrade.constants import DEFAULT_HYPEROPT
from freqtrade.optimize.hyperopt_interface import IHyperOpt
from freqtrade.resolvers import IResolver
logger = logging.getLogger(__name__)
class HyperOptResolver(IResolver):
"""
This class contains all the logic to load custom hyperopt class
"""
__slots__ = ['hyperopt']
def __init__(self, config: Optional[Dict] = None) -> None:
"""
Load the custom class from config parameter
:param config: configuration dictionary or None
"""
config = config or {}
# Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt
hyperopt_name = config.get('hyperopt') or DEFAULT_HYPEROPT
self.hyperopt = self._load_hyperopt(hyperopt_name, extra_dir=config.get('hyperopt_path'))
def _load_hyperopt(
self, hyperopt_name: str, extra_dir: Optional[str] = None) -> IHyperOpt:
"""
Search and loads the specified hyperopt.
:param hyperopt_name: name of the module to import
:param extra_dir: additional directory to search for the given hyperopt
:return: HyperOpt instance or None
"""
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
abs_paths = [
current_path.parent.parent.joinpath('user_data/hyperopts'),
current_path,
]
if extra_dir:
# Add extra hyperopt directory on top of search paths
abs_paths.insert(0, Path(extra_dir))
for _path in abs_paths:
try:
hyperopt = self._search_object(directory=_path, object_type=IHyperOpt,
object_name=hyperopt_name)
if hyperopt:
logger.info('Using resolved hyperopt %s from \'%s\'', hyperopt_name, _path)
return hyperopt
except FileNotFoundError:
logger.warning('Path "%s" does not exist', _path.relative_to(Path.cwd()))
raise ImportError(
"Impossible to load Hyperopt '{}'. This class does not exist"
" or contains Python code errors".format(hyperopt_name)
)
+61
View File
@@ -0,0 +1,61 @@
# pragma pylint: disable=attribute-defined-outside-init
"""
This module load custom objects
"""
import importlib.util
import inspect
import logging
from pathlib import Path
from typing import Optional, Type, Any
logger = logging.getLogger(__name__)
class IResolver(object):
"""
This class contains all the logic to load custom classes
"""
@staticmethod
def _get_valid_object(object_type, module_path: Path,
object_name: str) -> Optional[Type[Any]]:
"""
Returns the first object with matching object_type and object_name in the path given.
:param object_type: object_type (class)
:param module_path: absolute path to the module
:param object_name: Class name of the object
:return: class or None
"""
# Generate spec based on absolute path
spec = importlib.util.spec_from_file_location('unknown', str(module_path))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) # type: ignore # importlib does not use typehints
valid_objects_gen = (
obj for name, obj in inspect.getmembers(module, inspect.isclass)
if object_name == name and object_type in obj.__bases__
)
return next(valid_objects_gen, None)
@staticmethod
def _search_object(directory: Path, object_type, object_name: str,
kwargs: dict = {}) -> Optional[Any]:
"""
Search for the objectname in the given directory
:param directory: relative or absolute directory path
:return: object instance
"""
logger.debug('Searching for %s %s in \'%s\'', object_type.__name__, object_name, directory)
for entry in directory.iterdir():
# Only consider python files
if not str(entry).endswith('.py'):
logger.debug('Ignoring %s', entry)
continue
obj = IResolver._get_valid_object(
object_type, Path.resolve(directory.joinpath(entry)), object_name
)
if obj:
return obj(**kwargs)
return None
+59
View File
@@ -0,0 +1,59 @@
# pragma pylint: disable=attribute-defined-outside-init
"""
This module load custom hyperopts
"""
import logging
from pathlib import Path
from freqtrade.pairlist.IPairList import IPairList
from freqtrade.resolvers import IResolver
logger = logging.getLogger(__name__)
class PairListResolver(IResolver):
"""
This class contains all the logic to load custom hyperopt class
"""
__slots__ = ['pairlist']
def __init__(self, pairlist_name: str, freqtrade, config: dict) -> None:
"""
Load the custom class from config parameter
:param config: configuration dictionary or None
"""
self.pairlist = self._load_pairlist(pairlist_name, kwargs={'freqtrade': freqtrade,
'config': config})
def _load_pairlist(
self, pairlist_name: str, kwargs: dict) -> IPairList:
"""
Search and loads the specified pairlist.
:param pairlist_name: name of the module to import
:param extra_dir: additional directory to search for the given pairlist
:return: PairList instance or None
"""
current_path = Path(__file__).parent.parent.joinpath('pairlist').resolve()
abs_paths = [
current_path.parent.parent.joinpath('user_data/pairlist'),
current_path,
]
for _path in abs_paths:
try:
pairlist = self._search_object(directory=_path, object_type=IPairList,
object_name=pairlist_name,
kwargs=kwargs)
if pairlist:
logger.info('Using resolved pairlist %s from \'%s\'', pairlist_name, _path)
return pairlist
except FileNotFoundError:
logger.warning('Path "%s" does not exist', _path.relative_to(Path.cwd()))
raise ImportError(
"Impossible to load Pairlist '{}'. This class does not exist"
" or contains Python code errors".format(pairlist_name)
)
+165
View File
@@ -0,0 +1,165 @@
# pragma pylint: disable=attribute-defined-outside-init
"""
This module load custom strategies
"""
import inspect
import logging
import tempfile
from base64 import urlsafe_b64decode
from collections import OrderedDict
from pathlib import Path
from typing import Dict, Optional
from freqtrade import constants
from freqtrade.resolvers import IResolver
from freqtrade.strategy import import_strategy
from freqtrade.strategy.interface import IStrategy
logger = logging.getLogger(__name__)
class StrategyResolver(IResolver):
"""
This class contains all the logic to load custom strategy class
"""
__slots__ = ['strategy']
def __init__(self, config: Optional[Dict] = None) -> None:
"""
Load the custom class from config parameter
:param config: configuration dictionary or None
"""
config = config or {}
# Verify the strategy is in the configuration, otherwise fallback to the default strategy
strategy_name = config.get('strategy') or constants.DEFAULT_STRATEGY
self.strategy: IStrategy = self._load_strategy(strategy_name,
config=config,
extra_dir=config.get('strategy_path'))
# Set attributes
# Check if we need to override configuration
if 'minimal_roi' in config:
self.strategy.minimal_roi = config['minimal_roi']
logger.info("Override strategy 'minimal_roi' with value in config file: %s.",
config['minimal_roi'])
else:
config['minimal_roi'] = self.strategy.minimal_roi
if 'stoploss' in config:
self.strategy.stoploss = config['stoploss']
logger.info(
"Override strategy 'stoploss' with value in config file: %s.", config['stoploss']
)
else:
config['stoploss'] = self.strategy.stoploss
if 'ticker_interval' in config:
self.strategy.ticker_interval = config['ticker_interval']
logger.info(
"Override strategy 'ticker_interval' with value in config file: %s.",
config['ticker_interval']
)
else:
config['ticker_interval'] = self.strategy.ticker_interval
if 'process_only_new_candles' in config:
self.strategy.process_only_new_candles = config['process_only_new_candles']
logger.info(
"Override process_only_new_candles 'process_only_new_candles' "
"with value in config file: %s.", config['process_only_new_candles']
)
else:
config['process_only_new_candles'] = self.strategy.process_only_new_candles
if 'order_types' in config:
self.strategy.order_types = config['order_types']
logger.info(
"Override strategy 'order_types' with value in config file: %s.",
config['order_types']
)
else:
config['order_types'] = self.strategy.order_types
if 'order_time_in_force' in config:
self.strategy.order_time_in_force = config['order_time_in_force']
logger.info(
"Override strategy 'order_time_in_force' with value in config file: %s.",
config['order_time_in_force']
)
else:
config['order_time_in_force'] = self.strategy.order_time_in_force
if not all(k in self.strategy.order_types for k in constants.REQUIRED_ORDERTYPES):
raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. "
f"Order-types mapping is incomplete.")
if not all(k in self.strategy.order_time_in_force for k in constants.REQUIRED_ORDERTIF):
raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. "
f"Order-time-in-force mapping is incomplete.")
# Sort and apply type conversions
self.strategy.minimal_roi = OrderedDict(sorted(
{int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),
key=lambda t: t[0]))
self.strategy.stoploss = float(self.strategy.stoploss)
def _load_strategy(
self, strategy_name: str, config: dict, extra_dir: Optional[str] = None) -> IStrategy:
"""
Search and loads the specified strategy.
:param strategy_name: name of the module to import
:param config: configuration for the strategy
:param extra_dir: additional directory to search for the given strategy
:return: Strategy instance or None
"""
current_path = Path(__file__).parent.parent.joinpath('strategy').resolve()
abs_paths = [
Path.cwd().joinpath('user_data/strategies'),
current_path,
]
if extra_dir:
# Add extra strategy directory on top of search paths
abs_paths.insert(0, Path(extra_dir).resolve())
if ":" in strategy_name:
logger.info("loading base64 endocded strategy")
strat = strategy_name.split(":")
if len(strat) == 2:
temp = Path(tempfile.mkdtemp("freq", "strategy"))
name = strat[0] + ".py"
temp.joinpath(name).write_text(urlsafe_b64decode(strat[1]).decode('utf-8'))
temp.joinpath("__init__.py").touch()
strategy_name = strat[0]
# register temp path with the bot
abs_paths.insert(0, temp.resolve())
for _path in abs_paths:
try:
strategy = self._search_object(directory=_path, object_type=IStrategy,
object_name=strategy_name, kwargs={'config': config})
if strategy:
logger.info('Using resolved strategy %s from \'%s\'', strategy_name, _path)
strategy._populate_fun_len = len(
inspect.getfullargspec(strategy.populate_indicators).args)
strategy._buy_fun_len = len(
inspect.getfullargspec(strategy.populate_buy_trend).args)
strategy._sell_fun_len = len(
inspect.getfullargspec(strategy.populate_sell_trend).args)
return import_strategy(strategy, config=config)
except FileNotFoundError:
logger.warning('Path "%s" does not exist', _path.relative_to(Path.cwd()))
raise ImportError(
"Impossible to load Strategy '{}'. This class does not exist"
" or contains Python code errors".format(strategy_name)
)
+2 -1
View File
@@ -1 +1,2 @@
from . import telegram
from .rpc import RPC, RPCMessageType, RPCException # noqa
from .rpc_manager import RPCManager # noqa
+206
View File
@@ -0,0 +1,206 @@
"""
Module that define classes to convert Crypto-currency to FIAT
e.g BTC to USD
"""
import logging
import time
from typing import Dict, List
from coinmarketcap import Market
from freqtrade.constants import SUPPORTED_FIAT
logger = logging.getLogger(__name__)
class CryptoFiat(object):
"""
Object to describe what is the price of Crypto-currency in a FIAT
"""
# Constants
CACHE_DURATION = 6 * 60 * 60 # 6 hours
def __init__(self, crypto_symbol: str, fiat_symbol: str, price: float) -> None:
"""
Create an object that will contains the price for a crypto-currency in fiat
:param crypto_symbol: Crypto-currency you want to convert (e.g BTC)
:param fiat_symbol: FIAT currency you want to convert to (e.g USD)
:param price: Price in FIAT
"""
# Public attributes
self.crypto_symbol = None
self.fiat_symbol = None
self.price = 0.0
# Private attributes
self._expiration = 0.0
self.crypto_symbol = crypto_symbol.upper()
self.fiat_symbol = fiat_symbol.upper()
self.set_price(price=price)
def set_price(self, price: float) -> None:
"""
Set the price of the Crypto-currency in FIAT and set the expiration time
:param price: Price of the current Crypto currency in the fiat
:return: None
"""
self.price = price
self._expiration = time.time() + self.CACHE_DURATION
def is_expired(self) -> bool:
"""
Return if the current price is still valid or needs to be refreshed
:return: bool, true the price is expired and needs to be refreshed, false the price is
still valid
"""
return self._expiration - time.time() <= 0
class CryptoToFiatConverter(object):
"""
Main class to initiate Crypto to FIAT.
This object contains a list of pair Crypto, FIAT
This object is also a Singleton
"""
__instance = None
_coinmarketcap: Market = None
_cryptomap: Dict = {}
def __new__(cls):
if CryptoToFiatConverter.__instance is None:
CryptoToFiatConverter.__instance = object.__new__(cls)
try:
CryptoToFiatConverter._coinmarketcap = Market()
except BaseException:
CryptoToFiatConverter._coinmarketcap = None
return CryptoToFiatConverter.__instance
def __init__(self) -> None:
self._pairs: List[CryptoFiat] = []
self._load_cryptomap()
def _load_cryptomap(self) -> None:
try:
coinlistings = self._coinmarketcap.listings()
self._cryptomap = dict(map(lambda coin: (coin["symbol"], str(coin["id"])),
coinlistings["data"]))
except (BaseException) as exception:
logger.error(
"Could not load FIAT Cryptocurrency map for the following problem: %s",
type(exception).__name__
)
def convert_amount(self, crypto_amount: float, crypto_symbol: str, fiat_symbol: str) -> float:
"""
Convert an amount of crypto-currency to fiat
:param crypto_amount: amount of crypto-currency to convert
:param crypto_symbol: crypto-currency used
:param fiat_symbol: fiat to convert to
:return: float, value in fiat of the crypto-currency amount
"""
if crypto_symbol == fiat_symbol:
return crypto_amount
price = self.get_price(crypto_symbol=crypto_symbol, fiat_symbol=fiat_symbol)
return float(crypto_amount) * float(price)
def get_price(self, crypto_symbol: str, fiat_symbol: str) -> float:
"""
Return the price of the Crypto-currency in Fiat
:param crypto_symbol: Crypto-currency you want to convert (e.g BTC)
:param fiat_symbol: FIAT currency you want to convert to (e.g USD)
:return: Price in FIAT
"""
crypto_symbol = crypto_symbol.upper()
fiat_symbol = fiat_symbol.upper()
# Check if the fiat convertion you want is supported
if not self._is_supported_fiat(fiat=fiat_symbol):
raise ValueError(f'The fiat {fiat_symbol} is not supported.')
# Get the pair that interest us and return the price in fiat
for pair in self._pairs:
if pair.crypto_symbol == crypto_symbol and pair.fiat_symbol == fiat_symbol:
# If the price is expired we refresh it, avoid to call the API all the time
if pair.is_expired():
pair.set_price(
price=self._find_price(
crypto_symbol=pair.crypto_symbol,
fiat_symbol=pair.fiat_symbol
)
)
# return the last price we have for this pair
return pair.price
# The pair does not exist, so we create it and return the price
return self._add_pair(
crypto_symbol=crypto_symbol,
fiat_symbol=fiat_symbol,
price=self._find_price(
crypto_symbol=crypto_symbol,
fiat_symbol=fiat_symbol
)
)
def _add_pair(self, crypto_symbol: str, fiat_symbol: str, price: float) -> float:
"""
:param crypto_symbol: Crypto-currency you want to convert (e.g BTC)
:param fiat_symbol: FIAT currency you want to convert to (e.g USD)
:return: price in FIAT
"""
self._pairs.append(
CryptoFiat(
crypto_symbol=crypto_symbol,
fiat_symbol=fiat_symbol,
price=price
)
)
return price
def _is_supported_fiat(self, fiat: str) -> bool:
"""
Check if the FIAT your want to convert to is supported
:param fiat: FIAT to check (e.g USD)
:return: bool, True supported, False not supported
"""
fiat = fiat.upper()
return fiat in SUPPORTED_FIAT
def _find_price(self, crypto_symbol: str, fiat_symbol: str) -> float:
"""
Call CoinMarketCap API to retrieve the price in the FIAT
:param crypto_symbol: Crypto-currency you want to convert (e.g BTC)
:param fiat_symbol: FIAT currency you want to convert to (e.g USD)
:return: float, price of the crypto-currency in Fiat
"""
# Check if the fiat convertion you want is supported
if not self._is_supported_fiat(fiat=fiat_symbol):
raise ValueError(f'The fiat {fiat_symbol} is not supported.')
# No need to convert if both crypto and fiat are the same
if crypto_symbol == fiat_symbol:
return 1.0
if crypto_symbol not in self._cryptomap:
# return 0 for unsupported stake currencies (fiat-convert should not break the bot)
logger.warning("unsupported crypto-symbol %s - returning 0.0", crypto_symbol)
return 0.0
try:
return float(
self._coinmarketcap.ticker(
currency=self._cryptomap[crypto_symbol],
convert=fiat_symbol
)['data']['quotes'][fiat_symbol.upper()]['price']
)
except BaseException as exception:
logger.error("Error in _find_price: %s", exception)
return 0.0
+453
View File
@@ -0,0 +1,453 @@
"""
This module contains class to define a RPC communications
"""
import logging
from abc import abstractmethod
from datetime import timedelta, datetime, date
from decimal import Decimal
from enum import Enum
from typing import Dict, Any, List, Optional
import arrow
import sqlalchemy as sql
from numpy import mean, nan_to_num, NAN
from pandas import DataFrame
from freqtrade import TemporaryError, DependencyException
from freqtrade.misc import shorten_date
from freqtrade.persistence import Trade
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
from freqtrade.state import State
from freqtrade.strategy.interface import SellType
logger = logging.getLogger(__name__)
class RPCMessageType(Enum):
STATUS_NOTIFICATION = 'status'
WARNING_NOTIFICATION = 'warning'
CUSTOM_NOTIFICATION = 'custom'
BUY_NOTIFICATION = 'buy'
SELL_NOTIFICATION = 'sell'
def __repr__(self):
return self.value
class RPCException(Exception):
"""
Should be raised with a rpc-formatted message in an _rpc_* method
if the required state is wrong, i.e.:
raise RPCException('*Status:* `no active trade`')
"""
def __init__(self, message: str) -> None:
super().__init__(self)
self.message = message
def __str__(self):
return self.message
class RPC(object):
"""
RPC class can be used to have extra feature, like bot data, and access to DB data
"""
# Bind _fiat_converter if needed in each RPC handler
_fiat_converter: Optional[CryptoToFiatConverter] = None
def __init__(self, freqtrade) -> None:
"""
Initializes all enabled rpc modules
:param freqtrade: Instance of a freqtrade bot
:return: None
"""
self._freqtrade = freqtrade
@property
def name(self) -> str:
""" Returns the lowercase name of the implementation """
return self.__class__.__name__.lower()
@abstractmethod
def cleanup(self) -> None:
""" Cleanup pending module resources """
@abstractmethod
def send_msg(self, msg: Dict[str, str]) -> None:
""" Sends a message to all registered rpc modules """
def _rpc_trade_status(self) -> List[Dict[str, Any]]:
"""
Below follows the RPC backend it is prefixed with rpc_ to raise awareness that it is
a remotely exposed function
"""
# Fetch open trade
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
if not trades:
raise RPCException('no active trade')
else:
results = []
for trade in trades:
order = None
if trade.open_order_id:
order = self._freqtrade.exchange.get_order(trade.open_order_id, trade.pair)
# calculate profit and send message to user
try:
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
except DependencyException:
current_rate = NAN
current_profit = trade.calc_profit_percent(current_rate)
fmt_close_profit = (f'{round(trade.close_profit * 100, 2):.2f}%'
if trade.close_profit else None)
results.append(dict(
trade_id=trade.id,
pair=trade.pair,
market_url=self._freqtrade.exchange.get_pair_detail_url(trade.pair),
date=arrow.get(trade.open_date),
open_rate=trade.open_rate,
close_rate=trade.close_rate,
current_rate=current_rate,
amount=round(trade.amount, 8),
close_profit=fmt_close_profit,
current_profit=round(current_profit * 100, 2),
open_order='({} {} rem={:.8f})'.format(
order['type'], order['side'], order['remaining']
) if order else None,
))
return results
def _rpc_status_table(self) -> DataFrame:
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
if not trades:
raise RPCException('no active order')
else:
trades_list = []
for trade in trades:
# calculate profit and send message to user
try:
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
except DependencyException:
current_rate = NAN
trade_perc = (100 * trade.calc_profit_percent(current_rate))
trades_list.append([
trade.id,
trade.pair,
shorten_date(arrow.get(trade.open_date).humanize(only_distance=True)),
f'{trade_perc:.2f}%'
])
columns = ['ID', 'Pair', 'Since', 'Profit']
df_statuses = DataFrame.from_records(trades_list, columns=columns)
df_statuses = df_statuses.set_index(columns[0])
return df_statuses
def _rpc_daily_profit(
self, timescale: int,
stake_currency: str, fiat_display_currency: str) -> List[List[Any]]:
today = datetime.utcnow().date()
profit_days: Dict[date, Dict] = {}
if not (isinstance(timescale, int) and timescale > 0):
raise RPCException('timescale must be an integer greater than 0')
for day in range(0, timescale):
profitday = today - timedelta(days=day)
trades = Trade.query \
.filter(Trade.is_open.is_(False)) \
.filter(Trade.close_date >= profitday)\
.filter(Trade.close_date < (profitday + timedelta(days=1)))\
.order_by(Trade.close_date)\
.all()
curdayprofit = sum(trade.calc_profit() for trade in trades)
profit_days[profitday] = {
'amount': f'{curdayprofit:.8f}',
'trades': len(trades)
}
return [
[
key,
'{value:.8f} {symbol}'.format(
value=float(value['amount']),
symbol=stake_currency
),
'{value:.3f} {symbol}'.format(
value=self._fiat_converter.convert_amount(
value['amount'],
stake_currency,
fiat_display_currency
) if self._fiat_converter else 0,
symbol=fiat_display_currency
),
'{value} trade{s}'.format(
value=value['trades'],
s='' if value['trades'] < 2 else 's'
),
]
for key, value in profit_days.items()
]
def _rpc_trade_statistics(
self, stake_currency: str, fiat_display_currency: str) -> Dict[str, Any]:
""" Returns cumulative profit statistics """
trades = Trade.query.order_by(Trade.id).all()
profit_all_coin = []
profit_all_percent = []
profit_closed_coin = []
profit_closed_percent = []
durations = []
for trade in trades:
current_rate: float = 0.0
if not trade.open_rate:
continue
if trade.close_date:
durations.append((trade.close_date - trade.open_date).total_seconds())
if not trade.is_open:
profit_percent = trade.calc_profit_percent()
profit_closed_coin.append(trade.calc_profit())
profit_closed_percent.append(profit_percent)
else:
# Get current rate
try:
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
except DependencyException:
current_rate = NAN
profit_percent = trade.calc_profit_percent(rate=current_rate)
profit_all_coin.append(
trade.calc_profit(rate=Decimal(trade.close_rate or current_rate))
)
profit_all_percent.append(profit_percent)
best_pair = Trade.session.query(
Trade.pair, sql.func.sum(Trade.close_profit).label('profit_sum')
).filter(Trade.is_open.is_(False)) \
.group_by(Trade.pair) \
.order_by(sql.text('profit_sum DESC')).first()
if not best_pair:
raise RPCException('no closed trade')
bp_pair, bp_rate = best_pair
# Prepare data to display
profit_closed_coin_sum = round(sum(profit_closed_coin), 8)
profit_closed_percent = round(nan_to_num(mean(profit_closed_percent)) * 100, 2)
profit_closed_fiat = self._fiat_converter.convert_amount(
profit_closed_coin_sum,
stake_currency,
fiat_display_currency
) if self._fiat_converter else 0
profit_all_coin_sum = round(sum(profit_all_coin), 8)
profit_all_percent = round(nan_to_num(mean(profit_all_percent)) * 100, 2)
profit_all_fiat = self._fiat_converter.convert_amount(
profit_all_coin_sum,
stake_currency,
fiat_display_currency
) if self._fiat_converter else 0
num = float(len(durations) or 1)
return {
'profit_closed_coin': profit_closed_coin_sum,
'profit_closed_percent': profit_closed_percent,
'profit_closed_fiat': profit_closed_fiat,
'profit_all_coin': profit_all_coin_sum,
'profit_all_percent': profit_all_percent,
'profit_all_fiat': profit_all_fiat,
'trade_count': len(trades),
'first_trade_date': arrow.get(trades[0].open_date).humanize(),
'latest_trade_date': arrow.get(trades[-1].open_date).humanize(),
'avg_duration': str(timedelta(seconds=sum(durations) / num)).split('.')[0],
'best_pair': bp_pair,
'best_rate': round(bp_rate * 100, 2),
}
def _rpc_balance(self, fiat_display_currency: str) -> Dict:
""" Returns current account balance per crypto """
output = []
total = 0.0
for coin, balance in self._freqtrade.exchange.get_balances().items():
if not balance['total']:
continue
if coin == 'BTC':
rate = 1.0
else:
try:
if coin == 'USDT':
rate = 1.0 / self._freqtrade.exchange.get_ticker('BTC/USDT', False)['bid']
else:
rate = self._freqtrade.exchange.get_ticker(coin + '/BTC', False)['bid']
except (TemporaryError, DependencyException):
continue
est_btc: float = rate * balance['total']
total = total + est_btc
output.append({
'currency': coin,
'available': balance['free'],
'balance': balance['total'],
'pending': balance['used'],
'est_btc': est_btc,
})
if total == 0.0:
raise RPCException('all balances are zero')
symbol = fiat_display_currency
value = self._fiat_converter.convert_amount(total, 'BTC',
symbol) if self._fiat_converter else 0
return {
'currencies': output,
'total': total,
'symbol': symbol,
'value': value,
}
def _rpc_start(self) -> Dict[str, str]:
""" Handler for start """
if self._freqtrade.state == State.RUNNING:
return {'status': 'already running'}
self._freqtrade.state = State.RUNNING
return {'status': 'starting trader ...'}
def _rpc_stop(self) -> Dict[str, str]:
""" Handler for stop """
if self._freqtrade.state == State.RUNNING:
self._freqtrade.state = State.STOPPED
return {'status': 'stopping trader ...'}
return {'status': 'already stopped'}
def _rpc_reload_conf(self) -> Dict[str, str]:
""" Handler for reload_conf. """
self._freqtrade.state = State.RELOAD_CONF
return {'status': 'reloading config ...'}
def _rpc_forcesell(self, trade_id) -> None:
"""
Handler for forcesell <id>.
Sells the given trade at current price
"""
def _exec_forcesell(trade: Trade) -> None:
# Check if there is there is an open order
if trade.open_order_id:
order = self._freqtrade.exchange.get_order(trade.open_order_id, trade.pair)
# Cancel open LIMIT_BUY orders and close trade
if order and order['status'] == 'open' \
and order['type'] == 'limit' \
and order['side'] == 'buy':
self._freqtrade.exchange.cancel_order(trade.open_order_id, trade.pair)
trade.close(order.get('price') or trade.open_rate)
# Do the best effort, if we don't know 'filled' amount, don't try selling
if order['filled'] is None:
return
trade.amount = order['filled']
# Ignore trades with an attached LIMIT_SELL order
if order and order['status'] == 'open' \
and order['type'] == 'limit' \
and order['side'] == 'sell':
return
# Get current rate and execute sell
current_rate = self._freqtrade.exchange.get_ticker(trade.pair, False)['bid']
self._freqtrade.execute_sell(trade, current_rate, SellType.FORCE_SELL)
# ---- EOF def _exec_forcesell ----
if self._freqtrade.state != State.RUNNING:
raise RPCException('trader is not running')
if trade_id == 'all':
# Execute sell for all open orders
for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
_exec_forcesell(trade)
Trade.session.flush()
return
# Query for trade
trade = Trade.query.filter(
sql.and_(
Trade.id == trade_id,
Trade.is_open.is_(True)
)
).first()
if not trade:
logger.warning('forcesell: Invalid argument received')
raise RPCException('invalid argument')
_exec_forcesell(trade)
Trade.session.flush()
def _rpc_forcebuy(self, pair: str, price: Optional[float]) -> Optional[Trade]:
"""
Handler for forcebuy <asset> <price>
Buys a pair trade at the given or current price
"""
if not self._freqtrade.config.get('forcebuy_enable', False):
raise RPCException('Forcebuy not enabled.')
if self._freqtrade.state != State.RUNNING:
raise RPCException('trader is not running')
# Check pair is in stake currency
stake_currency = self._freqtrade.config.get('stake_currency')
if not pair.endswith(stake_currency):
raise RPCException(
f'Wrong pair selected. Please pairs with stake {stake_currency} pairs only')
# check if valid pair
# check if pair already has an open pair
trade = Trade.query.filter(Trade.is_open.is_(True)).filter(Trade.pair.is_(pair)).first()
if trade:
raise RPCException(f'position for {pair} already open - id: {trade.id}')
# gen stake amount
stakeamount = self._freqtrade._get_trade_stake_amount(pair)
# execute buy
if self._freqtrade.execute_buy(pair, stakeamount, price):
trade = Trade.query.filter(Trade.is_open.is_(True)).filter(Trade.pair.is_(pair)).first()
return trade
else:
return None
def _rpc_performance(self) -> List[Dict]:
"""
Handler for performance.
Shows a performance statistic from finished trades
"""
pair_rates = Trade.session.query(Trade.pair,
sql.func.sum(Trade.close_profit).label('profit_sum'),
sql.func.count(Trade.pair).label('count')) \
.filter(Trade.is_open.is_(False)) \
.group_by(Trade.pair) \
.order_by(sql.text('profit_sum DESC')) \
.all()
return [
{'pair': pair, 'profit': round(rate * 100, 2), 'count': count}
for pair, rate, count in pair_rates
]
def _rpc_count(self) -> List[Trade]:
""" Returns the number of trades running """
if self._freqtrade.state != State.RUNNING:
raise RPCException('trader is not running')
return Trade.query.filter(Trade.is_open.is_(True)).all()
def _rpc_whitelist(self) -> Dict:
""" Returns the currently active whitelist"""
res = {'method': self._freqtrade.pairlists.name,
'length': len(self._freqtrade.pairlists.whitelist),
'whitelist': self._freqtrade.active_pair_whitelist
}
return res
+79
View File
@@ -0,0 +1,79 @@
"""
This module contains class to manage RPC communications (Telegram, Slack, ...)
"""
import logging
from typing import List, Dict, Any
from freqtrade.rpc import RPC, RPCMessageType
logger = logging.getLogger(__name__)
class RPCManager(object):
"""
Class to manage RPC objects (Telegram, Slack, ...)
"""
def __init__(self, freqtrade) -> None:
""" Initializes all enabled rpc modules """
self.registered_modules: List[RPC] = []
# Enable telegram
if freqtrade.config['telegram'].get('enabled', False):
logger.info('Enabling rpc.telegram ...')
from freqtrade.rpc.telegram import Telegram
self.registered_modules.append(Telegram(freqtrade))
# Enable Webhook
if freqtrade.config.get('webhook', {}).get('enabled', False):
logger.info('Enabling rpc.webhook ...')
from freqtrade.rpc.webhook import Webhook
self.registered_modules.append(Webhook(freqtrade))
def cleanup(self) -> None:
""" Stops all enabled rpc modules """
logger.info('Cleaning up rpc modules ...')
while self.registered_modules:
mod = self.registered_modules.pop()
logger.debug('Cleaning up rpc.%s ...', mod.name)
mod.cleanup()
del mod
def send_msg(self, msg: Dict[str, Any]) -> None:
"""
Send given message to all registered rpc modules.
A message consists of one or more key value pairs of strings.
e.g.:
{
'status': 'stopping bot'
}
"""
logger.info('Sending rpc message: %s', msg)
for mod in self.registered_modules:
logger.debug('Forwarding message to rpc.%s', mod.name)
mod.send_msg(msg)
def startup_messages(self, config, pairlist) -> None:
if config.get('dry_run', False):
self.send_msg({
'type': RPCMessageType.WARNING_NOTIFICATION,
'status': 'Dry run is enabled. All trades are simulated.'
})
stake_currency = config['stake_currency']
stake_amount = config['stake_amount']
minimal_roi = config['minimal_roi']
ticker_interval = config['ticker_interval']
exchange_name = config['exchange']['name']
strategy_name = config.get('strategy', '')
self.send_msg({
'type': RPCMessageType.CUSTOM_NOTIFICATION,
'status': f'*Exchange:* `{exchange_name}`\n'
f'*Stake per trade:* `{stake_amount} {stake_currency}`\n'
f'*Minimum ROI:* `{minimal_roi}`\n'
f'*Ticker Interval:* `{ticker_interval}`\n'
f'*Strategy:* `{strategy_name}`'
})
self.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': f'Searching for {stake_currency} pairs to buy and sell '
f'based on {pairlist.short_desc()}'
})
+512 -458
View File
@@ -1,487 +1,541 @@
import logging
import re
from datetime import timedelta
from typing import Callable, Any
from pandas import DataFrame
from tabulate import tabulate
# pragma pylint: disable=unused-argument, unused-variable, protected-access, invalid-name
import arrow
from sqlalchemy import and_, func, text
from telegram import ParseMode, Bot, Update
from telegram.error import NetworkError
"""
This module manage Telegram communication
"""
import logging
from typing import Any, Callable, Dict
from tabulate import tabulate
from telegram import Bot, ParseMode, ReplyKeyboardMarkup, Update
from telegram.error import NetworkError, TelegramError
from telegram.ext import CommandHandler, Updater
from freqtrade import exchange, __version__
from freqtrade.misc import get_state, State, update_state
from freqtrade.persistence import Trade
from freqtrade.__init__ import __version__
from freqtrade.rpc import RPC, RPCException, RPCMessageType
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
# Remove noisy log messages
logging.getLogger('requests.packages.urllib3').setLevel(logging.INFO)
logging.getLogger('telegram').setLevel(logging.INFO)
logger = logging.getLogger(__name__)
_UPDATER: Updater = None
_CONF = {}
logger.debug('Included module rpc.telegram ...')
def init(config: dict) -> None:
"""
Initializes this module with the given config,
registers all known command handlers
and starts polling for message updates
:param config: config to use
:return: None
"""
global _UPDATER
_CONF.update(config)
if not is_enabled():
return
_UPDATER = Updater(token=config['telegram']['token'], workers=0)
# Register command handler and start telegram message polling
handles = [
CommandHandler('status', _status),
CommandHandler('profit', _profit),
CommandHandler('balance', _balance),
CommandHandler('start', _start),
CommandHandler('stop', _stop),
CommandHandler('forcesell', _forcesell),
CommandHandler('performance', _performance),
CommandHandler('count', _count),
CommandHandler('help', _help),
CommandHandler('version', _version),
]
for handle in handles:
_UPDATER.dispatcher.add_handler(handle)
_UPDATER.start_polling(
clean=True,
bootstrap_retries=3,
timeout=30,
read_latency=60,
)
logger.info(
'rpc.telegram is listening for following commands: %s',
[h.command for h in handles]
)
def cleanup() -> None:
"""
Stops all running telegram threads.
:return: None
"""
if not is_enabled():
return
_UPDATER.stop()
def is_enabled() -> bool:
"""
Returns True if the telegram module is activated, False otherwise
"""
return bool(_CONF['telegram'].get('enabled', False))
def authorized_only(command_handler: Callable[[Bot, Update], None]) -> Callable[..., Any]:
def authorized_only(command_handler: Callable[[Any, Bot, Update], None]) -> Callable[..., Any]:
"""
Decorator to check if the message comes from the correct chat_id
:param command_handler: Telegram CommandHandler
:return: decorated function
"""
def wrapper(*args, **kwargs):
bot, update = kwargs.get('bot') or args[0], kwargs.get('update') or args[1]
def wrapper(self, *args, **kwargs):
""" Decorator logic """
update = kwargs.get('update') or args[1]
# Reject unauthorized messages
chat_id = int(_CONF['telegram']['chat_id'])
chat_id = int(self._config['telegram']['chat_id'])
if int(update.message.chat_id) != chat_id:
logger.info('Rejected unauthorized message from: %s', update.message.chat_id)
logger.info(
'Rejected unauthorized message from: %s',
update.message.chat_id
)
return wrapper
logger.info('Executing handler: %s for chat_id: %s', command_handler.__name__, chat_id)
logger.info(
'Executing handler: %s for chat_id: %s',
command_handler.__name__,
chat_id
)
try:
return command_handler(*args, **kwargs)
return command_handler(self, *args, **kwargs)
except BaseException:
logger.exception('Exception occurred within Telegram module')
return wrapper
@authorized_only
def _status(bot: Bot, update: Update) -> None:
"""
Handler for /status.
Returns the current TradeThread status
:param bot: telegram bot
:param update: message update
:return: None
"""
class Telegram(RPC):
""" This class handles all telegram communication """
# Check if additional parameters are passed
params = update.message.text.replace('/status', '').split(' ') \
if update.message.text else []
if 'table' in params:
_status_table(bot, update)
return
def __init__(self, freqtrade) -> None:
"""
Init the Telegram call, and init the super class RPC
:param freqtrade: Instance of a freqtrade bot
:return: None
"""
super().__init__(freqtrade)
# Fetch open trade
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
if get_state() != State.RUNNING:
send_msg('*Status:* `trader is not running`', bot=bot)
elif not trades:
send_msg('*Status:* `no active trade`', bot=bot)
else:
for trade in trades:
order = None
if trade.open_order_id:
order = exchange.get_order(trade.open_order_id)
# calculate profit and send message to user
current_rate = exchange.get_ticker(trade.pair)['bid']
current_profit = trade.calc_profit(current_rate)
fmt_close_profit = '{:.2f}%'.format(
round(trade.close_profit * 100, 2)
) if trade.close_profit else None
message = """
*Trade ID:* `{trade_id}`
*Current Pair:* [{pair}]({market_url})
*Open Since:* `{date}`
*Amount:* `{amount}`
*Open Rate:* `{open_rate:.8f}`
*Close Rate:* `{close_rate}`
*Current Rate:* `{current_rate:.8f}`
*Close Profit:* `{close_profit}`
*Current Profit:* `{current_profit:.2f}%`
*Open Order:* `{open_order}`
""".format(
trade_id=trade.id,
pair=trade.pair,
market_url=exchange.get_pair_detail_url(trade.pair),
date=arrow.get(trade.open_date).humanize(),
open_rate=trade.open_rate,
close_rate=trade.close_rate,
current_rate=current_rate,
amount=round(trade.amount, 8),
close_profit=fmt_close_profit,
current_profit=round(current_profit * 100, 2),
open_order='{} ({})'.format(
order['remaining'], order['type']
) if order else None,
)
send_msg(message, bot=bot)
self._updater: Updater = None
self._config = freqtrade.config
self._init()
if self._config.get('fiat_display_currency', None):
self._fiat_converter = CryptoToFiatConverter()
def _init(self) -> None:
"""
Initializes this module with the given config,
registers all known command handlers
and starts polling for message updates
"""
self._updater = Updater(token=self._config['telegram']['token'], workers=0)
@authorized_only
def _status_table(bot: Bot, update: Update) -> None:
"""
Handler for /status table.
Returns the current TradeThread status in table format
:param bot: telegram bot
:param update: message update
:return: None
"""
# Fetch open trade
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
if get_state() != State.RUNNING:
send_msg('*Status:* `trader is not running`', bot=bot)
elif not trades:
send_msg('*Status:* `no active order`', bot=bot)
else:
trades_list = []
for trade in trades:
# calculate profit and send message to user
current_rate = exchange.get_ticker(trade.pair)['bid']
trades_list.append([
trade.id,
trade.pair,
shorten_date(arrow.get(trade.open_date).humanize(only_distance=True)),
'{:.2f}'.format(100 * trade.calc_profit(current_rate))
])
columns = ['ID', 'Pair', 'Since', 'Profit']
df_statuses = DataFrame.from_records(trades_list, columns=columns)
df_statuses = df_statuses.set_index(columns[0])
message = tabulate(df_statuses, headers='keys', tablefmt='simple')
message = "<pre>{}</pre>".format(message)
send_msg(message, parse_mode=ParseMode.HTML)
@authorized_only
def _profit(bot: Bot, update: Update) -> None:
"""
Handler for /profit.
Returns a cumulative profit statistics.
:param bot: telegram bot
:param update: message update
:return: None
"""
trades = Trade.query.order_by(Trade.id).all()
profit_amounts = []
profits = []
durations = []
for trade in trades:
if not trade.open_rate:
continue
if trade.close_date:
durations.append((trade.close_date - trade.open_date).total_seconds())
if trade.close_profit:
profit = trade.close_profit
else:
# Get current rate
current_rate = exchange.get_ticker(trade.pair)['bid']
profit = trade.calc_profit(current_rate)
profit_amounts.append(profit * trade.stake_amount)
profits.append(profit)
best_pair = Trade.session.query(Trade.pair, func.sum(Trade.close_profit).label('profit_sum')) \
.filter(Trade.is_open.is_(False)) \
.group_by(Trade.pair) \
.order_by(text('profit_sum DESC')) \
.first()
if not best_pair:
send_msg('*Status:* `no closed trade`', bot=bot)
return
bp_pair, bp_rate = best_pair
markdown_msg = """
*ROI:* `{profit_btc:.8f} ({profit:.2f}%)`
*Trade Count:* `{trade_count}`
*First Trade opened:* `{first_trade_date}`
*Latest Trade opened:* `{latest_trade_date}`
*Avg. Duration:* `{avg_duration}`
*Best Performing:* `{best_pair}: {best_rate:.2f}%`
""".format(
profit_btc=round(sum(profit_amounts), 8),
profit=round(sum(profits) * 100, 2),
trade_count=len(trades),
first_trade_date=arrow.get(trades[0].open_date).humanize(),
latest_trade_date=arrow.get(trades[-1].open_date).humanize(),
avg_duration=str(timedelta(seconds=sum(durations) / float(len(durations)))).split('.')[0],
best_pair=bp_pair,
best_rate=round(bp_rate * 100, 2),
)
send_msg(markdown_msg, bot=bot)
@authorized_only
def _balance(bot: Bot, update: Update) -> None:
"""
Handler for /balance
Returns current account balance per crypto
"""
output = ''
balances = [
c for c in exchange.get_balances()
if c['Balance'] or c['Available'] or c['Pending']
]
if not balances:
output = '`All balances are zero.`'
for currency in balances:
output += """*Currency*: {Currency}
*Available*: {Available}
*Balance*: {Balance}
*Pending*: {Pending}
""".format(**currency)
send_msg(output)
@authorized_only
def _start(bot: Bot, update: Update) -> None:
"""
Handler for /start.
Starts TradeThread
:param bot: telegram bot
:param update: message update
:return: None
"""
if get_state() == State.RUNNING:
send_msg('*Status:* `already running`', bot=bot)
else:
update_state(State.RUNNING)
@authorized_only
def _stop(bot: Bot, update: Update) -> None:
"""
Handler for /stop.
Stops TradeThread
:param bot: telegram bot
:param update: message update
:return: None
"""
if get_state() == State.RUNNING:
send_msg('`Stopping trader ...`', bot=bot)
update_state(State.STOPPED)
else:
send_msg('*Status:* `already stopped`', bot=bot)
@authorized_only
def _forcesell(bot: Bot, update: Update) -> None:
"""
Handler for /forcesell <id>.
Sells the given trade at current price
:param bot: telegram bot
:param update: message update
:return: None
"""
if get_state() != State.RUNNING:
send_msg('`trader is not running`', bot=bot)
return
trade_id = update.message.text.replace('/forcesell', '').strip()
if trade_id == 'all':
# Execute sell for all open orders
for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
# Get current rate
current_rate = exchange.get_ticker(trade.pair)['bid']
from freqtrade.main import execute_sell
execute_sell(trade, current_rate)
return
# Query for trade
trade = Trade.query.filter(and_(
Trade.id == trade_id,
Trade.is_open.is_(True)
)).first()
if not trade:
send_msg('Invalid argument. See `/help` to view usage')
logger.warning('/forcesell: Invalid argument received')
return
# Get current rate
current_rate = exchange.get_ticker(trade.pair)['bid']
from freqtrade.main import execute_sell
execute_sell(trade, current_rate)
@authorized_only
def _performance(bot: Bot, update: Update) -> None:
"""
Handler for /performance.
Shows a performance statistic from finished trades
:param bot: telegram bot
:param update: message update
:return: None
"""
if get_state() != State.RUNNING:
send_msg('`trader is not running`', bot=bot)
return
pair_rates = Trade.session.query(Trade.pair, func.sum(Trade.close_profit).label('profit_sum')) \
.filter(Trade.is_open.is_(False)) \
.group_by(Trade.pair) \
.order_by(text('profit_sum DESC')) \
.all()
stats = '\n'.join('{index}.\t<code>{pair}\t{profit:.2f}%</code>'.format(
index=i + 1,
pair=pair,
profit=round(rate * 100, 2)
) for i, (pair, rate) in enumerate(pair_rates))
message = '<b>Performance:</b>\n{}'.format(stats)
logger.debug(message)
send_msg(message, parse_mode=ParseMode.HTML)
@authorized_only
def _count(bot: Bot, update: Update) -> None:
"""
Handler for /count.
Returns the number of trades running
:param bot: telegram bot
:param update: message update
:return: None
"""
if get_state() != State.RUNNING:
send_msg('`trader is not running`', bot=bot)
return
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
message = tabulate({
'current': [len(trades)],
'max': [_CONF['max_open_trades']]
}, headers=['current', 'max'], tablefmt='simple')
message = "<pre>{}</pre>".format(message)
logger.debug(message)
send_msg(message, parse_mode=ParseMode.HTML)
@authorized_only
def _help(bot: Bot, update: Update) -> None:
"""
Handler for /help.
Show commands of the bot
:param bot: telegram bot
:param update: message update
:return: None
"""
message = """
*/start:* `Starts the trader`
*/stop:* `Stops the trader`
*/status [table]:* `Lists all open trades`
*table :* `will display trades in a table`
*/profit:* `Lists cumulative profit from all finished trades`
*/forcesell <trade_id>|all:* `Instantly sells the given trade or all trades, regardless of profit`
*/performance:* `Show performance of each finished trade grouped by pair`
*/count:* `Show number of trades running compared to allowed number of trades`
*/balance:* `Show account balance per currency`
*/help:* `This help message`
*/version:* `Show version`
"""
send_msg(message, bot=bot)
@authorized_only
def _version(bot: Bot, update: Update) -> None:
"""
Handler for /version.
Show version information
:param bot: telegram bot
:param update: message update
:return: None
"""
send_msg('*Version:* `{}`'.format(__version__), bot=bot)
def shorten_date(date):
"""
Trim the date so it fits on small screens
"""
new_date = re.sub('seconds?', 'sec', date)
new_date = re.sub('minutes?', 'min', new_date)
new_date = re.sub('hours?', 'h', new_date)
new_date = re.sub('days?', 'd', new_date)
new_date = re.sub('^an?', '1', new_date)
return new_date
def send_msg(msg: str, bot: Bot = None, parse_mode: ParseMode = ParseMode.MARKDOWN) -> None:
"""
Send given markdown message
:param msg: message
:param bot: alternative bot
:param parse_mode: telegram parse mode
:return: None
"""
if not is_enabled():
return
bot = bot or _UPDATER.bot
try:
bot.send_message(_CONF['telegram']['chat_id'], msg, parse_mode=parse_mode)
except NetworkError as error:
# Sometimes the telegram server resets the current connection,
# if this is the case we send the message again.
logger.warning(
'Got Telegram NetworkError: %s! Trying one more time.',
error.message
# Register command handler and start telegram message polling
handles = [
CommandHandler('status', self._status),
CommandHandler('profit', self._profit),
CommandHandler('balance', self._balance),
CommandHandler('start', self._start),
CommandHandler('stop', self._stop),
CommandHandler('forcesell', self._forcesell),
CommandHandler('forcebuy', self._forcebuy),
CommandHandler('performance', self._performance),
CommandHandler('daily', self._daily),
CommandHandler('count', self._count),
CommandHandler('reload_conf', self._reload_conf),
CommandHandler('whitelist', self._whitelist),
CommandHandler('help', self._help),
CommandHandler('version', self._version),
]
for handle in handles:
self._updater.dispatcher.add_handler(handle)
self._updater.start_polling(
clean=True,
bootstrap_retries=-1,
timeout=30,
read_latency=60,
)
bot.send_message(_CONF['telegram']['chat_id'], msg, parse_mode=parse_mode)
logger.info(
'rpc.telegram is listening for following commands: %s',
[h.command for h in handles]
)
def cleanup(self) -> None:
"""
Stops all running telegram threads.
:return: None
"""
self._updater.stop()
def send_msg(self, msg: Dict[str, Any]) -> None:
""" Send a message to telegram channel """
if msg['type'] == RPCMessageType.BUY_NOTIFICATION:
if self._fiat_converter:
msg['stake_amount_fiat'] = self._fiat_converter.convert_amount(
msg['stake_amount'], msg['stake_currency'], msg['fiat_currency'])
else:
msg['stake_amount_fiat'] = 0
message = ("*{exchange}:* Buying [{pair}]({market_url})\n"
"with limit `{limit:.8f}\n"
"({stake_amount:.6f} {stake_currency}").format(**msg)
if msg.get('fiat_currency', None):
message += ",{stake_amount_fiat:.3f} {fiat_currency}".format(**msg)
message += ")`"
elif msg['type'] == RPCMessageType.SELL_NOTIFICATION:
msg['amount'] = round(msg['amount'], 8)
msg['profit_percent'] = round(msg['profit_percent'] * 100, 2)
message = ("*{exchange}:* Selling [{pair}]({market_url})\n"
"*Limit:* `{limit:.8f}`\n"
"*Amount:* `{amount:.8f}`\n"
"*Open Rate:* `{open_rate:.8f}`\n"
"*Current Rate:* `{current_rate:.8f}`\n"
"*Sell Reason:* `{sell_reason}`\n"
"*Profit:* `{profit_percent:.2f}%`").format(**msg)
# Check if all sell properties are available.
# This might not be the case if the message origin is triggered by /forcesell
if (all(prop in msg for prop in ['gain', 'fiat_currency', 'stake_currency'])
and self._fiat_converter):
msg['profit_fiat'] = self._fiat_converter.convert_amount(
msg['profit_amount'], msg['stake_currency'], msg['fiat_currency'])
message += ('` ({gain}: {profit_amount:.8f} {stake_currency}`'
'` / {profit_fiat:.3f} {fiat_currency})`').format(**msg)
elif msg['type'] == RPCMessageType.STATUS_NOTIFICATION:
message = '*Status:* `{status}`'.format(**msg)
elif msg['type'] == RPCMessageType.WARNING_NOTIFICATION:
message = '*Warning:* `{status}`'.format(**msg)
elif msg['type'] == RPCMessageType.CUSTOM_NOTIFICATION:
message = '{status}'.format(**msg)
else:
raise NotImplementedError('Unknown message type: {}'.format(msg['type']))
self._send_msg(message)
@authorized_only
def _status(self, bot: Bot, update: Update) -> None:
"""
Handler for /status.
Returns the current TradeThread status
:param bot: telegram bot
:param update: message update
:return: None
"""
# Check if additional parameters are passed
params = update.message.text.replace('/status', '').split(' ') \
if update.message.text else []
if 'table' in params:
self._status_table(bot, update)
return
try:
results = self._rpc_trade_status()
# pre format data
for result in results:
result['date'] = result['date'].humanize()
messages = [
"*Trade ID:* `{trade_id}`\n"
"*Current Pair:* [{pair}]({market_url})\n"
"*Open Since:* `{date}`\n"
"*Amount:* `{amount}`\n"
"*Open Rate:* `{open_rate:.8f}`\n"
"*Close Rate:* `{close_rate}`\n"
"*Current Rate:* `{current_rate:.8f}`\n"
"*Close Profit:* `{close_profit}`\n"
"*Current Profit:* `{current_profit:.2f}%`\n"
"*Open Order:* `{open_order}`".format(**result)
for result in results
]
for msg in messages:
self._send_msg(msg, bot=bot)
except RPCException as e:
self._send_msg(str(e), bot=bot)
@authorized_only
def _status_table(self, bot: Bot, update: Update) -> None:
"""
Handler for /status table.
Returns the current TradeThread status in table format
:param bot: telegram bot
:param update: message update
:return: None
"""
try:
df_statuses = self._rpc_status_table()
message = tabulate(df_statuses, headers='keys', tablefmt='simple')
self._send_msg(f"<pre>{message}</pre>", parse_mode=ParseMode.HTML)
except RPCException as e:
self._send_msg(str(e), bot=bot)
@authorized_only
def _daily(self, bot: Bot, update: Update) -> None:
"""
Handler for /daily <n>
Returns a daily profit (in BTC) over the last n days.
:param bot: telegram bot
:param update: message update
:return: None
"""
stake_cur = self._config['stake_currency']
fiat_disp_cur = self._config.get('fiat_display_currency', '')
try:
timescale = int(update.message.text.replace('/daily', '').strip())
except (TypeError, ValueError):
timescale = 7
try:
stats = self._rpc_daily_profit(
timescale,
stake_cur,
fiat_disp_cur
)
stats = tabulate(stats,
headers=[
'Day',
f'Profit {stake_cur}',
f'Profit {fiat_disp_cur}'
],
tablefmt='simple')
message = f'<b>Daily Profit over the last {timescale} days</b>:\n<pre>{stats}</pre>'
self._send_msg(message, bot=bot, parse_mode=ParseMode.HTML)
except RPCException as e:
self._send_msg(str(e), bot=bot)
@authorized_only
def _profit(self, bot: Bot, update: Update) -> None:
"""
Handler for /profit.
Returns a cumulative profit statistics.
:param bot: telegram bot
:param update: message update
:return: None
"""
stake_cur = self._config['stake_currency']
fiat_disp_cur = self._config.get('fiat_display_currency', '')
try:
stats = self._rpc_trade_statistics(
stake_cur,
fiat_disp_cur)
profit_closed_coin = stats['profit_closed_coin']
profit_closed_percent = stats['profit_closed_percent']
profit_closed_fiat = stats['profit_closed_fiat']
profit_all_coin = stats['profit_all_coin']
profit_all_percent = stats['profit_all_percent']
profit_all_fiat = stats['profit_all_fiat']
trade_count = stats['trade_count']
first_trade_date = stats['first_trade_date']
latest_trade_date = stats['latest_trade_date']
avg_duration = stats['avg_duration']
best_pair = stats['best_pair']
best_rate = stats['best_rate']
# Message to display
markdown_msg = "*ROI:* Close trades\n" \
f"∙ `{profit_closed_coin:.8f} {stake_cur} "\
f"({profit_closed_percent:.2f}%)`\n" \
f"∙ `{profit_closed_fiat:.3f} {fiat_disp_cur}`\n" \
f"*ROI:* All trades\n" \
f"∙ `{profit_all_coin:.8f} {stake_cur} ({profit_all_percent:.2f}%)`\n" \
f"∙ `{profit_all_fiat:.3f} {fiat_disp_cur}`\n" \
f"*Total Trade Count:* `{trade_count}`\n" \
f"*First Trade opened:* `{first_trade_date}`\n" \
f"*Latest Trade opened:* `{latest_trade_date}`\n" \
f"*Avg. Duration:* `{avg_duration}`\n" \
f"*Best Performing:* `{best_pair}: {best_rate:.2f}%`"
self._send_msg(markdown_msg, bot=bot)
except RPCException as e:
self._send_msg(str(e), bot=bot)
@authorized_only
def _balance(self, bot: Bot, update: Update) -> None:
""" Handler for /balance """
try:
result = self._rpc_balance(self._config.get('fiat_display_currency', ''))
output = ''
for currency in result['currencies']:
if currency['est_btc'] > 0.0001:
output += "*{currency}:*\n" \
"\t`Available: {available: .8f}`\n" \
"\t`Balance: {balance: .8f}`\n" \
"\t`Pending: {pending: .8f}`\n" \
"\t`Est. BTC: {est_btc: .8f}`\n".format(**currency)
else:
output += "*{currency}:* not showing <1$ amount \n".format(**currency)
output += "\n*Estimated Value*:\n" \
"\t`BTC: {total: .8f}`\n" \
"\t`{symbol}: {value: .2f}`\n".format(**result)
self._send_msg(output, bot=bot)
except RPCException as e:
self._send_msg(str(e), bot=bot)
@authorized_only
def _start(self, bot: Bot, update: Update) -> None:
"""
Handler for /start.
Starts TradeThread
:param bot: telegram bot
:param update: message update
:return: None
"""
msg = self._rpc_start()
self._send_msg('Status: `{status}`'.format(**msg), bot=bot)
@authorized_only
def _stop(self, bot: Bot, update: Update) -> None:
"""
Handler for /stop.
Stops TradeThread
:param bot: telegram bot
:param update: message update
:return: None
"""
msg = self._rpc_stop()
self._send_msg('Status: `{status}`'.format(**msg), bot=bot)
@authorized_only
def _reload_conf(self, bot: Bot, update: Update) -> None:
"""
Handler for /reload_conf.
Triggers a config file reload
:param bot: telegram bot
:param update: message update
:return: None
"""
msg = self._rpc_reload_conf()
self._send_msg('Status: `{status}`'.format(**msg), bot=bot)
@authorized_only
def _forcesell(self, bot: Bot, update: Update) -> None:
"""
Handler for /forcesell <id>.
Sells the given trade at current price
:param bot: telegram bot
:param update: message update
:return: None
"""
trade_id = update.message.text.replace('/forcesell', '').strip()
try:
self._rpc_forcesell(trade_id)
except RPCException as e:
self._send_msg(str(e), bot=bot)
@authorized_only
def _forcebuy(self, bot: Bot, update: Update) -> None:
"""
Handler for /forcebuy <asset> <price>.
Buys a pair trade at the given or current price
:param bot: telegram bot
:param update: message update
:return: None
"""
message = update.message.text.replace('/forcebuy', '').strip().split()
pair = message[0]
price = float(message[1]) if len(message) > 1 else None
try:
self._rpc_forcebuy(pair, price)
except RPCException as e:
self._send_msg(str(e), bot=bot)
@authorized_only
def _performance(self, bot: Bot, update: Update) -> None:
"""
Handler for /performance.
Shows a performance statistic from finished trades
:param bot: telegram bot
:param update: message update
:return: None
"""
try:
trades = self._rpc_performance()
stats = '\n'.join('{index}.\t<code>{pair}\t{profit:.2f}% ({count})</code>'.format(
index=i + 1,
pair=trade['pair'],
profit=trade['profit'],
count=trade['count']
) for i, trade in enumerate(trades))
message = '<b>Performance:</b>\n{}'.format(stats)
self._send_msg(message, parse_mode=ParseMode.HTML)
except RPCException as e:
self._send_msg(str(e), bot=bot)
@authorized_only
def _count(self, bot: Bot, update: Update) -> None:
"""
Handler for /count.
Returns the number of trades running
:param bot: telegram bot
:param update: message update
:return: None
"""
try:
trades = self._rpc_count()
message = tabulate({
'current': [len(trades)],
'max': [self._config['max_open_trades']],
'total stake': [sum((trade.open_rate * trade.amount) for trade in trades)]
}, headers=['current', 'max', 'total stake'], tablefmt='simple')
message = "<pre>{}</pre>".format(message)
logger.debug(message)
self._send_msg(message, parse_mode=ParseMode.HTML)
except RPCException as e:
self._send_msg(str(e), bot=bot)
@authorized_only
def _whitelist(self, bot: Bot, update: Update) -> None:
"""
Handler for /whitelist
Shows the currently active whitelist
"""
try:
whitelist = self._rpc_whitelist()
message = f"Using whitelist `{whitelist['method']}` with {whitelist['length']} pairs\n"
message += f"`{', '.join(whitelist['whitelist'])}`"
logger.debug(message)
self._send_msg(message)
except RPCException as e:
self._send_msg(str(e), bot=bot)
@authorized_only
def _help(self, bot: Bot, update: Update) -> None:
"""
Handler for /help.
Show commands of the bot
:param bot: telegram bot
:param update: message update
:return: None
"""
message = "*/start:* `Starts the trader`\n" \
"*/stop:* `Stops the trader`\n" \
"*/status [table]:* `Lists all open trades`\n" \
" *table :* `will display trades in a table`\n" \
"*/profit:* `Lists cumulative profit from all finished trades`\n" \
"*/forcesell <trade_id>|all:* `Instantly sells the given trade or all trades, " \
"regardless of profit`\n" \
"*/performance:* `Show performance of each finished trade grouped by pair`\n" \
"*/daily <n>:* `Shows profit or loss per day, over the last n days`\n" \
"*/count:* `Show number of trades running compared to allowed number of trades`" \
"\n" \
"*/balance:* `Show account balance per currency`\n" \
"*/reload_conf:* `Reload configuration file` \n" \
"*/whitelist:* `Show current whitelist` \n" \
"*/help:* `This help message`\n" \
"*/version:* `Show version`"
self._send_msg(message, bot=bot)
@authorized_only
def _version(self, bot: Bot, update: Update) -> None:
"""
Handler for /version.
Show version information
:param bot: telegram bot
:param update: message update
:return: None
"""
self._send_msg('*Version:* `{}`'.format(__version__), bot=bot)
def _send_msg(self, msg: str, bot: Bot = None,
parse_mode: ParseMode = ParseMode.MARKDOWN) -> None:
"""
Send given markdown message
:param msg: message
:param bot: alternative bot
:param parse_mode: telegram parse mode
:return: None
"""
bot = bot or self._updater.bot
keyboard = [['/daily', '/profit', '/balance'],
['/status', '/status table', '/performance'],
['/count', '/start', '/stop', '/help']]
reply_markup = ReplyKeyboardMarkup(keyboard)
try:
try:
bot.send_message(
self._config['telegram']['chat_id'],
text=msg,
parse_mode=parse_mode,
reply_markup=reply_markup
)
except NetworkError as network_err:
# Sometimes the telegram server resets the current connection,
# if this is the case we send the message again.
logger.warning(
'Telegram NetworkError: %s! Trying one more time.',
network_err.message
)
bot.send_message(
self._config['telegram']['chat_id'],
text=msg,
parse_mode=parse_mode,
reply_markup=reply_markup
)
except TelegramError as telegram_err:
logger.warning(
'TelegramError: %s! Giving up on that message.',
telegram_err.message
)
+66
View File
@@ -0,0 +1,66 @@
"""
This module manages webhook communication
"""
import logging
from typing import Any, Dict
from requests import post, RequestException
from freqtrade.rpc import RPC, RPCMessageType
logger = logging.getLogger(__name__)
logger.debug('Included module rpc.webhook ...')
class Webhook(RPC):
""" This class handles all webhook communication """
def __init__(self, freqtrade) -> None:
"""
Init the Webhook class, and init the super class RPC
:param freqtrade: Instance of a freqtrade bot
:return: None
"""
super().__init__(freqtrade)
self._config = freqtrade.config
self._url = self._config['webhook']['url']
def cleanup(self) -> None:
"""
Cleanup pending module resources.
This will do nothing for webhooks, they will simply not be called anymore
"""
pass
def send_msg(self, msg: Dict[str, Any]) -> None:
""" Send a message to telegram channel """
try:
if msg['type'] == RPCMessageType.BUY_NOTIFICATION:
valuedict = self._config['webhook'].get('webhookbuy', None)
elif msg['type'] == RPCMessageType.SELL_NOTIFICATION:
valuedict = self._config['webhook'].get('webhooksell', None)
elif msg['type'] == RPCMessageType.STATUS_NOTIFICATION:
valuedict = self._config['webhook'].get('webhookstatus', None)
else:
raise NotImplementedError('Unknown message type: {}'.format(msg['type']))
if not valuedict:
logger.info("Message type %s not configured for webhooks", msg['type'])
return
payload = {key: value.format(**msg) for (key, value) in valuedict.items()}
self._send_msg(payload)
except KeyError as exc:
logger.exception("Problem calling Webhook. Please check your webhook configuration. "
"Exception: %s", exc)
def _send_msg(self, payload: dict) -> None:
"""do the actual call to the webhook"""
try:
post(self._url, data=payload)
except RequestException as exc:
logger.warning("Could not call webhook url. Exception: %s", exc)
+15
View File
@@ -0,0 +1,15 @@
# pragma pylint: disable=too-few-public-methods
"""
Bot state constant
"""
import enum
class State(enum.Enum):
"""
Bot application states
"""
RUNNING = 0
STOPPED = 1
RELOAD_CONF = 2
+44
View File
@@ -0,0 +1,44 @@
import logging
import sys
from copy import deepcopy
from freqtrade.strategy.interface import IStrategy
# Import Default-Strategy to have hyperopt correctly resolve
from freqtrade.strategy.default_strategy import DefaultStrategy # noqa: F401
logger = logging.getLogger(__name__)
def import_strategy(strategy: IStrategy, config: dict) -> IStrategy:
"""
Imports given Strategy instance to global scope
of freqtrade.strategy and returns an instance of it
"""
# Copy all attributes from base class and class
comb = {**strategy.__class__.__dict__, **strategy.__dict__}
# Delete '_abc_impl' from dict as deepcopy fails on 3.7 with
# `TypeError: can't pickle _abc_data objects``
# This will only apply to python 3.7
if sys.version_info.major == 3 and sys.version_info.minor == 7 and '_abc_impl' in comb:
del comb['_abc_impl']
attr = deepcopy(comb)
# Adjust module name
attr['__module__'] = 'freqtrade.strategy'
name = strategy.__class__.__name__
clazz = type(name, (IStrategy,), attr)
logger.debug(
'Imported strategy %s.%s as %s.%s',
strategy.__module__, strategy.__class__.__name__,
clazz.__module__, strategy.__class__.__name__,
)
# Modify global scope to declare class
globals()[name] = clazz
return clazz(config)
+259
View File
@@ -0,0 +1,259 @@
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
import talib.abstract as ta
from pandas import DataFrame
import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.indicator_helpers import fishers_inverse
from freqtrade.strategy.interface import IStrategy
class DefaultStrategy(IStrategy):
"""
Default Strategy provided by freqtrade bot.
You can override it with your own strategy
"""
# Minimal ROI designed for the strategy
minimal_roi = {
"40": 0.0,
"30": 0.01,
"20": 0.02,
"0": 0.04
}
# Optimal stoploss designed for the strategy
stoploss = -0.10
# Optimal ticker interval for the strategy
ticker_interval = '5m'
# Optional order type mapping
order_types = {
'buy': 'limit',
'sell': 'limit',
'stoploss': 'limit',
'stoploss_on_exchange': False
}
# Optional time in force for orders
order_time_in_force = {
'buy': 'gtc',
'sell': 'gtc',
}
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Adds several different TA indicators to the given DataFrame
Performance Note: For the best performance be frugal on the number of indicators
you are using. Let uncomment only the indicator you are using in your strategies
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe()
:param metadata: Additional information, like the currently traded pair
:return: a Dataframe with all mandatory indicators for the strategies
"""
# Momentum Indicator
# ------------------------------------
# ADX
dataframe['adx'] = ta.ADX(dataframe)
# Awesome oscillator
dataframe['ao'] = qtpylib.awesome_oscillator(dataframe)
"""
# Commodity Channel Index: values Oversold:<-100, Overbought:>100
dataframe['cci'] = ta.CCI(dataframe)
"""
# MACD
macd = ta.MACD(dataframe)
dataframe['macd'] = macd['macd']
dataframe['macdsignal'] = macd['macdsignal']
dataframe['macdhist'] = macd['macdhist']
# MFI
dataframe['mfi'] = ta.MFI(dataframe)
# Minus Directional Indicator / Movement
dataframe['minus_dm'] = ta.MINUS_DM(dataframe)
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
# Plus Directional Indicator / Movement
dataframe['plus_dm'] = ta.PLUS_DM(dataframe)
dataframe['plus_di'] = ta.PLUS_DI(dataframe)
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
"""
# ROC
dataframe['roc'] = ta.ROC(dataframe)
"""
# RSI
dataframe['rsi'] = ta.RSI(dataframe)
# Inverse Fisher transform on RSI, values [-1.0, 1.0] (https://goo.gl/2JGGoy)
dataframe['fisher_rsi'] = fishers_inverse(dataframe['rsi'])
# Inverse Fisher transform on RSI normalized, value [0.0, 100.0] (https://goo.gl/2JGGoy)
dataframe['fisher_rsi_norma'] = 50 * (dataframe['fisher_rsi'] + 1)
# Stoch
stoch = ta.STOCH(dataframe)
dataframe['slowd'] = stoch['slowd']
dataframe['slowk'] = stoch['slowk']
# Stoch fast
stoch_fast = ta.STOCHF(dataframe)
dataframe['fastd'] = stoch_fast['fastd']
dataframe['fastk'] = stoch_fast['fastk']
"""
# Stoch RSI
stoch_rsi = ta.STOCHRSI(dataframe)
dataframe['fastd_rsi'] = stoch_rsi['fastd']
dataframe['fastk_rsi'] = stoch_rsi['fastk']
"""
# Overlap Studies
# ------------------------------------
# Previous Bollinger bands
# Because ta.BBANDS implementation is broken with small numbers, it actually
# returns middle band for all the three bands. Switch to qtpylib.bollinger_bands
# and use middle band instead.
dataframe['blower'] = ta.BBANDS(dataframe, nbdevup=2, nbdevdn=2)['lowerband']
# Bollinger bands
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
dataframe['bb_lowerband'] = bollinger['lower']
dataframe['bb_middleband'] = bollinger['mid']
dataframe['bb_upperband'] = bollinger['upper']
# EMA - Exponential Moving Average
dataframe['ema3'] = ta.EMA(dataframe, timeperiod=3)
dataframe['ema5'] = ta.EMA(dataframe, timeperiod=5)
dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
dataframe['ema50'] = ta.EMA(dataframe, timeperiod=50)
dataframe['ema100'] = ta.EMA(dataframe, timeperiod=100)
# SAR Parabol
dataframe['sar'] = ta.SAR(dataframe)
# SMA - Simple Moving Average
dataframe['sma'] = ta.SMA(dataframe, timeperiod=40)
# TEMA - Triple Exponential Moving Average
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
# Cycle Indicator
# ------------------------------------
# Hilbert Transform Indicator - SineWave
hilbert = ta.HT_SINE(dataframe)
dataframe['htsine'] = hilbert['sine']
dataframe['htleadsine'] = hilbert['leadsine']
# Pattern Recognition - Bullish candlestick patterns
# ------------------------------------
"""
# Hammer: values [0, 100]
dataframe['CDLHAMMER'] = ta.CDLHAMMER(dataframe)
# Inverted Hammer: values [0, 100]
dataframe['CDLINVERTEDHAMMER'] = ta.CDLINVERTEDHAMMER(dataframe)
# Dragonfly Doji: values [0, 100]
dataframe['CDLDRAGONFLYDOJI'] = ta.CDLDRAGONFLYDOJI(dataframe)
# Piercing Line: values [0, 100]
dataframe['CDLPIERCING'] = ta.CDLPIERCING(dataframe) # values [0, 100]
# Morningstar: values [0, 100]
dataframe['CDLMORNINGSTAR'] = ta.CDLMORNINGSTAR(dataframe) # values [0, 100]
# Three White Soldiers: values [0, 100]
dataframe['CDL3WHITESOLDIERS'] = ta.CDL3WHITESOLDIERS(dataframe) # values [0, 100]
"""
# Pattern Recognition - Bearish candlestick patterns
# ------------------------------------
"""
# Hanging Man: values [0, 100]
dataframe['CDLHANGINGMAN'] = ta.CDLHANGINGMAN(dataframe)
# Shooting Star: values [0, 100]
dataframe['CDLSHOOTINGSTAR'] = ta.CDLSHOOTINGSTAR(dataframe)
# Gravestone Doji: values [0, 100]
dataframe['CDLGRAVESTONEDOJI'] = ta.CDLGRAVESTONEDOJI(dataframe)
# Dark Cloud Cover: values [0, 100]
dataframe['CDLDARKCLOUDCOVER'] = ta.CDLDARKCLOUDCOVER(dataframe)
# Evening Doji Star: values [0, 100]
dataframe['CDLEVENINGDOJISTAR'] = ta.CDLEVENINGDOJISTAR(dataframe)
# Evening Star: values [0, 100]
dataframe['CDLEVENINGSTAR'] = ta.CDLEVENINGSTAR(dataframe)
"""
# Pattern Recognition - Bullish/Bearish candlestick patterns
# ------------------------------------
"""
# Three Line Strike: values [0, -100, 100]
dataframe['CDL3LINESTRIKE'] = ta.CDL3LINESTRIKE(dataframe)
# Spinning Top: values [0, -100, 100]
dataframe['CDLSPINNINGTOP'] = ta.CDLSPINNINGTOP(dataframe) # values [0, -100, 100]
# Engulfing: values [0, -100, 100]
dataframe['CDLENGULFING'] = ta.CDLENGULFING(dataframe) # values [0, -100, 100]
# Harami: values [0, -100, 100]
dataframe['CDLHARAMI'] = ta.CDLHARAMI(dataframe) # values [0, -100, 100]
# Three Outside Up/Down: values [0, -100, 100]
dataframe['CDL3OUTSIDE'] = ta.CDL3OUTSIDE(dataframe) # values [0, -100, 100]
# Three Inside Up/Down: values [0, -100, 100]
dataframe['CDL3INSIDE'] = ta.CDL3INSIDE(dataframe) # values [0, -100, 100]
"""
# Chart type
# ------------------------------------
# Heikinashi stategy
heikinashi = qtpylib.heikinashi(dataframe)
dataframe['ha_open'] = heikinashi['open']
dataframe['ha_close'] = heikinashi['close']
dataframe['ha_high'] = heikinashi['high']
dataframe['ha_low'] = heikinashi['low']
return dataframe
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the buy signal for the given dataframe
:param dataframe: DataFrame
:param metadata: Additional information, like the currently traded pair
:return: DataFrame with buy column
"""
dataframe.loc[
(
(dataframe['rsi'] < 35) &
(dataframe['fastd'] < 35) &
(dataframe['adx'] > 30) &
(dataframe['plus_di'] > 0.5)
) |
(
(dataframe['adx'] > 65) &
(dataframe['plus_di'] > 0.5)
),
'buy'] = 1
return dataframe
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the sell signal for the given dataframe
:param dataframe: DataFrame
:param metadata: Additional information, like the currently traded pair
:return: DataFrame with buy column
"""
dataframe.loc[
(
(
(qtpylib.crossed_above(dataframe['rsi'], 70)) |
(qtpylib.crossed_above(dataframe['fastd'], 70))
) &
(dataframe['adx'] > 10) &
(dataframe['minus_di'] > 0)
) |
(
(dataframe['adx'] > 70) &
(dataframe['minus_di'] > 0.5)
),
'sell'] = 1
return dataframe
+380
View File
@@ -0,0 +1,380 @@
"""
IStrategy interface
This module defines the interface to apply for strategies
"""
import logging
from abc import ABC, abstractmethod
from datetime import datetime
from enum import Enum
from typing import Dict, List, NamedTuple, Tuple
import warnings
import arrow
from pandas import DataFrame
from freqtrade import constants
from freqtrade.persistence import Trade
logger = logging.getLogger(__name__)
class SignalType(Enum):
"""
Enum to distinguish between buy and sell signals
"""
BUY = "buy"
SELL = "sell"
class SellType(Enum):
"""
Enum to distinguish between sell reasons
"""
ROI = "roi"
STOP_LOSS = "stop_loss"
STOPLOSS_ON_EXCHANGE = "stoploss_on_exchange"
TRAILING_STOP_LOSS = "trailing_stop_loss"
SELL_SIGNAL = "sell_signal"
FORCE_SELL = "force_sell"
NONE = ""
class SellCheckTuple(NamedTuple):
"""
NamedTuple for Sell type + reason
"""
sell_flag: bool
sell_type: SellType
class IStrategy(ABC):
"""
Interface for freqtrade strategies
Defines the mandatory structure must follow any custom strategies
Attributes you can use:
minimal_roi -> Dict: Minimal ROI designed for the strategy
stoploss -> float: optimal stoploss designed for the strategy
ticker_interval -> str: value of the ticker interval to use for the strategy
"""
_populate_fun_len: int = 0
_buy_fun_len: int = 0
_sell_fun_len: int = 0
# associated minimal roi
minimal_roi: Dict
# associated stoploss
stoploss: float
# associated ticker interval
ticker_interval: str
# Optional order types
order_types: Dict = {
'buy': 'limit',
'sell': 'limit',
'stoploss': 'limit',
'stoploss_on_exchange': False
}
# Optional time in force
order_time_in_force: Dict = {
'buy': 'gtc',
'sell': 'gtc',
}
# run "populate_indicators" only for new candle
process_only_new_candles: bool = False
# Dict to determine if analysis is necessary
_last_candle_seen_per_pair: Dict[str, datetime] = {}
def __init__(self, config: dict) -> None:
self.config = config
self._last_candle_seen_per_pair = {}
@abstractmethod
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Populate indicators that will be used in the Buy and Sell strategy
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe()
:param metadata: Additional information, like the currently traded pair
:return: a Dataframe with all mandatory indicators for the strategies
"""
@abstractmethod
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the buy signal for the given dataframe
:param dataframe: DataFrame
:param metadata: Additional information, like the currently traded pair
:return: DataFrame with buy column
"""
@abstractmethod
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the sell signal for the given dataframe
:param dataframe: DataFrame
:param metadata: Additional information, like the currently traded pair
:return: DataFrame with sell column
"""
def get_strategy_name(self) -> str:
"""
Returns strategy class name
"""
return self.__class__.__name__
def analyze_ticker(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Parses the given ticker history and returns a populated DataFrame
add several TA indicators and buy signal to it
:return DataFrame with ticker data and indicator data
"""
pair = str(metadata.get('pair'))
# Test if seen this pair and last candle before.
# always run if process_only_new_candles is set to false
if (not self.process_only_new_candles or
self._last_candle_seen_per_pair.get(pair, None) != dataframe.iloc[-1]['date']):
# Defs that only make change on new candle data.
logging.debug("TA Analysis Launched")
dataframe = self.advise_indicators(dataframe, metadata)
dataframe = self.advise_buy(dataframe, metadata)
dataframe = self.advise_sell(dataframe, metadata)
self._last_candle_seen_per_pair[pair] = dataframe.iloc[-1]['date']
else:
logging.debug("Skippinig TA Analysis for already analyzed candle")
dataframe['buy'] = 0
dataframe['sell'] = 0
# Other Defs in strategy that want to be called every loop here
# twitter_sell = self.watch_twitter_feed(dataframe, metadata)
logging.debug("Loop Analysis Launched")
return dataframe
def get_signal(self, pair: str, interval: str,
dataframe: DataFrame) -> Tuple[bool, bool]:
"""
Calculates current signal based several technical analysis indicators
:param pair: pair in format ANT/BTC
:param interval: Interval to use (in min)
:param dataframe: Dataframe to analyze
:return: (Buy, Sell) A bool-tuple indicating buy/sell signal
"""
if not isinstance(dataframe, DataFrame) or dataframe.empty:
logger.warning('Empty ticker history for pair %s', pair)
return False, False
try:
dataframe = self.analyze_ticker(dataframe, {'pair': pair})
except ValueError as error:
logger.warning(
'Unable to analyze ticker for pair %s: %s',
pair,
str(error)
)
return False, False
except Exception as error:
logger.exception(
'Unexpected error when analyzing ticker for pair %s: %s',
pair,
str(error)
)
return False, False
if dataframe.empty:
logger.warning('Empty dataframe for pair %s', pair)
return False, False
latest = dataframe.iloc[-1]
# Check if dataframe is out of date
signal_date = arrow.get(latest['date'])
interval_minutes = constants.TICKER_INTERVAL_MINUTES[interval]
offset = self.config.get('exchange', {}).get('outdated_offset', 5)
if signal_date < (arrow.utcnow().shift(minutes=-(interval_minutes * 2 + offset))):
logger.warning(
'Outdated history for pair %s. Last tick is %s minutes old',
pair,
(arrow.utcnow() - signal_date).seconds // 60
)
return False, False
(buy, sell) = latest[SignalType.BUY.value] == 1, latest[SignalType.SELL.value] == 1
logger.debug(
'trigger: %s (pair=%s) buy=%s sell=%s',
latest['date'],
pair,
str(buy),
str(sell)
)
return buy, sell
def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool,
sell: bool, low: float = None, high: float = None,
force_stoploss: float = 0) -> SellCheckTuple:
"""
This function evaluate if on the condition required to trigger a sell has been reached
if the threshold is reached and updates the trade record.
:return: True if trade should be sold, False otherwise
"""
# Set current rate to low for backtesting sell
current_rate = low or rate
current_profit = trade.calc_profit_percent(current_rate)
if self.order_types.get('stoploss_on_exchange'):
stoplossflag = SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
else:
stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade,
current_time=date, current_profit=current_profit,
force_stoploss=force_stoploss)
if stoplossflag.sell_flag:
return stoplossflag
# Set current rate to low for backtesting sell
current_rate = high or rate
current_profit = trade.calc_profit_percent(current_rate)
experimental = self.config.get('experimental', {})
if buy and experimental.get('ignore_roi_if_buy_signal', False):
logger.debug('Buy signal still active - not selling.')
return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
# Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
logger.debug('Required profit reached. Selling..')
return SellCheckTuple(sell_flag=True, sell_type=SellType.ROI)
if experimental.get('sell_profit_only', False):
logger.debug('Checking if trade is profitable..')
if trade.calc_profit(rate=rate) <= 0:
return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
if sell and not buy and experimental.get('use_sell_signal', False):
logger.debug('Sell signal received. Selling..')
return SellCheckTuple(sell_flag=True, sell_type=SellType.SELL_SIGNAL)
return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
def stop_loss_reached(self, current_rate: float, trade: Trade, current_time: datetime,
current_profit: float, force_stoploss: float) -> SellCheckTuple:
"""
Based on current profit of the trade and configured (trailing) stoploss,
decides to sell or not
:param current_profit: current profit in percent
"""
trailing_stop = self.config.get('trailing_stop', False)
trade.adjust_stop_loss(trade.open_rate, force_stoploss if force_stoploss
else self.stoploss, initial=True)
# evaluate if the stoploss was hit
if self.stoploss is not None and trade.stop_loss >= current_rate:
selltype = SellType.STOP_LOSS
if trailing_stop:
selltype = SellType.TRAILING_STOP_LOSS
logger.debug(
f"HIT STOP: current price at {current_rate:.6f}, "
f"stop loss is {trade.stop_loss:.6f}, "
f"initial stop loss was at {trade.initial_stop_loss:.6f}, "
f"trade opened at {trade.open_rate:.6f}")
logger.debug(f"trailing stop saved {trade.stop_loss - trade.initial_stop_loss:.6f}")
logger.debug('Stop loss hit.')
return SellCheckTuple(sell_flag=True, sell_type=selltype)
# update the stop loss afterwards, after all by definition it's supposed to be hanging
if trailing_stop:
# check if we have a special stop loss for positive condition
# and if profit is positive
stop_loss_value = self.stoploss
sl_offset = self.config.get('trailing_stop_positive_offset', 0.0)
if 'trailing_stop_positive' in self.config and current_profit > sl_offset:
# Ignore mypy error check in configuration that this is a float
stop_loss_value = self.config.get('trailing_stop_positive') # type: ignore
logger.debug(f"using positive stop loss mode: {stop_loss_value} "
f"with offset {sl_offset:.4g} "
f"since we have profit {current_profit:.4f}%")
trade.adjust_stop_loss(current_rate, stop_loss_value)
return SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool:
"""
Based an earlier trade and current price and ROI configuration, decides whether bot should
sell
:return True if bot should sell at current rate
"""
# Check if time matches and current rate is above threshold
time_diff = (current_time.timestamp() - trade.open_date.timestamp()) / 60
for duration, threshold in self.minimal_roi.items():
if time_diff <= duration:
return False
if current_profit > threshold:
return True
return False
def tickerdata_to_dataframe(self, tickerdata: Dict[str, List]) -> Dict[str, DataFrame]:
"""
Creates a dataframe and populates indicators for given ticker data
"""
return {pair: self.advise_indicators(pair_data, {'pair': pair})
for pair, pair_data in tickerdata.items()}
def advise_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Populate indicators that will be used in the Buy and Sell strategy
This method should not be overridden.
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe()
:param metadata: Additional information, like the currently traded pair
:return: a Dataframe with all mandatory indicators for the strategies
"""
if self._populate_fun_len == 2:
warnings.warn("deprecated - check out the Sample strategy to see "
"the current function headers!", DeprecationWarning)
return self.populate_indicators(dataframe) # type: ignore
else:
return self.populate_indicators(dataframe, metadata)
def advise_buy(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the buy signal for the given dataframe
This method should not be overridden.
:param dataframe: DataFrame
:param pair: Additional information, like the currently traded pair
:return: DataFrame with buy column
"""
if self._buy_fun_len == 2:
warnings.warn("deprecated - check out the Sample strategy to see "
"the current function headers!", DeprecationWarning)
return self.populate_buy_trend(dataframe) # type: ignore
else:
return self.populate_buy_trend(dataframe, metadata)
def advise_sell(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
"""
Based on TA indicators, populates the sell signal for the given dataframe
This method should not be overridden.
:param dataframe: DataFrame
:param pair: Additional information, like the currently traded pair
:return: DataFrame with sell column
"""
if self._sell_fun_len == 2:
warnings.warn("deprecated - check out the Sample strategy to see "
"the current function headers!", DeprecationWarning)
return self.populate_sell_trend(dataframe) # type: ignore
else:
return self.populate_sell_trend(dataframe, metadata)
+776 -82
View File
@@ -1,32 +1,152 @@
# pragma pylint: disable=missing-docstring
import json
import logging
from datetime import datetime
from unittest.mock import MagicMock
from functools import reduce
from typing import Dict, Optional
from unittest.mock import MagicMock, PropertyMock
import arrow
import pytest
from jsonschema import validate
from telegram import Message, Chat, Update
from telegram import Chat, Message, Update
from freqtrade.misc import CONF_SCHEMA
from freqtrade import constants
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.exchange import Exchange
from freqtrade.edge import Edge, PairInfo
from freqtrade.freqtradebot import FreqtradeBot
logging.getLogger('').setLevel(logging.INFO)
@pytest.fixture(scope="module")
def log_has(line, logs):
# caplog mocker returns log as a tuple: ('freqtrade.something', logging.WARNING, 'foobar')
# and we want to match line against foobar in the tuple
return reduce(lambda a, b: a or b,
filter(lambda x: x[2] == line, logs),
False)
def patch_exchange(mocker, api_mock=None, id='bittrex') -> None:
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
mocker.patch('freqtrade.exchange.Exchange.validate_ordertypes', MagicMock())
mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value=id))
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title()))
if api_mock:
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
else:
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock())
def get_patched_exchange(mocker, config, api_mock=None, id='bittrex') -> Exchange:
patch_exchange(mocker, api_mock, id)
exchange = Exchange(config)
return exchange
def patch_wallet(mocker, free=999.9) -> None:
mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(
return_value=free
))
def patch_edge(mocker) -> None:
# "ETH/BTC",
# "LTC/BTC",
# "XRP/BTC",
# "NEO/BTC"
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={
'NEO/BTC': PairInfo(-0.20, 0.66, 3.71, 0.50, 1.71, 10, 25),
'LTC/BTC': PairInfo(-0.21, 0.66, 3.71, 0.50, 1.71, 11, 20),
}
))
mocker.patch('freqtrade.edge.Edge.calculate', MagicMock(return_value=True))
def get_patched_edge(mocker, config) -> Edge:
patch_edge(mocker)
edge = Edge(config)
return edge
# Functions for recurrent object patching
def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
"""
This function patch _init_modules() to not call dependencies
:param mocker: a Mocker object to apply patches
:param config: Config to pass to the bot
:return: None
"""
patch_coinmarketcap(mocker, {'price_usd': 12345.0})
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
patch_exchange(mocker, None)
mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock())
mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock())
return FreqtradeBot(config)
def patch_coinmarketcap(mocker, value: Optional[Dict[str, float]] = None) -> None:
"""
Mocker to coinmarketcap to speed up tests
:param mocker: mocker to patch coinmarketcap class
:return: None
"""
tickermock = MagicMock(return_value={'price_usd': 12345.0})
listmock = MagicMock(return_value={'data': [{'id': 1, 'name': 'Bitcoin', 'symbol': 'BTC',
'website_slug': 'bitcoin'},
{'id': 1027, 'name': 'Ethereum', 'symbol': 'ETH',
'website_slug': 'ethereum'}
]})
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.Market',
ticker=tickermock,
listings=listmock,
)
@pytest.fixture(scope="function")
def default_conf():
""" Returns validated configuration suitable for most tests """
configuration = {
"max_open_trades": 1,
"stake_currency": "BTC",
"stake_amount": 0.05,
"stake_amount": 0.001,
"fiat_display_currency": "USD",
"ticker_interval": '5m',
"dry_run": True,
"minimal_roi": {
"40": 0.0,
"30": 0.01,
"20": 0.02,
"0": 0.04
"40": 0.0,
"30": 0.01,
"20": 0.02,
"0": 0.04
},
"stoploss": -0.10,
"unfilledtimeout": {
"buy": 10,
"sell": 30
},
"stoploss": -0.05,
"bid_strategy": {
"ask_last_balance": 0.0
"ask_last_balance": 0.0,
"use_order_book": False,
"order_book_top": 1,
"check_depth_of_market": {
"enabled": False,
"bids_to_ask_delta": 1
}
},
"ask_strategy": {
"use_order_book": False,
"order_book_min": 1,
"order_book_max": 1
},
"exchange": {
"name": "bittrex",
@@ -34,11 +154,10 @@ def default_conf():
"key": "key",
"secret": "secret",
"pair_whitelist": [
"BTC_ETH",
"BTC_TKN",
"BTC_TRST",
"BTC_SWT",
"BTC_BCC"
"ETH/BTC",
"LTC/BTC",
"XRP/BTC",
"NEO/BTC"
]
},
"telegram": {
@@ -46,35 +165,13 @@ def default_conf():
"token": "token",
"chat_id": "0"
},
"initial_state": "running"
"initial_state": "running",
"db_url": "sqlite://",
"loglevel": logging.DEBUG,
}
validate(configuration, CONF_SCHEMA)
return configuration
@pytest.fixture(scope="module")
def backtest_conf():
return {
"minimal_roi": {
"40": 0.0,
"30": 0.01,
"20": 0.02,
"0": 0.04
},
"stoploss": -0.05
}
@pytest.fixture(scope="module")
def backdata():
result = {}
for pair in ['btc-neo', 'btc-eth', 'btc-omg', 'btc-edg', 'btc-pay',
'btc-pivx', 'btc-qtum', 'btc-mtl', 'btc-etc', 'btc-ltc']:
with open('freqtrade/tests/testdata/' + pair + '.json') as data_file:
result[pair] = json.load(data_file)
return result
@pytest.fixture
def update():
_update = Update(0)
@@ -82,56 +179,286 @@ def update():
return _update
@pytest.fixture
def fee():
return MagicMock(return_value=0.0025)
@pytest.fixture
def ticker():
return MagicMock(return_value={
'bid': 0.07256061,
'ask': 0.072661,
'last': 0.07256061,
'bid': 0.00001098,
'ask': 0.00001099,
'last': 0.00001098,
})
@pytest.fixture
def health():
return MagicMock(return_value=[{
'Currency': 'BTC',
'IsActive': True,
'LastChecked': '2017-11-13T20:15:00.00',
'Notice': None
}, {
'Currency': 'ETH',
'IsActive': True,
'LastChecked': '2017-11-13T20:15:00.00',
'Notice': None
}, {
'Currency': 'TRST',
'IsActive': True,
'LastChecked': '2017-11-13T20:15:00.00',
'Notice': None
}, {
'Currency': 'SWT',
'IsActive': True,
'LastChecked': '2017-11-13T20:15:00.00',
'Notice': None
}, {
'Currency': 'BCC',
'IsActive': False,
'LastChecked': '2017-11-13T20:15:00.00',
'Notice': None
}])
def ticker_sell_up():
return MagicMock(return_value={
'bid': 0.00001172,
'ask': 0.00001173,
'last': 0.00001172,
})
@pytest.fixture
def ticker_sell_down():
return MagicMock(return_value={
'bid': 0.00001044,
'ask': 0.00001043,
'last': 0.00001044,
})
@pytest.fixture
def markets():
return MagicMock(return_value=[
{
'id': 'ethbtc',
'symbol': 'ETH/BTC',
'base': 'ETH',
'quote': 'BTC',
'active': True,
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': {
'min': 1,
'max': 500000,
},
},
'info': '',
},
{
'id': 'tknbtc',
'symbol': 'TKN/BTC',
'base': 'TKN',
'quote': 'BTC',
'active': True,
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': {
'min': 1,
'max': 500000,
},
},
'info': '',
},
{
'id': 'blkbtc',
'symbol': 'BLK/BTC',
'base': 'BLK',
'quote': 'BTC',
'active': True,
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': {
'min': 1,
'max': 500000,
},
},
'info': '',
},
{
'id': 'ltcbtc',
'symbol': 'LTC/BTC',
'base': 'LTC',
'quote': 'BTC',
'active': False,
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': {
'min': 1,
'max': 500000,
},
},
'info': '',
},
{
'id': 'xrpbtc',
'symbol': 'XRP/BTC',
'base': 'XRP',
'quote': 'BTC',
'active': False,
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': {
'min': 1,
'max': 500000,
},
},
'info': '',
},
{
'id': 'neobtc',
'symbol': 'NEO/BTC',
'base': 'NEO',
'quote': 'BTC',
'active': False,
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': {
'min': 1,
'max': 500000,
},
},
'info': '',
}
])
@pytest.fixture
def markets_empty():
return MagicMock(return_value=[])
@pytest.fixture(scope='function')
def limit_buy_order():
return {
'id': 'mocked_limit_buy',
'type': 'LIMIT_BUY',
'type': 'limit',
'side': 'buy',
'pair': 'mocked',
'opened': datetime.utcnow(),
'rate': 0.07256061,
'amount': 206.43811673387373,
'datetime': arrow.utcnow().isoformat(),
'price': 0.00001099,
'amount': 90.99181073,
'remaining': 0.0,
'closed': datetime.utcnow(),
'status': 'closed'
}
@pytest.fixture(scope='function')
def market_buy_order():
return {
'id': 'mocked_market_buy',
'type': 'market',
'side': 'buy',
'pair': 'mocked',
'datetime': arrow.utcnow().isoformat(),
'price': 0.00004099,
'amount': 91.99181073,
'remaining': 0.0,
'status': 'closed'
}
@pytest.fixture
def market_sell_order():
return {
'id': 'mocked_limit_sell',
'type': 'market',
'side': 'sell',
'pair': 'mocked',
'datetime': arrow.utcnow().isoformat(),
'price': 0.00004173,
'amount': 91.99181073,
'remaining': 0.0,
'status': 'closed'
}
@pytest.fixture
def limit_buy_order_old():
return {
'id': 'mocked_limit_buy_old',
'type': 'limit',
'side': 'buy',
'pair': 'mocked',
'datetime': str(arrow.utcnow().shift(minutes=-601).datetime),
'price': 0.00001099,
'amount': 90.99181073,
'remaining': 90.99181073,
'status': 'open'
}
@pytest.fixture
def limit_sell_order_old():
return {
'id': 'mocked_limit_sell_old',
'type': 'limit',
'side': 'sell',
'pair': 'ETH/BTC',
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
'price': 0.00001099,
'amount': 90.99181073,
'remaining': 90.99181073,
'status': 'open'
}
@pytest.fixture
def limit_buy_order_old_partial():
return {
'id': 'mocked_limit_buy_old_partial',
'type': 'limit',
'side': 'buy',
'pair': 'ETH/BTC',
'datetime': arrow.utcnow().shift(minutes=-601).isoformat(),
'price': 0.00001099,
'amount': 90.99181073,
'remaining': 67.99181073,
'status': 'open'
}
@@ -139,11 +466,378 @@ def limit_buy_order():
def limit_sell_order():
return {
'id': 'mocked_limit_sell',
'type': 'LIMIT_SELL',
'type': 'limit',
'side': 'sell',
'pair': 'mocked',
'opened': datetime.utcnow(),
'rate': 0.0802134,
'amount': 206.43811673387373,
'datetime': arrow.utcnow().isoformat(),
'price': 0.00001173,
'amount': 90.99181073,
'remaining': 0.0,
'closed': datetime.utcnow(),
'status': 'closed'
}
@pytest.fixture
def order_book_l2():
return MagicMock(return_value={
'bids': [
[0.043936, 10.442],
[0.043935, 31.865],
[0.043933, 11.212],
[0.043928, 0.088],
[0.043925, 10.0],
[0.043921, 10.0],
[0.04392, 37.64],
[0.043899, 0.066],
[0.043885, 0.676],
[0.04387, 22.758]
],
'asks': [
[0.043949, 0.346],
[0.04395, 0.608],
[0.043951, 3.948],
[0.043954, 0.288],
[0.043958, 9.277],
[0.043995, 1.566],
[0.044, 0.588],
[0.044002, 0.992],
[0.044003, 0.095],
[0.04402, 37.64]
],
'timestamp': None,
'datetime': None,
'nonce': 288004540
})
@pytest.fixture
def ticker_history_list():
return [
[
1511686200000, # unix timestamp ms
8.794e-05, # open
8.948e-05, # high
8.794e-05, # low
8.88e-05, # close
0.0877869, # volume (in quote currency)
],
[
1511686500000,
8.88e-05,
8.942e-05,
8.88e-05,
8.893e-05,
0.05874751,
],
[
1511686800000,
8.891e-05,
8.893e-05,
8.875e-05,
8.877e-05,
0.7039405
]
]
@pytest.fixture
def ticker_history(ticker_history_list):
return parse_ticker_dataframe(ticker_history_list)
@pytest.fixture
def tickers():
return MagicMock(return_value={
'ETH/BTC': {
'symbol': 'ETH/BTC',
'timestamp': 1522014806207,
'datetime': '2018-03-25T21:53:26.207Z',
'high': 0.061697,
'low': 0.060531,
'bid': 0.061588,
'bidVolume': 3.321,
'ask': 0.061655,
'askVolume': 0.212,
'vwap': 0.06105296,
'open': 0.060809,
'close': 0.060761,
'first': None,
'last': 0.061588,
'change': 1.281,
'percentage': None,
'average': None,
'baseVolume': 111649.001,
'quoteVolume': 6816.50176926,
'info': {}
},
'TKN/BTC': {
'symbol': 'TKN/BTC',
'timestamp': 1522014806169,
'datetime': '2018-03-25T21:53:26.169Z',
'high': 0.01885,
'low': 0.018497,
'bid': 0.018799,
'bidVolume': 8.38,
'ask': 0.018802,
'askVolume': 15.0,
'vwap': 0.01869197,
'open': 0.018585,
'close': 0.018573,
'baseVolume': 81058.66,
'quoteVolume': 2247.48374509,
},
'BLK/BTC': {
'symbol': 'BLK/BTC',
'timestamp': 1522014806072,
'datetime': '2018-03-25T21:53:26.720Z',
'high': 0.007745,
'low': 0.007512,
'bid': 0.007729,
'bidVolume': 0.01,
'ask': 0.007743,
'askVolume': 21.37,
'vwap': 0.00761466,
'open': 0.007653,
'close': 0.007652,
'first': None,
'last': 0.007743,
'change': 1.176,
'percentage': None,
'average': None,
'baseVolume': 295152.26,
'quoteVolume': 1515.14631229,
'info': {}
},
'LTC/BTC': {
'symbol': 'LTC/BTC',
'timestamp': 1523787258992,
'datetime': '2018-04-15T10:14:19.992Z',
'high': 0.015978,
'low': 0.0157,
'bid': 0.015954,
'bidVolume': 12.83,
'ask': 0.015957,
'askVolume': 0.49,
'vwap': 0.01581636,
'open': 0.015823,
'close': 0.01582,
'first': None,
'last': 0.015951,
'change': 0.809,
'percentage': None,
'average': None,
'baseVolume': 88620.68,
'quoteVolume': 1401.65697943,
'info': {}
},
'ETH/USDT': {
'symbol': 'ETH/USDT',
'timestamp': 1522014804118,
'datetime': '2018-03-25T21:53:24.118Z',
'high': 530.88,
'low': 512.0,
'bid': 529.73,
'bidVolume': 0.2,
'ask': 530.21,
'askVolume': 0.2464,
'vwap': 521.02438405,
'open': 527.27,
'close': 528.42,
'first': None,
'last': 530.21,
'change': 0.558,
'percentage': None,
'average': None,
'baseVolume': 72300.0659,
'quoteVolume': 37670097.3022171,
'info': {}
},
'TKN/USDT': {
'symbol': 'TKN/USDT',
'timestamp': 1522014806198,
'datetime': '2018-03-25T21:53:26.198Z',
'high': 8718.0,
'low': 8365.77,
'bid': 8603.64,
'bidVolume': 0.15846,
'ask': 8603.67,
'askVolume': 0.069147,
'vwap': 8536.35621697,
'open': 8680.0,
'close': 8680.0,
'first': None,
'last': 8603.67,
'change': -0.879,
'percentage': None,
'average': None,
'baseVolume': 30414.604298,
'quoteVolume': 259629896.48584127,
'info': {}
},
'BLK/USDT': {
'symbol': 'BLK/USDT',
'timestamp': 1522014806145,
'datetime': '2018-03-25T21:53:26.145Z',
'high': 66.95,
'low': 63.38,
'bid': 66.473,
'bidVolume': 4.968,
'ask': 66.54,
'askVolume': 2.704,
'vwap': 65.0526901,
'open': 66.43,
'close': 66.383,
'first': None,
'last': 66.5,
'change': 0.105,
'percentage': None,
'average': None,
'baseVolume': 294106.204,
'quoteVolume': 19132399.743954,
'info': {}
},
'LTC/USDT': {
'symbol': 'LTC/USDT',
'timestamp': 1523787257812,
'datetime': '2018-04-15T10:14:18.812Z',
'high': 129.94,
'low': 124.0,
'bid': 129.28,
'bidVolume': 0.03201,
'ask': 129.52,
'askVolume': 0.14529,
'vwap': 126.92838682,
'open': 127.0,
'close': 127.1,
'first': None,
'last': 129.28,
'change': 1.795,
'percentage': None,
'average': None,
'baseVolume': 59698.79897,
'quoteVolume': 29132399.743954,
'info': {}
}
})
@pytest.fixture
def result():
with open('freqtrade/tests/testdata/UNITTEST_BTC-1m.json') as data_file:
return parse_ticker_dataframe(json.load(data_file))
# FIX:
# Create an fixture/function
# that inserts a trade of some type and open-status
# return the open-order-id
# See tests in rpc/main that could use this
@pytest.fixture(scope="function")
def trades_for_order():
return [{'info': {'id': 34567,
'orderId': 123456,
'price': '0.24544100',
'qty': '8.00000000',
'commission': '0.00800000',
'commissionAsset': 'LTC',
'time': 1521663363189,
'isBuyer': True,
'isMaker': False,
'isBestMatch': True},
'timestamp': 1521663363189,
'datetime': '2018-03-21T20:16:03.189Z',
'symbol': 'LTC/ETH',
'id': '34567',
'order': '123456',
'type': None,
'side': 'buy',
'price': 0.245441,
'cost': 1.963528,
'amount': 8.0,
'fee': {'cost': 0.008, 'currency': 'LTC'}}]
@pytest.fixture(scope="function")
def trades_for_order2():
return [{'info': {'id': 34567,
'orderId': 123456,
'price': '0.24544100',
'qty': '8.00000000',
'commission': '0.00800000',
'commissionAsset': 'LTC',
'time': 1521663363189,
'isBuyer': True,
'isMaker': False,
'isBestMatch': True},
'timestamp': 1521663363189,
'datetime': '2018-03-21T20:16:03.189Z',
'symbol': 'LTC/ETH',
'id': '34567',
'order': '123456',
'type': None,
'side': 'buy',
'price': 0.245441,
'cost': 1.963528,
'amount': 4.0,
'fee': {'cost': 0.004, 'currency': 'LTC'}},
{'info': {'id': 34567,
'orderId': 123456,
'price': '0.24544100',
'qty': '8.00000000',
'commission': '0.00800000',
'commissionAsset': 'LTC',
'time': 1521663363189,
'isBuyer': True,
'isMaker': False,
'isBestMatch': True},
'timestamp': 1521663363189,
'datetime': '2018-03-21T20:16:03.189Z',
'symbol': 'LTC/ETH',
'id': '34567',
'order': '123456',
'type': None,
'side': 'buy',
'price': 0.245441,
'cost': 1.963528,
'amount': 4.0,
'fee': {'cost': 0.004, 'currency': 'LTC'}}]
@pytest.fixture
def buy_order_fee():
return {
'id': 'mocked_limit_buy_old',
'type': 'limit',
'side': 'buy',
'pair': 'mocked',
'datetime': str(arrow.utcnow().shift(minutes=-601).datetime),
'price': 0.245441,
'amount': 8.0,
'remaining': 90.99181073,
'status': 'closed',
'fee': None
}
@pytest.fixture(scope="function")
def edge_conf(default_conf):
default_conf['max_open_trades'] = -1
default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
default_conf['edge'] = {
"enabled": True,
"process_throttle_secs": 1800,
"calculate_since_number_of_days": 14,
"capital_available_percentage": 0.5,
"allowed_risk": 0.01,
"stoploss_range_min": -0.01,
"stoploss_range_max": -0.1,
"stoploss_range_step": -0.01,
"maximum_winrate": 0.80,
"minimum_expectancy": 0.20,
"min_trade_number": 15,
"max_trade_duration_minute": 1440,
"remove_pumps": False
}
return default_conf
View File
+25
View File
@@ -0,0 +1,25 @@
# pragma pylint: disable=missing-docstring, C0103
import logging
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.tests.conftest import log_has
def test_dataframe_correct_length(result):
dataframe = parse_ticker_dataframe(result)
assert len(result.index) - 1 == len(dataframe.index) # last partial candle removed
def test_dataframe_correct_columns(result):
assert result.columns.tolist() == \
['date', 'open', 'high', 'low', 'close', 'volume']
def test_parse_ticker_dataframe(ticker_history, caplog):
columns = ['date', 'open', 'high', 'low', 'close', 'volume']
caplog.set_level(logging.DEBUG)
# Test file with BV data
dataframe = parse_ticker_dataframe(ticker_history)
assert dataframe.columns.tolist() == columns
assert log_has('Parsing tickerlist to dataframe', caplog.record_tuples)
+475
View File
@@ -0,0 +1,475 @@
# pragma pylint: disable=missing-docstring, protected-access, C0103
import json
import os
from pathlib import Path
import uuid
from shutil import copyfile
import arrow
from pandas import DataFrame
import pytest
from freqtrade import OperationalException
from freqtrade.arguments import TimeRange
from freqtrade.data import history
from freqtrade.data.history import (download_pair_history,
load_cached_data_for_updating,
load_tickerdata_file,
make_testdata_path,
trim_tickerlist)
from freqtrade.misc import file_dump_json
from freqtrade.tests.conftest import get_patched_exchange, log_has
# Change this if modifying UNITTEST/BTC testdatafile
_BTC_UNITTEST_LENGTH = 13681
def _backup_file(file: str, copy_file: bool = False) -> None:
"""
Backup existing file to avoid deleting the user file
:param file: complete path to the file
:param touch_file: create an empty file in replacement
:return: None
"""
file_swp = file + '.swp'
if os.path.isfile(file):
os.rename(file, file_swp)
if copy_file:
copyfile(file_swp, file)
def _clean_test_file(file: str) -> None:
"""
Backup existing file to avoid deleting the user file
:param file: complete path to the file
:return: None
"""
file_swp = file + '.swp'
# 1. Delete file from the test
if os.path.isfile(file):
os.remove(file)
# 2. Rollback to the initial file
if os.path.isfile(file_swp):
os.rename(file_swp, file)
def test_load_data_30min_ticker(mocker, caplog, default_conf) -> None:
ld = history.load_pair_history(pair='UNITTEST/BTC', ticker_interval='30m', datadir=None)
assert isinstance(ld, DataFrame)
assert not log_has('Download the pair: "UNITTEST/BTC", Interval: 30m', caplog.record_tuples)
def test_load_data_7min_ticker(mocker, caplog, default_conf) -> None:
ld = history.load_pair_history(pair='UNITTEST/BTC', ticker_interval='7m', datadir=None)
assert not isinstance(ld, DataFrame)
assert ld is None
assert log_has(
'No data for pair: "UNITTEST/BTC", Interval: 7m. '
'Use --refresh-pairs-cached to download the data', caplog.record_tuples)
def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_history', return_value=ticker_history)
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-1m.json')
_backup_file(file, copy_file=True)
history.load_data(datadir=None, ticker_interval='1m', pairs=['UNITTEST/BTC'])
assert os.path.isfile(file) is True
assert not log_has('Download the pair: "UNITTEST/BTC", Interval: 1m', caplog.record_tuples)
_clean_test_file(file)
def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog, default_conf) -> None:
"""
Test load_pair_history() with 1 min ticker
"""
mocker.patch('freqtrade.exchange.Exchange.get_history', return_value=ticker_history_list)
exchange = get_patched_exchange(mocker, default_conf)
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
_backup_file(file)
# do not download a new pair if refresh_pairs isn't set
history.load_pair_history(datadir=None,
ticker_interval='1m',
refresh_pairs=False,
pair='MEME/BTC')
assert os.path.isfile(file) is False
assert log_has('No data for pair: "MEME/BTC", Interval: 1m. '
'Use --refresh-pairs-cached to download the data',
caplog.record_tuples)
# download a new pair if refresh_pairs is set
history.load_pair_history(datadir=None,
ticker_interval='1m',
refresh_pairs=True,
exchange=exchange,
pair='MEME/BTC')
assert os.path.isfile(file) is True
assert log_has('Download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples)
with pytest.raises(OperationalException, match=r'Exchange needs to be initialized when.*'):
history.load_pair_history(datadir=None,
ticker_interval='1m',
refresh_pairs=True,
exchange=None,
pair='MEME/BTC')
_clean_test_file(file)
def test_testdata_path() -> None:
assert str(Path('freqtrade') / 'tests' / 'testdata') in str(make_testdata_path(None))
def test_load_cached_data_for_updating(mocker) -> None:
datadir = Path(__file__).parent.parent.joinpath('testdata')
test_data = None
test_filename = datadir.joinpath('UNITTEST_BTC-1m.json')
with open(test_filename, "rt") as file:
test_data = json.load(file)
# change now time to test 'line' cases
# now = last cached item + 1 hour
now_ts = test_data[-1][0] / 1000 + 60 * 60
mocker.patch('arrow.utcnow', return_value=arrow.get(now_ts))
# timeframe starts earlier than the cached data
# should fully update data
timerange = TimeRange('date', None, test_data[0][0] / 1000 - 1, 0)
data, start_ts = load_cached_data_for_updating(test_filename,
'1m',
timerange)
assert data == []
assert start_ts == test_data[0][0] - 1000
# same with 'line' timeframe
num_lines = (test_data[-1][0] - test_data[1][0]) / 1000 / 60 + 120
data, start_ts = load_cached_data_for_updating(test_filename,
'1m',
TimeRange(None, 'line', 0, -num_lines))
assert data == []
assert start_ts < test_data[0][0] - 1
# timeframe starts in the center of the cached data
# should return the chached data w/o the last item
timerange = TimeRange('date', None, test_data[0][0] / 1000 + 1, 0)
data, start_ts = load_cached_data_for_updating(test_filename,
'1m',
timerange)
assert data == test_data[:-1]
assert test_data[-2][0] < start_ts < test_data[-1][0]
# same with 'line' timeframe
num_lines = (test_data[-1][0] - test_data[1][0]) / 1000 / 60 + 30
timerange = TimeRange(None, 'line', 0, -num_lines)
data, start_ts = load_cached_data_for_updating(test_filename,
'1m',
timerange)
assert data == test_data[:-1]
assert test_data[-2][0] < start_ts < test_data[-1][0]
# timeframe starts after the chached data
# should return the chached data w/o the last item
timerange = TimeRange('date', None, test_data[-1][0] / 1000 + 1, 0)
data, start_ts = load_cached_data_for_updating(test_filename,
'1m',
timerange)
assert data == test_data[:-1]
assert test_data[-2][0] < start_ts < test_data[-1][0]
# same with 'line' timeframe
num_lines = 30
timerange = TimeRange(None, 'line', 0, -num_lines)
data, start_ts = load_cached_data_for_updating(test_filename,
'1m',
timerange)
assert data == test_data[:-1]
assert test_data[-2][0] < start_ts < test_data[-1][0]
# no timeframe is set
# should return the chached data w/o the last item
num_lines = 30
timerange = TimeRange(None, 'line', 0, -num_lines)
data, start_ts = load_cached_data_for_updating(test_filename,
'1m',
timerange)
assert data == test_data[:-1]
assert test_data[-2][0] < start_ts < test_data[-1][0]
# no datafile exist
# should return timestamp start time
timerange = TimeRange('date', None, now_ts - 10000, 0)
data, start_ts = load_cached_data_for_updating(test_filename.with_name('unexist'),
'1m',
timerange)
assert data == []
assert start_ts == (now_ts - 10000) * 1000
# same with 'line' timeframe
num_lines = 30
timerange = TimeRange(None, 'line', 0, -num_lines)
data, start_ts = load_cached_data_for_updating(test_filename.with_name('unexist'),
'1m',
timerange)
assert data == []
assert start_ts == (now_ts - num_lines * 60) * 1000
# no datafile exist, no timeframe is set
# should return an empty array and None
data, start_ts = load_cached_data_for_updating(test_filename.with_name('unexist'),
'1m',
None)
assert data == []
assert start_ts is None
def test_download_pair_history(ticker_history_list, mocker, default_conf) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_history', return_value=ticker_history_list)
exchange = get_patched_exchange(mocker, default_conf)
file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
file2_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'CFI_BTC-1m.json')
file2_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'CFI_BTC-5m.json')
_backup_file(file1_1)
_backup_file(file1_5)
_backup_file(file2_1)
_backup_file(file2_5)
assert os.path.isfile(file1_1) is False
assert os.path.isfile(file2_1) is False
assert download_pair_history(datadir=None, exchange=exchange,
pair='MEME/BTC',
tick_interval='1m')
assert download_pair_history(datadir=None, exchange=exchange,
pair='CFI/BTC',
tick_interval='1m')
assert not exchange._pairs_last_refresh_time
assert os.path.isfile(file1_1) is True
assert os.path.isfile(file2_1) is True
# clean files freshly downloaded
_clean_test_file(file1_1)
_clean_test_file(file2_1)
assert os.path.isfile(file1_5) is False
assert os.path.isfile(file2_5) is False
assert download_pair_history(datadir=None, exchange=exchange,
pair='MEME/BTC',
tick_interval='5m')
assert download_pair_history(datadir=None, exchange=exchange,
pair='CFI/BTC',
tick_interval='5m')
assert not exchange._pairs_last_refresh_time
assert os.path.isfile(file1_5) is True
assert os.path.isfile(file2_5) is True
# clean files freshly downloaded
_clean_test_file(file1_5)
_clean_test_file(file2_5)
def test_download_pair_history2(mocker, default_conf) -> None:
tick = [
[1509836520000, 0.00162008, 0.00162008, 0.00162008, 0.00162008, 108.14853839],
[1509836580000, 0.00161, 0.00161, 0.00161, 0.00161, 82.390199]
]
json_dump_mock = mocker.patch('freqtrade.misc.file_dump_json', return_value=None)
mocker.patch('freqtrade.exchange.Exchange.get_history', return_value=tick)
exchange = get_patched_exchange(mocker, default_conf)
download_pair_history(None, exchange, pair="UNITTEST/BTC", tick_interval='1m')
download_pair_history(None, exchange, pair="UNITTEST/BTC", tick_interval='3m')
assert json_dump_mock.call_count == 2
def test_download_backtesting_data_exception(ticker_history, mocker, caplog, default_conf) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_history',
side_effect=BaseException('File Error'))
exchange = get_patched_exchange(mocker, default_conf)
file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
_backup_file(file1_1)
_backup_file(file1_5)
assert not download_pair_history(datadir=None, exchange=exchange,
pair='MEME/BTC',
tick_interval='1m')
# clean files freshly downloaded
_clean_test_file(file1_1)
_clean_test_file(file1_5)
assert log_has('Failed to download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples)
def test_load_tickerdata_file() -> None:
# 7 does not exist in either format.
assert not load_tickerdata_file(None, 'UNITTEST/BTC', '7m')
# 1 exists only as a .json
tickerdata = load_tickerdata_file(None, 'UNITTEST/BTC', '1m')
assert _BTC_UNITTEST_LENGTH == len(tickerdata)
# 8 .json is empty and will fail if it's loaded. .json.gz is a copy of 1.json
tickerdata = load_tickerdata_file(None, 'UNITTEST/BTC', '8m')
assert _BTC_UNITTEST_LENGTH == len(tickerdata)
def test_load_partial_missing(caplog) -> None:
# Make sure we start fresh - test missing data at start
start = arrow.get('2018-01-01T00:00:00')
end = arrow.get('2018-01-11T00:00:00')
tickerdata = history.load_data(None, '5m', ['UNITTEST/BTC'],
refresh_pairs=False,
timerange=TimeRange('date', 'date',
start.timestamp, end.timestamp))
# timedifference in 5 minutes
td = ((end - start).total_seconds() // 60 // 5) + 1
assert td != len(tickerdata['UNITTEST/BTC'])
start_real = tickerdata['UNITTEST/BTC'].iloc[0, 0]
assert log_has(f'Missing data at start for pair '
f'UNITTEST/BTC, data starts at {start_real.strftime("%Y-%m-%d %H:%M:%S")}',
caplog.record_tuples)
# Make sure we start fresh - test missing data at end
caplog.clear()
start = arrow.get('2018-01-10T00:00:00')
end = arrow.get('2018-02-20T00:00:00')
tickerdata = history.load_data(datadir=None, ticker_interval='5m',
pairs=['UNITTEST/BTC'], refresh_pairs=False,
timerange=TimeRange('date', 'date',
start.timestamp, end.timestamp))
# timedifference in 5 minutes
td = ((end - start).total_seconds() // 60 // 5) + 1
assert td != len(tickerdata['UNITTEST/BTC'])
# Shift endtime with +5 - as last candle is dropped (partial candle)
end_real = arrow.get(tickerdata['UNITTEST/BTC'].iloc[-1, 0]).shift(minutes=5)
assert log_has(f'Missing data at end for pair '
f'UNITTEST/BTC, data ends at {end_real.strftime("%Y-%m-%d %H:%M:%S")}',
caplog.record_tuples)
def test_init(default_conf, mocker) -> None:
exchange = get_patched_exchange(mocker, default_conf)
assert {} == history.load_data(
datadir='',
exchange=exchange,
pairs=[],
refresh_pairs=True,
ticker_interval=default_conf['ticker_interval']
)
def test_trim_tickerlist() -> None:
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-1m.json')
with open(file) as data_file:
ticker_list = json.load(data_file)
ticker_list_len = len(ticker_list)
# Test the pattern ^(-\d+)$
# This pattern uses the latest N elements
timerange = TimeRange(None, 'line', 0, -5)
ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker)
assert ticker_len == 5
assert ticker_list[0] is not ticker[0] # The first element should be different
assert ticker_list[-1] is ticker[-1] # The last element must be the same
# Test the pattern ^(\d+)-$
# This pattern keep X element from the end
timerange = TimeRange('line', None, 5, 0)
ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker)
assert ticker_len == 5
assert ticker_list[0] is ticker[0] # The first element must be the same
assert ticker_list[-1] is not ticker[-1] # The last element should be different
# Test the pattern ^(\d+)-(\d+)$
# This pattern extract a window
timerange = TimeRange('index', 'index', 5, 10)
ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker)
assert ticker_len == 5
assert ticker_list[0] is not ticker[0] # The first element should be different
assert ticker_list[5] is ticker[0] # The list starts at the index 5
assert ticker_list[9] is ticker[-1] # The list ends at the index 9 (5 elements)
# Test the pattern ^(\d{8})-(\d{8})$
# This pattern extract a window between the dates
timerange = TimeRange('date', 'date', ticker_list[5][0] / 1000, ticker_list[10][0] / 1000 - 1)
ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker)
assert ticker_len == 5
assert ticker_list[0] is not ticker[0] # The first element should be different
assert ticker_list[5] is ticker[0] # The list starts at the index 5
assert ticker_list[9] is ticker[-1] # The list ends at the index 9 (5 elements)
# Test the pattern ^-(\d{8})$
# This pattern extracts elements from the start to the date
timerange = TimeRange(None, 'date', 0, ticker_list[10][0] / 1000 - 1)
ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker)
assert ticker_len == 10
assert ticker_list[0] is ticker[0] # The start of the list is included
assert ticker_list[9] is ticker[-1] # The element 10 is not included
# Test the pattern ^(\d{8})-$
# This pattern extracts elements from the date to now
timerange = TimeRange('date', None, ticker_list[10][0] / 1000 - 1, None)
ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker)
assert ticker_len == ticker_list_len - 10
assert ticker_list[10] is ticker[0] # The first element is element #10
assert ticker_list[-1] is ticker[-1] # The last element is the same
# Test a wrong pattern
# This pattern must return the list unchanged
timerange = TimeRange(None, None, None, 5)
ticker = trim_tickerlist(ticker_list, timerange)
ticker_len = len(ticker)
assert ticker_list_len == ticker_len
# Test invalid timerange (start after stop)
timerange = TimeRange('index', 'index', 10, 5)
with pytest.raises(ValueError, match=r'The timerange .* is incorrect'):
trim_tickerlist(ticker_list, timerange)
assert ticker_list_len == ticker_len
# passing empty list
timerange = TimeRange(None, None, None, 5)
ticker = trim_tickerlist([], timerange)
assert 0 == len(ticker)
assert not ticker
def test_file_dump_json() -> None:
file = os.path.join(os.path.dirname(__file__), '..', 'testdata',
'test_{id}.json'.format(id=str(uuid.uuid4())))
data = {'bar': 'foo'}
# check the file we will create does not exist
assert os.path.isfile(file) is False
# Create the Json file
file_dump_json(file, data)
# Check the file was create
assert os.path.isfile(file) is True
# Open the Json file created and test the data is in it
with open(file) as data_file:
json_from_file = json.load(data_file)
assert 'bar' in json_from_file
assert json_from_file['bar'] == 'foo'
# Remove the file
_clean_test_file(file)
View File
+362
View File
@@ -0,0 +1,362 @@
# pragma pylint: disable=missing-docstring, C0103, C0330
# pragma pylint: disable=protected-access, too-many-lines, invalid-name, too-many-arguments
import logging
import math
from unittest.mock import MagicMock
import arrow
import numpy as np
import pytest
from pandas import DataFrame, to_datetime
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.edge import Edge, PairInfo
from freqtrade.strategy.interface import SellType
from freqtrade.tests.conftest import get_patched_freqtradebot
from freqtrade.tests.optimize import (BTContainer, BTrade,
_build_backtest_dataframe,
_get_frame_time_from_offset)
# Cases to be tested:
# 1) Open trade should be removed from the end
# 2) Two complete trades within dataframe (with sell hit for all)
# 3) Entered, sl 1%, candle drops 8% => Trade closed, 1% loss
# 4) Entered, sl 3%, candle drops 4%, recovers to 1% => Trade closed, 3% loss
# 5) Stoploss and sell are hit. should sell on stoploss
####################################################################
ticker_start_time = arrow.get(2018, 10, 3)
ticker_interval_in_minute = 60
_ohlc = {'date': 0, 'buy': 1, 'open': 2, 'high': 3, 'low': 4, 'close': 5, 'sell': 6, 'volume': 7}
# Open trade should be removed from the end
tc0 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 1]], # enter trade (signal on last candle)
stop_loss=-0.99, roi=float('inf'), profit_perc=0.00,
trades=[]
)
# Two complete trades within dataframe(with sell hit for all)
tc1 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 1], # enter trade (signal on last candle)
[2, 5000, 5025, 4975, 4987, 6172, 0, 0], # exit at open
[3, 5000, 5025, 4975, 4987, 6172, 1, 0], # no action
[4, 5000, 5025, 4975, 4987, 6172, 0, 0], # should enter the trade
[5, 5000, 5025, 4975, 4987, 6172, 0, 1], # no action
[6, 5000, 5025, 4975, 4987, 6172, 0, 0], # should sell
],
stop_loss=-0.99, roi=float('inf'), profit_perc=0.00,
trades=[BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=1, close_tick=2),
BTrade(sell_reason=SellType.SELL_SIGNAL, open_tick=4, close_tick=6)]
)
# 3) Entered, sl 1%, candle drops 8% => Trade closed, 1% loss
tc2 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4600, 4987, 6172, 0, 0], # enter trade, stoploss hit
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
],
stop_loss=-0.01, roi=float('inf'), profit_perc=-0.01,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)]
)
# 4) Entered, sl 3 %, candle drops 4%, recovers to 1 % = > Trade closed, 3 % loss
tc3 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4800, 4987, 6172, 0, 0], # enter trade, stoploss hit
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
],
stop_loss=-0.03, roi=float('inf'), profit_perc=-0.03,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)]
)
# 5) Stoploss and sell are hit. should sell on stoploss
tc4 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4800, 4987, 6172, 0, 1], # enter trade, stoploss hit, sell signal
[2, 5000, 5025, 4975, 4987, 6172, 0, 0],
],
stop_loss=-0.03, roi=float('inf'), profit_perc=-0.03,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=1)]
)
TESTS = [
tc0,
tc1,
tc2,
tc3,
tc4
]
@pytest.mark.parametrize("data", TESTS)
def test_edge_results(edge_conf, mocker, caplog, data) -> None:
"""
run functional tests
"""
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
frame = _build_backtest_dataframe(data.data)
caplog.set_level(logging.DEBUG)
edge.fee = 0
trades = edge._find_trades_for_stoploss_range(frame, 'TEST/BTC', [data.stop_loss])
results = edge._fill_calculable_fields(DataFrame(trades)) if trades else DataFrame()
print(results)
assert len(trades) == len(data.trades)
if not results.empty:
assert round(results["profit_percent"].sum(), 3) == round(data.profit_perc, 3)
for c, trade in enumerate(data.trades):
res = results.iloc[c]
assert res.exit_type == trade.sell_reason
assert res.open_time == _get_frame_time_from_offset(trade.open_tick)
assert res.close_time == _get_frame_time_from_offset(trade.close_tick)
def test_adjust(mocker, edge_conf):
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={
'E/F': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
'C/D': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
'N/O': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60)
}
))
pairs = ['A/B', 'C/D', 'E/F', 'G/H']
assert(edge.adjust(pairs) == ['E/F', 'C/D'])
def test_stoploss(mocker, edge_conf):
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={
'E/F': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
'C/D': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
'N/O': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60)
}
))
assert edge.stoploss('E/F') == -0.01
def test_nonexisting_stoploss(mocker, edge_conf):
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={
'E/F': PairInfo(-0.01, 0.66, 3.71, 0.50, 1.71, 10, 60),
}
))
assert edge.stoploss('N/O') == -0.1
def test_stake_amount(mocker, edge_conf):
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={
'E/F': PairInfo(-0.02, 0.66, 3.71, 0.50, 1.71, 10, 60),
}
))
free = 100
total = 100
in_trade = 25
assert edge.stake_amount('E/F', free, total, in_trade) == 31.25
free = 20
total = 100
in_trade = 25
assert edge.stake_amount('E/F', free, total, in_trade) == 20
free = 0
total = 100
in_trade = 25
assert edge.stake_amount('E/F', free, total, in_trade) == 0
def test_nonexisting_stake_amount(mocker, edge_conf):
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={
'E/F': PairInfo(-0.11, 0.66, 3.71, 0.50, 1.71, 10, 60),
}
))
# should use strategy stoploss
assert edge.stake_amount('N/O', 1, 2, 1) == 0.15
def _validate_ohlc(buy_ohlc_sell_matrice):
for index, ohlc in enumerate(buy_ohlc_sell_matrice):
# if not high < open < low or not high < close < low
if not ohlc[3] >= ohlc[2] >= ohlc[4] or not ohlc[3] >= ohlc[5] >= ohlc[4]:
raise Exception('Line ' + str(index + 1) + ' of ohlc has invalid values!')
return True
def _build_dataframe(buy_ohlc_sell_matrice):
_validate_ohlc(buy_ohlc_sell_matrice)
tickers = []
for ohlc in buy_ohlc_sell_matrice:
ticker = {
'date': ticker_start_time.shift(
minutes=(
ohlc[0] *
ticker_interval_in_minute)).timestamp *
1000,
'buy': ohlc[1],
'open': ohlc[2],
'high': ohlc[3],
'low': ohlc[4],
'close': ohlc[5],
'sell': ohlc[6]}
tickers.append(ticker)
frame = DataFrame(tickers)
frame['date'] = to_datetime(frame['date'],
unit='ms',
utc=True,
infer_datetime_format=True)
return frame
def _time_on_candle(number):
return np.datetime64(ticker_start_time.shift(
minutes=(number * ticker_interval_in_minute)).timestamp * 1000, 'ms')
def test_edge_heartbeat_calculate(mocker, edge_conf):
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
heartbeat = edge_conf['edge']['process_throttle_secs']
# should not recalculate if heartbeat not reached
edge._last_updated = arrow.utcnow().timestamp - heartbeat + 1
assert edge.calculate() is False
def mocked_load_data(datadir, pairs=[], ticker_interval='0m', refresh_pairs=False,
timerange=None, exchange=None):
hz = 0.1
base = 0.001
ETHBTC = [
[
ticker_start_time.shift(minutes=(x * ticker_interval_in_minute)).timestamp * 1000,
math.sin(x * hz) / 1000 + base,
math.sin(x * hz) / 1000 + base + 0.0001,
math.sin(x * hz) / 1000 + base - 0.0001,
math.sin(x * hz) / 1000 + base,
123.45
] for x in range(0, 500)]
hz = 0.2
base = 0.002
LTCBTC = [
[
ticker_start_time.shift(minutes=(x * ticker_interval_in_minute)).timestamp * 1000,
math.sin(x * hz) / 1000 + base,
math.sin(x * hz) / 1000 + base + 0.0001,
math.sin(x * hz) / 1000 + base - 0.0001,
math.sin(x * hz) / 1000 + base,
123.45
] for x in range(0, 500)]
pairdata = {'NEO/BTC': parse_ticker_dataframe(ETHBTC),
'LTC/BTC': parse_ticker_dataframe(LTCBTC)}
return pairdata
def test_edge_process_downloaded_data(mocker, edge_conf):
edge_conf['datadir'] = None
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.001))
mocker.patch('freqtrade.data.history.load_data', mocked_load_data)
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
assert edge.calculate()
assert len(edge._cached_pairs) == 2
assert edge._last_updated <= arrow.utcnow().timestamp + 2
def test_process_expectancy(mocker, edge_conf):
edge_conf['edge']['min_trade_number'] = 2
freqtrade = get_patched_freqtradebot(mocker, edge_conf)
def get_fee():
return 0.001
freqtrade.exchange.get_fee = get_fee
edge = Edge(edge_conf, freqtrade.exchange, freqtrade.strategy)
trades = [
{'pair': 'TEST/BTC',
'stoploss': -0.9,
'profit_percent': '',
'profit_abs': '',
'open_time': np.datetime64('2018-10-03T00:05:00.000000000'),
'close_time': np.datetime64('2018-10-03T00:10:00.000000000'),
'open_index': 1,
'close_index': 1,
'trade_duration': '',
'open_rate': 17,
'close_rate': 17,
'exit_type': 'sell_signal'},
{'pair': 'TEST/BTC',
'stoploss': -0.9,
'profit_percent': '',
'profit_abs': '',
'open_time': np.datetime64('2018-10-03T00:20:00.000000000'),
'close_time': np.datetime64('2018-10-03T00:25:00.000000000'),
'open_index': 4,
'close_index': 4,
'trade_duration': '',
'open_rate': 20,
'close_rate': 20,
'exit_type': 'sell_signal'},
{'pair': 'TEST/BTC',
'stoploss': -0.9,
'profit_percent': '',
'profit_abs': '',
'open_time': np.datetime64('2018-10-03T00:30:00.000000000'),
'close_time': np.datetime64('2018-10-03T00:40:00.000000000'),
'open_index': 6,
'close_index': 7,
'trade_duration': '',
'open_rate': 26,
'close_rate': 34,
'exit_type': 'sell_signal'}
]
trades_df = DataFrame(trades)
trades_df = edge._fill_calculable_fields(trades_df)
final = edge._process_expectancy(trades_df)
assert len(final) == 1
assert 'TEST/BTC' in final
assert final['TEST/BTC'].stoploss == -0.9
assert round(final['TEST/BTC'].winrate, 10) == 0.3333333333
assert round(final['TEST/BTC'].risk_reward_ratio, 10) == 306.5384615384
assert round(final['TEST/BTC'].required_risk_reward, 10) == 2.0
assert round(final['TEST/BTC'].expectancy, 10) == 101.5128205128
File diff suppressed because it is too large Load Diff
+46
View File
@@ -0,0 +1,46 @@
from typing import NamedTuple, List
import arrow
from pandas import DataFrame
from freqtrade.strategy.interface import SellType
from freqtrade.constants import TICKER_INTERVAL_MINUTES
ticker_start_time = arrow.get(2018, 10, 3)
tests_ticker_interval = "1h"
class BTrade(NamedTuple):
"""
Minimalistic Trade result used for functional backtesting
"""
sell_reason: SellType
open_tick: int
close_tick: int
class BTContainer(NamedTuple):
"""
Minimal BacktestContainer defining Backtest inputs and results.
"""
data: List[float]
stop_loss: float
roi: float
trades: List[BTrade]
profit_perc: float
def _get_frame_time_from_offset(offset):
return ticker_start_time.shift(minutes=(offset * TICKER_INTERVAL_MINUTES[tests_ticker_interval])
).datetime.replace(tzinfo=None)
def _build_backtest_dataframe(ticker_with_signals):
columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'buy', 'sell']
frame = DataFrame.from_records(ticker_with_signals, columns=columns)
frame['date'] = frame['date'].apply(_get_frame_time_from_offset)
# Ensure floats are in place
for column in ['open', 'high', 'low', 'close', 'volume']:
frame[column] = frame[column].astype('float64')
return frame
@@ -0,0 +1,182 @@
# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, C0330, unused-argument
import logging
from unittest.mock import MagicMock
from pandas import DataFrame
import pytest
from freqtrade.optimize import get_timeframe
from freqtrade.optimize.backtesting import Backtesting
from freqtrade.strategy.interface import SellType
from freqtrade.tests.optimize import (BTrade, BTContainer, _build_backtest_dataframe,
_get_frame_time_from_offset, tests_ticker_interval)
from freqtrade.tests.conftest import patch_exchange
# Test 0 Minus 8% Close
# Test with Stop-loss at 1%
# TC1: Stop-Loss Triggered 1% loss
tc0 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
[2, 4987, 5012, 4600, 4600, 6172, 0, 0], # exit with stoploss hit
[3, 4975, 5000, 4980, 4977, 6172, 0, 0],
[4, 4977, 4987, 4977, 4995, 6172, 0, 0],
[5, 4995, 4995, 4995, 4950, 6172, 0, 0]],
stop_loss=-0.01, roi=1, profit_perc=-0.01,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)]
)
# Test 1 Minus 4% Low, minus 1% close
# Test with Stop-Loss at 3%
# TC2: Stop-Loss Triggered 3% Loss
tc1 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
[2, 4987, 5012, 4962, 4975, 6172, 0, 0],
[3, 4975, 5000, 4800, 4962, 6172, 0, 0], # exit with stoploss hit
[4, 4962, 4987, 4937, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.03, roi=1, profit_perc=-0.03,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=3)]
)
# Test 3 Candle drops 4%, Recovers 1%.
# Entry Criteria Met
# Candle drops 20%
# Candle Data for test 3
# Test with Stop-Loss at 2%
# TC3: Trade-A: Stop-Loss Triggered 2% Loss
# Trade-B: Stop-Loss Triggered 2% Loss
tc2 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
[2, 4987, 5012, 4800, 4975, 6172, 0, 0], # exit with stoploss hit
[3, 4975, 5000, 4950, 4962, 6172, 1, 0],
[4, 4975, 5000, 4950, 4962, 6172, 0, 0], # enter trade 2 (signal on last candle)
[5, 4962, 4987, 4000, 4000, 6172, 0, 0], # exit with stoploss hit
[6, 4950, 4975, 4975, 4950, 6172, 0, 0]],
stop_loss=-0.02, roi=1, profit_perc=-0.04,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2),
BTrade(sell_reason=SellType.STOP_LOSS, open_tick=4, close_tick=5)]
)
# Test 4 Minus 3% / recovery +15%
# Candle Data for test 3 Candle drops 3% Closed 15% up
# Test with Stop-loss at 2% ROI 6%
# TC4: Stop-Loss Triggered 2% Loss
tc3 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
[2, 4987, 5750, 4850, 5750, 6172, 0, 0], # Exit with stoploss hit
[3, 4975, 5000, 4950, 4962, 6172, 0, 0],
[4, 4962, 4987, 4937, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.02, roi=0.06, profit_perc=-0.02,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)]
)
# Test 4 / Drops 0.5% Closes +20%
# Set stop-loss at 1% ROI 3%
# TC5: ROI triggers 3% Gain
tc4 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4980, 4987, 6172, 1, 0],
[1, 5000, 5025, 4980, 4987, 6172, 0, 0], # enter trade (signal on last candle)
[2, 4987, 5025, 4975, 4987, 6172, 0, 0],
[3, 4975, 6000, 4975, 6000, 6172, 0, 0], # ROI
[4, 4962, 4987, 4972, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.01, roi=0.03, profit_perc=0.03,
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=3)]
)
# Test 6 / Drops 3% / Recovers 6% Positive / Closes 1% positve
# Candle Data for test 6
# Set stop-loss at 2% ROI at 5%
# TC6: Stop-Loss triggers 2% Loss
tc5 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 0], # enter trade (signal on last candle)
[2, 4987, 5300, 4850, 5050, 6172, 0, 0], # Exit with stoploss
[3, 4975, 5000, 4950, 4962, 6172, 0, 0],
[4, 4962, 4987, 4972, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.02, roi=0.05, profit_perc=-0.02,
trades=[BTrade(sell_reason=SellType.STOP_LOSS, open_tick=1, close_tick=2)]
)
# Test 7 - 6% Positive / 1% Negative / Close 1% Positve
# Candle Data for test 7
# Set stop-loss at 2% ROI at 3%
# TC7: ROI Triggers 3% Gain
tc6 = BTContainer(data=[
# D O H L C V B S
[0, 5000, 5025, 4975, 4987, 6172, 1, 0],
[1, 5000, 5025, 4975, 4987, 6172, 0, 0],
[2, 4987, 5300, 4950, 5050, 6172, 0, 0],
[3, 4975, 5000, 4950, 4962, 6172, 0, 0],
[4, 4962, 4987, 4972, 4950, 6172, 0, 0],
[5, 4950, 4975, 4925, 4950, 6172, 0, 0]],
stop_loss=-0.02, roi=0.03, profit_perc=0.03,
trades=[BTrade(sell_reason=SellType.ROI, open_tick=1, close_tick=2)]
)
TESTS = [
tc0,
tc1,
tc2,
tc3,
tc4,
tc5,
tc6,
]
@pytest.mark.parametrize("data", TESTS)
def test_backtest_results(default_conf, fee, mocker, caplog, data) -> None:
"""
run functional tests
"""
default_conf["stoploss"] = data.stop_loss
default_conf["minimal_roi"] = {"0": data.roi}
default_conf['ticker_interval'] = tests_ticker_interval
mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.0))
patch_exchange(mocker)
frame = _build_backtest_dataframe(data.data)
backtesting = Backtesting(default_conf)
backtesting.advise_buy = lambda a, m: frame
backtesting.advise_sell = lambda a, m: frame
caplog.set_level(logging.DEBUG)
pair = 'UNITTEST/BTC'
# Dummy data as we mock the analyze functions
data_processed = {pair: DataFrame()}
min_date, max_date = get_timeframe({pair: frame})
results = backtesting.backtest(
{
'stake_amount': default_conf['stake_amount'],
'processed': data_processed,
'max_open_trades': 10,
'start_date': min_date,
'end_date': max_date,
}
)
print(results.T)
assert len(results) == len(data.trades)
assert round(results["profit_percent"].sum(), 3) == round(data.profit_perc, 3)
for c, trade in enumerate(data.trades):
res = results.iloc[c]
assert res.sell_reason == trade.sell_reason
assert res.open_time == _get_frame_time_from_offset(trade.open_tick)
assert res.close_time == _get_frame_time_from_offset(trade.close_tick)
@@ -0,0 +1,904 @@
# pragma pylint: disable=missing-docstring, W0212, line-too-long, C0103, unused-argument
import json
import math
import random
from typing import List
from unittest.mock import MagicMock
import numpy as np
import pandas as pd
import pytest
from arrow import Arrow
from freqtrade import DependencyException, constants
from freqtrade.arguments import Arguments, TimeRange
from freqtrade.data import history
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.optimize import get_timeframe
from freqtrade.optimize.backtesting import (Backtesting, setup_configuration,
start)
from freqtrade.strategy.default_strategy import DefaultStrategy
from freqtrade.strategy.interface import SellType
from freqtrade.tests.conftest import log_has, patch_exchange
def get_args(args) -> List[str]:
return Arguments(args, '').get_parsed_arg()
def trim_dictlist(dict_list, num):
new = {}
for pair, pair_data in dict_list.items():
new[pair] = pair_data[num:]
return new
def load_data_test(what):
timerange = TimeRange(None, 'line', 0, -101)
pair = history.load_tickerdata_file(None, ticker_interval='1m',
pair='UNITTEST/BTC', timerange=timerange)
datalen = len(pair)
base = 0.001
if what == 'raise':
data = [
[
pair[x][0], # Keep old dates
x * base, # But replace O,H,L,C
x * base + 0.0001,
x * base - 0.0001,
x * base,
pair[x][5], # Keep old volume
] for x in range(0, datalen)
]
if what == 'lower':
data = [
[
pair[x][0], # Keep old dates
1 - x * base, # But replace O,H,L,C
1 - x * base + 0.0001,
1 - x * base - 0.0001,
1 - x * base,
pair[x][5] # Keep old volume
] for x in range(0, datalen)
]
if what == 'sine':
hz = 0.1 # frequency
data = [
[
pair[x][0], # Keep old dates
math.sin(x * hz) / 1000 + base, # But replace O,H,L,C
math.sin(x * hz) / 1000 + base + 0.0001,
math.sin(x * hz) / 1000 + base - 0.0001,
math.sin(x * hz) / 1000 + base,
pair[x][5] # Keep old volume
] for x in range(0, datalen)
]
return {'UNITTEST/BTC': parse_ticker_dataframe(data)}
def simple_backtest(config, contour, num_results, mocker) -> None:
patch_exchange(mocker)
config['ticker_interval'] = '1m'
backtesting = Backtesting(config)
data = load_data_test(contour)
processed = backtesting.strategy.tickerdata_to_dataframe(data)
min_date, max_date = get_timeframe(processed)
assert isinstance(processed, dict)
results = backtesting.backtest(
{
'stake_amount': config['stake_amount'],
'processed': processed,
'max_open_trades': 1,
'position_stacking': False,
'start_date': min_date,
'end_date': max_date,
}
)
# results :: <class 'pandas.core.frame.DataFrame'>
assert len(results) == num_results
def mocked_load_data(datadir, pairs=[], ticker_interval='0m', refresh_pairs=False,
timerange=None, exchange=None):
tickerdata = history.load_tickerdata_file(datadir, 'UNITTEST/BTC', '1m', timerange=timerange)
pairdata = {'UNITTEST/BTC': parse_ticker_dataframe(tickerdata)}
return pairdata
# use for mock ccxt.fetch_ohlvc'
def _load_pair_as_ticks(pair, tickfreq):
ticks = history.load_tickerdata_file(None, ticker_interval=tickfreq, pair=pair)
ticks = ticks[-201:]
return ticks
# FIX: fixturize this?
def _make_backtest_conf(mocker, conf=None, pair='UNITTEST/BTC', record=None):
data = history.load_data(datadir=None, ticker_interval='1m', pairs=[pair])
data = trim_dictlist(data, -201)
patch_exchange(mocker)
backtesting = Backtesting(conf)
processed = backtesting.strategy.tickerdata_to_dataframe(data)
min_date, max_date = get_timeframe(processed)
return {
'stake_amount': conf['stake_amount'],
'processed': processed,
'max_open_trades': 10,
'position_stacking': False,
'record': record,
'start_date': min_date,
'end_date': max_date,
}
def _trend(signals, buy_value, sell_value):
n = len(signals['low'])
buy = np.zeros(n)
sell = np.zeros(n)
for i in range(0, len(signals['buy'])):
if random.random() > 0.5: # Both buy and sell signals at same timeframe
buy[i] = buy_value
sell[i] = sell_value
signals['buy'] = buy
signals['sell'] = sell
return signals
def _trend_alternate(dataframe=None, metadata=None):
signals = dataframe
low = signals['low']
n = len(low)
buy = np.zeros(n)
sell = np.zeros(n)
for i in range(0, len(buy)):
if i % 2 == 0:
buy[i] = 1
else:
sell[i] = 1
signals['buy'] = buy
signals['sell'] = sell
return dataframe
# Unit tests
def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'backtesting'
]
config = setup_configuration(get_args(args))
assert 'max_open_trades' in config
assert 'stake_currency' in config
assert 'stake_amount' in config
assert 'exchange' in config
assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config
assert log_has(
'Using data folder: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config
assert not log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples)
assert 'live' not in config
assert not log_has('Parameter -l/--live detected ...', caplog.record_tuples)
assert 'position_stacking' not in config
assert not log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples)
assert 'refresh_pairs' not in config
assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples)
assert 'timerange' not in config
assert 'export' not in config
def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'--datadir', '/foo/bar',
'backtesting',
'--ticker-interval', '1m',
'--live',
'--enable-position-stacking',
'--disable-max-market-positions',
'--refresh-pairs-cached',
'--timerange', ':100',
'--export', '/bar/foo',
'--export-filename', 'foo_bar.json'
]
config = setup_configuration(get_args(args))
assert 'max_open_trades' in config
assert 'stake_currency' in config
assert 'stake_amount' in config
assert 'exchange' in config
assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config
assert log_has(
'Using data folder: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples)
assert log_has(
'Using ticker_interval: 1m ...',
caplog.record_tuples
)
assert 'live' in config
assert log_has('Parameter -l/--live detected ...', caplog.record_tuples)
assert 'position_stacking' in config
assert log_has('Parameter --enable-position-stacking detected ...', caplog.record_tuples)
assert 'use_max_market_positions' in config
assert log_has('Parameter --disable-max-market-positions detected ...', caplog.record_tuples)
assert log_has('max_open_trades set to unlimited ...', caplog.record_tuples)
assert 'refresh_pairs' in config
assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples)
assert 'timerange' in config
assert log_has(
'Parameter --timerange detected: {} ...'.format(config['timerange']),
caplog.record_tuples
)
assert 'export' in config
assert log_has(
'Parameter --export detected: {} ...'.format(config['export']),
caplog.record_tuples
)
assert 'exportfilename' in config
assert log_has(
'Storing backtest results to {} ...'.format(config['exportfilename']),
caplog.record_tuples
)
def test_setup_configuration_unlimited_stake_amount(mocker, default_conf, caplog) -> None:
default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'backtesting'
]
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
setup_configuration(get_args(args))
def test_start(mocker, fee, default_conf, caplog) -> None:
start_mock = MagicMock()
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
patch_exchange(mocker)
mocker.patch('freqtrade.optimize.backtesting.Backtesting.start', start_mock)
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'backtesting'
]
args = get_args(args)
start(args)
assert log_has(
'Starting freqtrade in Backtesting mode',
caplog.record_tuples
)
assert start_mock.call_count == 1
def test_backtesting_init(mocker, default_conf) -> None:
patch_exchange(mocker)
get_fee = mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5))
backtesting = Backtesting(default_conf)
assert backtesting.config == default_conf
assert backtesting.ticker_interval == '5m'
assert callable(backtesting.strategy.tickerdata_to_dataframe)
assert callable(backtesting.advise_buy)
assert callable(backtesting.advise_sell)
get_fee.assert_called()
assert backtesting.fee == 0.5
def test_tickerdata_to_dataframe(default_conf, mocker) -> None:
patch_exchange(mocker)
timerange = TimeRange(None, 'line', 0, -100)
tick = history.load_tickerdata_file(None, 'UNITTEST/BTC', '1m', timerange=timerange)
tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick)}
backtesting = Backtesting(default_conf)
data = backtesting.strategy.tickerdata_to_dataframe(tickerlist)
assert len(data['UNITTEST/BTC']) == 99
# Load strategy to compare the result between Backtesting function and strategy are the same
strategy = DefaultStrategy(default_conf)
data2 = strategy.tickerdata_to_dataframe(tickerlist)
assert data['UNITTEST/BTC'].equals(data2['UNITTEST/BTC'])
def test_generate_text_table(default_conf, mocker):
patch_exchange(mocker)
backtesting = Backtesting(default_conf)
results = pd.DataFrame(
{
'pair': ['ETH/BTC', 'ETH/BTC'],
'profit_percent': [0.1, 0.2],
'profit_abs': [0.2, 0.4],
'trade_duration': [10, 30],
'profit': [2, 0],
'loss': [0, 0]
}
)
result_str = (
'| pair | buy count | avg profit % | cum profit % | '
'total profit BTC | avg duration | profit | loss |\n'
'|:--------|------------:|---------------:|---------------:|'
'-------------------:|:---------------|---------:|-------:|\n'
'| ETH/BTC | 2 | 15.00 | 30.00 | '
'0.60000000 | 0:20:00 | 2 | 0 |\n'
'| TOTAL | 2 | 15.00 | 30.00 | '
'0.60000000 | 0:20:00 | 2 | 0 |'
)
assert backtesting._generate_text_table(data={'ETH/BTC': {}}, results=results) == result_str
def test_generate_text_table_sell_reason(default_conf, mocker):
patch_exchange(mocker)
backtesting = Backtesting(default_conf)
results = pd.DataFrame(
{
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_percent': [0.1, 0.2, 0.3],
'profit_abs': [0.2, 0.4, 0.5],
'trade_duration': [10, 30, 10],
'profit': [2, 0, 0],
'loss': [0, 0, 1],
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
}
)
result_str = (
'| Sell Reason | Count |\n'
'|:--------------|--------:|\n'
'| roi | 2 |\n'
'| stop_loss | 1 |'
)
assert backtesting._generate_text_table_sell_reason(
data={'ETH/BTC': {}}, results=results) == result_str
def test_generate_text_table_strategyn(default_conf, mocker):
"""
Test Backtesting.generate_text_table_sell_reason() method
"""
patch_exchange(mocker)
backtesting = Backtesting(default_conf)
results = {}
results['ETH/BTC'] = pd.DataFrame(
{
'pair': ['ETH/BTC', 'ETH/BTC', 'ETH/BTC'],
'profit_percent': [0.1, 0.2, 0.3],
'profit_abs': [0.2, 0.4, 0.5],
'trade_duration': [10, 30, 10],
'profit': [2, 0, 0],
'loss': [0, 0, 1],
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
}
)
results['LTC/BTC'] = pd.DataFrame(
{
'pair': ['LTC/BTC', 'LTC/BTC', 'LTC/BTC'],
'profit_percent': [0.4, 0.2, 0.3],
'profit_abs': [0.4, 0.4, 0.5],
'trade_duration': [15, 30, 15],
'profit': [4, 1, 0],
'loss': [0, 0, 1],
'sell_reason': [SellType.ROI, SellType.ROI, SellType.STOP_LOSS]
}
)
result_str = (
'| Strategy | buy count | avg profit % | cum profit % '
'| total profit BTC | avg duration | profit | loss |\n'
'|:-----------|------------:|---------------:|---------------:'
'|-------------------:|:---------------|---------:|-------:|\n'
'| ETH/BTC | 3 | 20.00 | 60.00 '
'| 1.10000000 | 0:17:00 | 3 | 0 |\n'
'| LTC/BTC | 3 | 30.00 | 90.00 '
'| 1.30000000 | 0:20:00 | 3 | 0 |'
)
print(backtesting._generate_text_table_strategy(all_results=results))
assert backtesting._generate_text_table_strategy(all_results=results) == result_str
def test_backtesting_start(default_conf, mocker, caplog) -> None:
def get_timeframe(input1):
return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
mocker.patch('freqtrade.data.history.load_data', mocked_load_data)
mocker.patch('freqtrade.optimize.get_timeframe', get_timeframe)
mocker.patch('freqtrade.exchange.Exchange.refresh_tickers', MagicMock())
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.optimize.backtesting.Backtesting',
backtest=MagicMock(),
_generate_text_table=MagicMock(return_value='1'),
)
default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
default_conf['ticker_interval'] = '1m'
default_conf['live'] = False
default_conf['datadir'] = None
default_conf['export'] = None
default_conf['timerange'] = '-100'
backtesting = Backtesting(default_conf)
backtesting.start()
# check the logs, that will contain the backtest result
exists = [
'Using local backtesting data (using whitelist in given config) ...',
'Using stake_currency: BTC ...',
'Using stake_amount: 0.001 ...',
'Measuring data from 2017-11-14T21:17:00+00:00 '
'up to 2017-11-14T22:59:00+00:00 (0 days)..'
]
for line in exists:
assert log_has(line, caplog.record_tuples)
def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None:
def get_timeframe(input1):
return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
mocker.patch('freqtrade.data.history.load_data', MagicMock(return_value={}))
mocker.patch('freqtrade.optimize.get_timeframe', get_timeframe)
mocker.patch('freqtrade.exchange.Exchange.refresh_tickers', MagicMock())
patch_exchange(mocker)
mocker.patch.multiple(
'freqtrade.optimize.backtesting.Backtesting',
backtest=MagicMock(),
_generate_text_table=MagicMock(return_value='1'),
)
default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
default_conf['ticker_interval'] = "1m"
default_conf['live'] = False
default_conf['datadir'] = None
default_conf['export'] = None
default_conf['timerange'] = '20180101-20180102'
backtesting = Backtesting(default_conf)
backtesting.start()
# check the logs, that will contain the backtest result
assert log_has('No data found. Terminating.', caplog.record_tuples)
def test_backtest(default_conf, fee, mocker) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
patch_exchange(mocker)
backtesting = Backtesting(default_conf)
pair = 'UNITTEST/BTC'
timerange = TimeRange(None, 'line', 0, -201)
data = history.load_data(datadir=None, ticker_interval='5m', pairs=['UNITTEST/BTC'],
timerange=timerange)
data_processed = backtesting.strategy.tickerdata_to_dataframe(data)
min_date, max_date = get_timeframe(data_processed)
results = backtesting.backtest(
{
'stake_amount': default_conf['stake_amount'],
'processed': data_processed,
'max_open_trades': 10,
'position_stacking': False,
'start_date': min_date,
'end_date': max_date,
}
)
assert not results.empty
assert len(results) == 2
expected = pd.DataFrame(
{'pair': [pair, pair],
'profit_percent': [0.0, 0.0],
'profit_abs': [0.0, 0.0],
'open_time': [Arrow(2018, 1, 29, 18, 40, 0).datetime,
Arrow(2018, 1, 30, 3, 30, 0).datetime],
'close_time': [Arrow(2018, 1, 29, 22, 35, 0).datetime,
Arrow(2018, 1, 30, 4, 15, 0).datetime],
'open_index': [78, 184],
'close_index': [125, 193],
'trade_duration': [235, 45],
'open_at_end': [False, False],
'open_rate': [0.104445, 0.10302485],
'close_rate': [0.104969, 0.103541],
'sell_reason': [SellType.ROI, SellType.ROI]
})
pd.testing.assert_frame_equal(results, expected)
data_pair = data_processed[pair]
for _, t in results.iterrows():
ln = data_pair.loc[data_pair["date"] == t["open_time"]]
# Check open trade rate alignes to open rate
assert ln is not None
assert round(ln.iloc[0]["open"], 6) == round(t["open_rate"], 6)
# check close trade rate alignes to close rate or is between high and low
ln = data_pair.loc[data_pair["date"] == t["close_time"]]
assert (round(ln.iloc[0]["open"], 6) == round(t["close_rate"], 6) or
round(ln.iloc[0]["low"], 6) < round(
t["close_rate"], 6) < round(ln.iloc[0]["high"], 6))
def test_backtest_1min_ticker_interval(default_conf, fee, mocker) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
patch_exchange(mocker)
backtesting = Backtesting(default_conf)
# Run a backtesting for an exiting 1min ticker_interval
timerange = TimeRange(None, 'line', 0, -200)
data = history.load_data(datadir=None, ticker_interval='1m', pairs=['UNITTEST/BTC'],
timerange=timerange)
processed = backtesting.strategy.tickerdata_to_dataframe(data)
min_date, max_date = get_timeframe(processed)
results = backtesting.backtest(
{
'stake_amount': default_conf['stake_amount'],
'processed': processed,
'max_open_trades': 1,
'position_stacking': False,
'start_date': min_date,
'end_date': max_date,
}
)
assert not results.empty
assert len(results) == 1
def test_processed(default_conf, mocker) -> None:
patch_exchange(mocker)
backtesting = Backtesting(default_conf)
dict_of_tickerrows = load_data_test('raise')
dataframes = backtesting.strategy.tickerdata_to_dataframe(dict_of_tickerrows)
dataframe = dataframes['UNITTEST/BTC']
cols = dataframe.columns
# assert the dataframe got some of the indicator columns
for col in ['close', 'high', 'low', 'open', 'date',
'ema50', 'ao', 'macd', 'plus_dm']:
assert col in cols
def test_backtest_pricecontours(default_conf, fee, mocker) -> None:
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
tests = [['raise', 18], ['lower', 0], ['sine', 19]]
# We need to enable sell-signal - otherwise it sells on ROI!!
default_conf['experimental'] = {"use_sell_signal": True}
for [contour, numres] in tests:
simple_backtest(default_conf, contour, numres, mocker)
def test_backtest_clash_buy_sell(mocker, default_conf):
# Override the default buy trend function in our default_strategy
def fun(dataframe=None, pair=None):
buy_value = 1
sell_value = 1
return _trend(dataframe, buy_value, sell_value)
backtest_conf = _make_backtest_conf(mocker, conf=default_conf)
backtesting = Backtesting(default_conf)
backtesting.advise_buy = fun # Override
backtesting.advise_sell = fun # Override
results = backtesting.backtest(backtest_conf)
assert results.empty
def test_backtest_only_sell(mocker, default_conf):
# Override the default buy trend function in our default_strategy
def fun(dataframe=None, pair=None):
buy_value = 0
sell_value = 1
return _trend(dataframe, buy_value, sell_value)
backtest_conf = _make_backtest_conf(mocker, conf=default_conf)
backtesting = Backtesting(default_conf)
backtesting.advise_buy = fun # Override
backtesting.advise_sell = fun # Override
results = backtesting.backtest(backtest_conf)
assert results.empty
def test_backtest_alternate_buy_sell(default_conf, fee, mocker):
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch('freqtrade.optimize.backtesting.file_dump_json', MagicMock())
backtest_conf = _make_backtest_conf(mocker, conf=default_conf, pair='UNITTEST/BTC')
# We need to enable sell-signal - otherwise it sells on ROI!!
default_conf['experimental'] = {"use_sell_signal": True}
default_conf['ticker_interval'] = '1m'
backtesting = Backtesting(default_conf)
backtesting.advise_buy = _trend_alternate # Override
backtesting.advise_sell = _trend_alternate # Override
results = backtesting.backtest(backtest_conf)
backtesting._store_backtest_result("test_.json", results)
# 200 candles in backtest data
# won't buy on first (shifted by 1)
# 100 buys signals
assert len(results) == 100
# One trade was force-closed at the end
assert len(results.loc[results.open_at_end]) == 0
def test_backtest_multi_pair(default_conf, fee, mocker):
def evaluate_result_multi(results, freq, max_open_trades):
# Find overlapping trades by expanding each trade once per period
# and then counting overlaps
dates = [pd.Series(pd.date_range(row[1].open_time, row[1].close_time, freq=freq))
for row in results[['open_time', 'close_time']].iterrows()]
deltas = [len(x) for x in dates]
dates = pd.Series(pd.concat(dates).values, name='date')
df2 = pd.DataFrame(np.repeat(results.values, deltas, axis=0), columns=results.columns)
df2 = df2.astype(dtype={"open_time": "datetime64", "close_time": "datetime64"})
df2 = pd.concat([dates, df2], axis=1)
df2 = df2.set_index('date')
df_final = df2.resample(freq)[['pair']].count()
return df_final[df_final['pair'] > max_open_trades]
def _trend_alternate_hold(dataframe=None, metadata=None):
"""
Buy every 8th candle - sell every other 8th -2 (hold on to pairs a bit)
"""
multi = 8
dataframe['buy'] = np.where(dataframe.index % multi == 0, 1, 0)
dataframe['sell'] = np.where((dataframe.index + multi - 2) % multi == 0, 1, 0)
if metadata['pair'] in('ETH/BTC', 'LTC/BTC'):
dataframe['buy'] = dataframe['buy'].shift(-4)
dataframe['sell'] = dataframe['sell'].shift(-4)
return dataframe
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
patch_exchange(mocker)
pairs = ['ADA/BTC', 'DASH/BTC', 'ETH/BTC', 'LTC/BTC', 'NXT/BTC']
data = history.load_data(datadir=None, ticker_interval='5m', pairs=pairs)
data = trim_dictlist(data, -500)
# We need to enable sell-signal - otherwise it sells on ROI!!
default_conf['experimental'] = {"use_sell_signal": True}
default_conf['ticker_interval'] = '5m'
backtesting = Backtesting(default_conf)
backtesting.advise_buy = _trend_alternate_hold # Override
backtesting.advise_sell = _trend_alternate_hold # Override
data_processed = backtesting.strategy.tickerdata_to_dataframe(data)
min_date, max_date = get_timeframe(data_processed)
backtest_conf = {
'stake_amount': default_conf['stake_amount'],
'processed': data_processed,
'max_open_trades': 3,
'position_stacking': False,
'start_date': min_date,
'end_date': max_date,
}
results = backtesting.backtest(backtest_conf)
# Make sure we have parallel trades
assert len(evaluate_result_multi(results, '5min', 2)) > 0
# make sure we don't have trades with more than configured max_open_trades
assert len(evaluate_result_multi(results, '5min', 3)) == 0
backtest_conf = {
'stake_amount': default_conf['stake_amount'],
'processed': data_processed,
'max_open_trades': 1,
'position_stacking': False,
'start_date': min_date,
'end_date': max_date,
}
results = backtesting.backtest(backtest_conf)
assert len(evaluate_result_multi(results, '5min', 1)) == 0
def test_backtest_record(default_conf, fee, mocker):
names = []
records = []
patch_exchange(mocker)
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
mocker.patch(
'freqtrade.optimize.backtesting.file_dump_json',
new=lambda n, r: (names.append(n), records.append(r))
)
backtesting = Backtesting(default_conf)
results = pd.DataFrame({"pair": ["UNITTEST/BTC", "UNITTEST/BTC",
"UNITTEST/BTC", "UNITTEST/BTC"],
"profit_percent": [0.003312, 0.010801, 0.013803, 0.002780],
"profit_abs": [0.000003, 0.000011, 0.000014, 0.000003],
"open_time": [Arrow(2017, 11, 14, 19, 32, 00).datetime,
Arrow(2017, 11, 14, 21, 36, 00).datetime,
Arrow(2017, 11, 14, 22, 12, 00).datetime,
Arrow(2017, 11, 14, 22, 44, 00).datetime],
"close_time": [Arrow(2017, 11, 14, 21, 35, 00).datetime,
Arrow(2017, 11, 14, 22, 10, 00).datetime,
Arrow(2017, 11, 14, 22, 43, 00).datetime,
Arrow(2017, 11, 14, 22, 58, 00).datetime],
"open_rate": [0.002543, 0.003003, 0.003089, 0.003214],
"close_rate": [0.002546, 0.003014, 0.003103, 0.003217],
"open_index": [1, 119, 153, 185],
"close_index": [118, 151, 184, 199],
"trade_duration": [123, 34, 31, 14],
"open_at_end": [False, False, False, True],
"sell_reason": [SellType.ROI, SellType.STOP_LOSS,
SellType.ROI, SellType.FORCE_SELL]
})
backtesting._store_backtest_result("backtest-result.json", results)
assert len(results) == 4
# Assert file_dump_json was only called once
assert names == ['backtest-result.json']
records = records[0]
# Ensure records are of correct type
assert len(records) == 4
# reset test to test with strategy name
names = []
records = []
backtesting._store_backtest_result("backtest-result.json", results, "DefStrat")
assert len(results) == 4
# Assert file_dump_json was only called once
assert names == ['backtest-result-DefStrat.json']
records = records[0]
# Ensure records are of correct type
assert len(records) == 4
# ('UNITTEST/BTC', 0.00331158, '1510684320', '1510691700', 0, 117)
# Below follows just a typecheck of the schema/type of trade-records
oix = None
for (pair, profit, date_buy, date_sell, buy_index, dur,
openr, closer, open_at_end, sell_reason) in records:
assert pair == 'UNITTEST/BTC'
assert isinstance(profit, float)
# FIX: buy/sell should be converted to ints
assert isinstance(date_buy, float)
assert isinstance(date_sell, float)
assert isinstance(openr, float)
assert isinstance(closer, float)
assert isinstance(open_at_end, bool)
assert isinstance(sell_reason, str)
isinstance(buy_index, pd._libs.tslib.Timestamp)
if oix:
assert buy_index > oix
oix = buy_index
assert dur > 0
def test_backtest_start_live(default_conf, mocker, caplog):
default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
async def load_pairs(pair, timeframe, since):
return _load_pair_as_ticks(pair, timeframe)
api_mock = MagicMock()
api_mock.fetch_ohlcv = load_pairs
patch_exchange(mocker, api_mock)
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table', MagicMock())
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'--datadir', 'freqtrade/tests/testdata',
'backtesting',
'--ticker-interval', '1m',
'--live',
'--timerange', '-100',
'--enable-position-stacking',
'--disable-max-market-positions'
]
args = get_args(args)
start(args)
# check the logs, that will contain the backtest result
exists = [
'Parameter -i/--ticker-interval detected ...',
'Using ticker_interval: 1m ...',
'Parameter -l/--live detected ...',
'Ignoring max_open_trades (--disable-max-market-positions was used) ...',
'Parameter --timerange detected: -100 ...',
'Using data folder: freqtrade/tests/testdata ...',
'Using stake_currency: BTC ...',
'Using stake_amount: 0.001 ...',
'Downloading data for all pairs in whitelist ...',
'Measuring data from 2017-11-14T19:31:00+00:00 up to 2017-11-14T22:58:00+00:00 (0 days)..',
'Parameter --enable-position-stacking detected ...'
]
for line in exists:
assert log_has(line, caplog.record_tuples)
def test_backtest_start_multi_strat(default_conf, mocker, caplog):
default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
async def load_pairs(pair, timeframe, since):
return _load_pair_as_ticks(pair, timeframe)
api_mock = MagicMock()
api_mock.fetch_ohlcv = load_pairs
patch_exchange(mocker, api_mock)
backtestmock = MagicMock()
mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', backtestmock)
gen_table_mock = MagicMock()
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table', gen_table_mock)
gen_strattable_mock = MagicMock()
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table_strategy',
gen_strattable_mock)
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
args = [
'--config', 'config.json',
'--datadir', 'freqtrade/tests/testdata',
'backtesting',
'--ticker-interval', '1m',
'--live',
'--timerange', '-100',
'--enable-position-stacking',
'--disable-max-market-positions',
'--strategy-list',
'DefaultStrategy',
'TestStrategy',
]
args = get_args(args)
start(args)
# 2 backtests, 4 tables
assert backtestmock.call_count == 2
assert gen_table_mock.call_count == 4
assert gen_strattable_mock.call_count == 1
# check the logs, that will contain the backtest result
exists = [
'Parameter -i/--ticker-interval detected ...',
'Using ticker_interval: 1m ...',
'Parameter -l/--live detected ...',
'Ignoring max_open_trades (--disable-max-market-positions was used) ...',
'Parameter --timerange detected: -100 ...',
'Using data folder: freqtrade/tests/testdata ...',
'Using stake_currency: BTC ...',
'Using stake_amount: 0.001 ...',
'Downloading data for all pairs in whitelist ...',
'Measuring data from 2017-11-14T19:31:00+00:00 up to 2017-11-14T22:58:00+00:00 (0 days)..',
'Parameter --enable-position-stacking detected ...',
'Running backtesting for Strategy DefaultStrategy',
'Running backtesting for Strategy TestStrategy',
]
for line in exists:
assert log_has(line, caplog.record_tuples)
+131
View File
@@ -0,0 +1,131 @@
# pragma pylint: disable=missing-docstring, C0103, C0330
# pragma pylint: disable=protected-access, too-many-lines, invalid-name, too-many-arguments
from unittest.mock import MagicMock
import json
from typing import List
from freqtrade.edge import PairInfo
from freqtrade.arguments import Arguments
from freqtrade.optimize.edge_cli import (EdgeCli, setup_configuration, start)
from freqtrade.tests.conftest import log_has, patch_exchange
def get_args(args) -> List[str]:
return Arguments(args, '').get_parsed_arg()
def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf)
))
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'edge'
]
config = setup_configuration(get_args(args))
assert 'max_open_trades' in config
assert 'stake_currency' in config
assert 'stake_amount' in config
assert 'exchange' in config
assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config
assert log_has(
'Using data folder: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config
assert not log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples)
assert 'refresh_pairs' not in config
assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples)
assert 'timerange' not in config
assert 'stoploss_range' not in config
def test_setup_configuration_with_arguments(mocker, edge_conf, caplog) -> None:
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(edge_conf)
))
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'--datadir', '/foo/bar',
'edge',
'--ticker-interval', '1m',
'--refresh-pairs-cached',
'--timerange', ':100',
'--stoplosses=-0.01,-0.10,-0.001'
]
config = setup_configuration(get_args(args))
assert 'max_open_trades' in config
assert 'stake_currency' in config
assert 'stake_amount' in config
assert 'exchange' in config
assert 'pair_whitelist' in config['exchange']
assert 'datadir' in config
assert log_has(
'Using data folder: {} ...'.format(config['datadir']),
caplog.record_tuples
)
assert 'ticker_interval' in config
assert log_has('Parameter -i/--ticker-interval detected ...', caplog.record_tuples)
assert log_has(
'Using ticker_interval: 1m ...',
caplog.record_tuples
)
assert 'refresh_pairs' in config
assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples)
assert 'timerange' in config
assert log_has(
'Parameter --timerange detected: {} ...'.format(config['timerange']),
caplog.record_tuples
)
def test_start(mocker, fee, edge_conf, caplog) -> None:
start_mock = MagicMock()
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
patch_exchange(mocker)
mocker.patch('freqtrade.optimize.edge_cli.EdgeCli.start', start_mock)
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(edge_conf)
))
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'edge'
]
args = get_args(args)
start(args)
assert log_has(
'Starting freqtrade in Edge mode',
caplog.record_tuples
)
assert start_mock.call_count == 1
def test_edge_init(mocker, edge_conf) -> None:
patch_exchange(mocker)
edge_cli = EdgeCli(edge_conf)
assert edge_cli.config == edge_conf
assert callable(edge_cli.edge.calculate)
def test_generate_edge_table(edge_conf, mocker):
patch_exchange(mocker)
edge_cli = EdgeCli(edge_conf)
results = {}
results['ETH/BTC'] = PairInfo(-0.01, 0.60, 2, 1, 3, 10, 60)
assert edge_cli._generate_edge_table(results).count(':|') == 7
assert edge_cli._generate_edge_table(results).count('| ETH/BTC |') == 1
assert edge_cli._generate_edge_table(results).count(
'| risk reward ratio | required risk reward | expectancy |') == 1
+332
View File
@@ -0,0 +1,332 @@
# pragma pylint: disable=missing-docstring,W0212,C0103
from datetime import datetime
import os
from unittest.mock import MagicMock
import pandas as pd
import pytest
from freqtrade.data.converter import parse_ticker_dataframe
from freqtrade.data.history import load_tickerdata_file
from freqtrade.optimize.hyperopt import Hyperopt, start
from freqtrade.resolvers import StrategyResolver
from freqtrade.tests.conftest import log_has, patch_exchange
from freqtrade.tests.optimize.test_backtesting import get_args
@pytest.fixture(scope='function')
def hyperopt(default_conf, mocker):
patch_exchange(mocker)
return Hyperopt(default_conf)
# Functions for recurrent object patching
def create_trials(mocker, hyperopt) -> None:
"""
When creating trials, mock the hyperopt Trials so that *by default*
- we don't create any pickle'd files in the filesystem
- we might have a pickle'd file so make sure that we return
false when looking for it
"""
hyperopt.trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle')
mocker.patch('freqtrade.optimize.hyperopt.os.path.exists', return_value=False)
mocker.patch('freqtrade.optimize.hyperopt.os.path.getsize', return_value=1)
mocker.patch('freqtrade.optimize.hyperopt.os.remove', return_value=True)
mocker.patch('freqtrade.optimize.hyperopt.dump', return_value=None)
return [{'loss': 1, 'result': 'foo', 'params': {}}]
def test_start(mocker, default_conf, caplog) -> None:
start_mock = MagicMock()
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
patch_exchange(mocker)
args = [
'--config', 'config.json',
'--strategy', 'DefaultStrategy',
'hyperopt',
'--epochs', '5'
]
args = get_args(args)
StrategyResolver({'strategy': 'DefaultStrategy'})
start(args)
import pprint
pprint.pprint(caplog.record_tuples)
assert log_has(
'Starting freqtrade in Hyperopt mode',
caplog.record_tuples
)
assert start_mock.call_count == 1
def test_start_failure(mocker, default_conf, caplog) -> None:
start_mock = MagicMock()
mocker.patch(
'freqtrade.configuration.Configuration._load_config_file',
lambda *args, **kwargs: default_conf
)
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
patch_exchange(mocker)
args = [
'--config', 'config.json',
'--strategy', 'TestStrategy',
'hyperopt',
'--epochs', '5'
]
args = get_args(args)
StrategyResolver({'strategy': 'DefaultStrategy'})
with pytest.raises(ValueError):
start(args)
assert log_has(
"Please don't use --strategy for hyperopt.",
caplog.record_tuples
)
def test_loss_calculation_prefer_correct_trade_count(hyperopt) -> None:
StrategyResolver({'strategy': 'DefaultStrategy'})
correct = hyperopt.calculate_loss(1, hyperopt.target_trades, 20)
over = hyperopt.calculate_loss(1, hyperopt.target_trades + 100, 20)
under = hyperopt.calculate_loss(1, hyperopt.target_trades - 100, 20)
assert over > correct
assert under > correct
def test_loss_calculation_prefer_shorter_trades(hyperopt) -> None:
shorter = hyperopt.calculate_loss(1, 100, 20)
longer = hyperopt.calculate_loss(1, 100, 30)
assert shorter < longer
def test_loss_calculation_has_limited_profit(hyperopt) -> None:
correct = hyperopt.calculate_loss(hyperopt.expected_max_profit, hyperopt.target_trades, 20)
over = hyperopt.calculate_loss(hyperopt.expected_max_profit * 2, hyperopt.target_trades, 20)
under = hyperopt.calculate_loss(hyperopt.expected_max_profit / 2, hyperopt.target_trades, 20)
assert over == correct
assert under > correct
def test_log_results_if_loss_improves(hyperopt, capsys) -> None:
hyperopt.current_best_loss = 2
hyperopt.log_results(
{
'loss': 1,
'current_tries': 1,
'total_tries': 2,
'result': 'foo'
}
)
out, err = capsys.readouterr()
assert ' 1/2: foo. Loss 1.00000' in out
def test_no_log_if_loss_does_not_improve(hyperopt, caplog) -> None:
hyperopt.current_best_loss = 2
hyperopt.log_results(
{
'loss': 3,
}
)
assert caplog.record_tuples == []
def test_save_trials_saves_trials(mocker, hyperopt, caplog) -> None:
trials = create_trials(mocker, hyperopt)
mock_dump = mocker.patch('freqtrade.optimize.hyperopt.dump', return_value=None)
hyperopt.trials = trials
hyperopt.save_trials()
trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle')
assert log_has(
'Saving 1 evaluations to \'{}\''.format(trials_file),
caplog.record_tuples
)
mock_dump.assert_called_once()
def test_read_trials_returns_trials_file(mocker, hyperopt, caplog) -> None:
trials = create_trials(mocker, hyperopt)
mock_load = mocker.patch('freqtrade.optimize.hyperopt.load', return_value=trials)
hyperopt_trial = hyperopt.read_trials()
trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle')
assert log_has(
'Reading Trials from \'{}\''.format(trials_file),
caplog.record_tuples
)
assert hyperopt_trial == trials
mock_load.assert_called_once()
def test_roi_table_generation(hyperopt) -> None:
params = {
'roi_t1': 5,
'roi_t2': 10,
'roi_t3': 15,
'roi_p1': 1,
'roi_p2': 2,
'roi_p3': 3,
}
assert hyperopt.custom_hyperopt.generate_roi_table(params) == {0: 6, 15: 3, 25: 1, 30: 0}
def test_start_calls_optimizer(mocker, default_conf, caplog) -> None:
dumper = mocker.patch('freqtrade.optimize.hyperopt.dump', MagicMock())
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
mocker.patch('freqtrade.optimize.hyperopt.multiprocessing.cpu_count', MagicMock(return_value=1))
parallel = mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.run_optimizer_parallel',
MagicMock(return_value=[{'loss': 1, 'result': 'foo result', 'params': {}}])
)
patch_exchange(mocker)
default_conf.update({'config': 'config.json.example'})
default_conf.update({'epochs': 1})
default_conf.update({'timerange': None})
default_conf.update({'spaces': 'all'})
hyperopt = Hyperopt(default_conf)
hyperopt.strategy.tickerdata_to_dataframe = MagicMock()
hyperopt.start()
parallel.assert_called_once()
assert 'Best result:\nfoo result\nwith values:\n{}' in caplog.text
assert dumper.called
def test_format_results(hyperopt):
# Test with BTC as stake_currency
trades = [
('ETH/BTC', 2, 2, 123),
('LTC/BTC', 1, 1, 123),
('XPR/BTC', -1, -2, -246)
]
labels = ['currency', 'profit_percent', 'profit_abs', 'trade_duration']
df = pd.DataFrame.from_records(trades, columns=labels)
result = hyperopt.format_results(df)
assert result.find(' 66.67%')
assert result.find('Total profit 1.00000000 BTC')
assert result.find('2.0000Σ %')
# Test with EUR as stake_currency
trades = [
('ETH/EUR', 2, 2, 123),
('LTC/EUR', 1, 1, 123),
('XPR/EUR', -1, -2, -246)
]
df = pd.DataFrame.from_records(trades, columns=labels)
result = hyperopt.format_results(df)
assert result.find('Total profit 1.00000000 EUR')
def test_has_space(hyperopt):
hyperopt.config.update({'spaces': ['buy', 'roi']})
assert hyperopt.has_space('roi')
assert hyperopt.has_space('buy')
assert not hyperopt.has_space('stoploss')
hyperopt.config.update({'spaces': ['all']})
assert hyperopt.has_space('buy')
def test_populate_indicators(hyperopt) -> None:
tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m')
tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick)}
dataframes = hyperopt.strategy.tickerdata_to_dataframe(tickerlist)
dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'],
{'pair': 'UNITTEST/BTC'})
# Check if some indicators are generated. We will not test all of them
assert 'adx' in dataframe
assert 'mfi' in dataframe
assert 'rsi' in dataframe
def test_buy_strategy_generator(hyperopt) -> None:
tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m')
tickerlist = {'UNITTEST/BTC': parse_ticker_dataframe(tick)}
dataframes = hyperopt.strategy.tickerdata_to_dataframe(tickerlist)
dataframe = hyperopt.custom_hyperopt.populate_indicators(dataframes['UNITTEST/BTC'],
{'pair': 'UNITTEST/BTC'})
populate_buy_trend = hyperopt.custom_hyperopt.buy_strategy_generator(
{
'adx-value': 20,
'fastd-value': 20,
'mfi-value': 20,
'rsi-value': 20,
'adx-enabled': True,
'fastd-enabled': True,
'mfi-enabled': True,
'rsi-enabled': True,
'trigger': 'bb_lower'
}
)
result = populate_buy_trend(dataframe, {'pair': 'UNITTEST/BTC'})
# Check if some indicators are generated. We will not test all of them
assert 'buy' in result
assert 1 in result['buy']
def test_generate_optimizer(mocker, default_conf) -> None:
default_conf.update({'config': 'config.json.example'})
default_conf.update({'timerange': None})
default_conf.update({'spaces': 'all'})
trades = [
('POWR/BTC', 0.023117, 0.000233, 100)
]
labels = ['currency', 'profit_percent', 'profit_abs', 'trade_duration']
backtest_result = pd.DataFrame.from_records(trades, columns=labels)
mocker.patch(
'freqtrade.optimize.hyperopt.Hyperopt.backtest',
MagicMock(return_value=backtest_result)
)
mocker.patch(
'freqtrade.optimize.hyperopt.get_timeframe',
MagicMock(return_value=(datetime(2017, 12, 10), datetime(2017, 12, 13)))
)
patch_exchange(mocker)
mocker.patch('freqtrade.optimize.hyperopt.load', MagicMock())
optimizer_param = {
'adx-value': 0,
'fastd-value': 35,
'mfi-value': 0,
'rsi-value': 0,
'adx-enabled': False,
'fastd-enabled': True,
'mfi-enabled': False,
'rsi-enabled': False,
'trigger': 'macd_cross_signal',
'roi_t1': 60.0,
'roi_t2': 30.0,
'roi_t3': 20.0,
'roi_p1': 0.01,
'roi_p2': 0.01,
'roi_p3': 0.1,
'stoploss': -0.4,
}
response_expected = {
'loss': 1.9840569076926293,
'result': ' 1 trades. Avg profit 2.31%. Total profit 0.00023300 BTC '
'(0.0231Σ%). Avg duration 100.0 mins.',
'params': optimizer_param
}
hyperopt = Hyperopt(default_conf)
generate_optimizer_value = hyperopt.generate_optimizer(list(optimizer_param.values()))
assert generate_optimizer_value == response_expected
+64
View File
@@ -0,0 +1,64 @@
# pragma pylint: disable=missing-docstring, protected-access, C0103
from freqtrade import optimize, constants
from freqtrade.arguments import TimeRange
from freqtrade.data import history
from freqtrade.strategy.default_strategy import DefaultStrategy
from freqtrade.tests.conftest import log_has, patch_exchange
def test_get_timeframe(default_conf, mocker) -> None:
patch_exchange(mocker)
strategy = DefaultStrategy(default_conf)
data = strategy.tickerdata_to_dataframe(
history.load_data(
datadir=None,
ticker_interval='1m',
pairs=['UNITTEST/BTC']
)
)
min_date, max_date = optimize.get_timeframe(data)
assert min_date.isoformat() == '2017-11-04T23:02:00+00:00'
assert max_date.isoformat() == '2017-11-14T22:58:00+00:00'
def test_validate_backtest_data_warn(default_conf, mocker, caplog) -> None:
patch_exchange(mocker)
strategy = DefaultStrategy(default_conf)
data = strategy.tickerdata_to_dataframe(
history.load_data(
datadir=None,
ticker_interval='1m',
pairs=['UNITTEST/BTC']
)
)
min_date, max_date = optimize.get_timeframe(data)
caplog.clear()
assert optimize.validate_backtest_data(data, min_date, max_date,
constants.TICKER_INTERVAL_MINUTES["1m"])
assert len(caplog.record_tuples) == 1
assert log_has(
"UNITTEST/BTC has missing frames: expected 14396, got 13680, that's 716 missing values",
caplog.record_tuples)
def test_validate_backtest_data(default_conf, mocker, caplog) -> None:
patch_exchange(mocker)
strategy = DefaultStrategy(default_conf)
timerange = TimeRange('index', 'index', 200, 250)
data = strategy.tickerdata_to_dataframe(
history.load_data(
datadir=None,
ticker_interval='5m',
pairs=['UNITTEST/BTC'],
timerange=timerange
)
)
min_date, max_date = optimize.get_timeframe(data)
caplog.clear()
assert not optimize.validate_backtest_data(data, min_date, max_date,
constants.TICKER_INTERVAL_MINUTES["5m"])
assert len(caplog.record_tuples) == 0
+170
View File
@@ -0,0 +1,170 @@
# pragma pylint: disable=missing-docstring,C0103,protected-access
from unittest.mock import MagicMock
from freqtrade import OperationalException
from freqtrade.constants import AVAILABLE_PAIRLISTS
from freqtrade.resolvers import PairListResolver
from freqtrade.tests.conftest import get_patched_freqtradebot
import pytest
# whitelist, blacklist
@pytest.fixture(scope="function")
def whitelist_conf(default_conf):
default_conf['stake_currency'] = 'BTC'
default_conf['exchange']['pair_whitelist'] = [
'ETH/BTC',
'TKN/BTC',
'TRST/BTC',
'SWT/BTC',
'BCC/BTC'
]
default_conf['exchange']['pair_blacklist'] = [
'BLK/BTC'
]
default_conf['pairlist'] = {'method': 'StaticPairList',
'config': {'number_assets': 3}
}
return default_conf
def test_load_pairlist_noexist(mocker, markets, default_conf):
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets)
with pytest.raises(ImportError,
match=r"Impossible to load Pairlist 'NonexistingPairList'."
r" This class does not exist or contains Python code errors"):
PairListResolver('NonexistingPairList', freqtradebot, default_conf).pairlist
def test_refresh_market_pair_not_in_whitelist(mocker, markets, whitelist_conf):
freqtradebot = get_patched_freqtradebot(mocker, whitelist_conf)
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets)
freqtradebot.pairlists.refresh_pairlist()
# List ordered by BaseVolume
whitelist = ['ETH/BTC', 'TKN/BTC']
# Ensure all except those in whitelist are removed
assert set(whitelist) == set(freqtradebot.pairlists.whitelist)
# Ensure config dict hasn't been changed
assert (whitelist_conf['exchange']['pair_whitelist'] ==
freqtradebot.config['exchange']['pair_whitelist'])
def test_refresh_pairlists(mocker, markets, whitelist_conf):
freqtradebot = get_patched_freqtradebot(mocker, whitelist_conf)
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets)
freqtradebot.pairlists.refresh_pairlist()
# List ordered by BaseVolume
whitelist = ['ETH/BTC', 'TKN/BTC']
# Ensure all except those in whitelist are removed
assert set(whitelist) == set(freqtradebot.pairlists.whitelist)
assert whitelist_conf['exchange']['pair_blacklist'] == freqtradebot.pairlists.blacklist
def test_refresh_pairlist_dynamic(mocker, markets, tickers, whitelist_conf):
whitelist_conf['pairlist'] = {'method': 'VolumePairList',
'config': {'number_assets': 5}
}
mocker.patch.multiple(
'freqtrade.exchange.Exchange',
get_markets=markets,
get_tickers=tickers,
exchange_has=MagicMock(return_value=True)
)
freqtradebot = get_patched_freqtradebot(mocker, whitelist_conf)
# argument: use the whitelist dynamically by exchange-volume
whitelist = ['ETH/BTC', 'TKN/BTC']
freqtradebot.pairlists.refresh_pairlist()
assert whitelist == freqtradebot.pairlists.whitelist
whitelist_conf['pairlist'] = {'method': 'VolumePairList',
'config': {}
}
with pytest.raises(OperationalException,
match=r'`number_assets` not specified. Please check your configuration '
r'for "pairlist.config.number_assets"'):
PairListResolver('VolumePairList', freqtradebot, whitelist_conf).pairlist
def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
freqtradebot = get_patched_freqtradebot(mocker, whitelist_conf)
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets_empty)
# argument: use the whitelist dynamically by exchange-volume
whitelist = []
whitelist_conf['exchange']['pair_whitelist'] = []
freqtradebot.pairlists.refresh_pairlist()
pairslist = whitelist_conf['exchange']['pair_whitelist']
assert set(whitelist) == set(pairslist)
def test_VolumePairList_whitelist_gen(mocker, whitelist_conf, markets, tickers) -> None:
whitelist_conf['pairlist']['method'] = 'VolumePairList'
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
freqtrade = get_patched_freqtradebot(mocker, whitelist_conf)
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets)
mocker.patch('freqtrade.exchange.Exchange.get_tickers', tickers)
# Test to retrieved BTC sorted on quoteVolume (default)
whitelist = freqtrade.pairlists._gen_pair_whitelist(base_currency='BTC', key='quoteVolume')
assert whitelist == ['ETH/BTC', 'TKN/BTC', 'BLK/BTC', 'LTC/BTC']
# Test to retrieve BTC sorted on bidVolume
whitelist = freqtrade.pairlists._gen_pair_whitelist(base_currency='BTC', key='bidVolume')
assert whitelist == ['LTC/BTC', 'TKN/BTC', 'ETH/BTC', 'BLK/BTC']
# Test with USDT sorted on quoteVolume (default)
whitelist = freqtrade.pairlists._gen_pair_whitelist(base_currency='USDT', key='quoteVolume')
assert whitelist == ['TKN/USDT', 'ETH/USDT', 'LTC/USDT', 'BLK/USDT']
# Test with ETH (our fixture does not have ETH, so result should be empty)
whitelist = freqtrade.pairlists._gen_pair_whitelist(base_currency='ETH', key='quoteVolume')
assert whitelist == []
def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None:
default_conf['pairlist'] = {'method': 'VolumePairList',
'config': {'number_assets': 10}
}
mocker.patch('freqtrade.exchange.Exchange.get_tickers', tickers)
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=False))
with pytest.raises(OperationalException):
get_patched_freqtradebot(mocker, default_conf)
@pytest.mark.parametrize("pairlist", AVAILABLE_PAIRLISTS)
def test_pairlist_class(mocker, whitelist_conf, markets, pairlist):
whitelist_conf['pairlist']['method'] = pairlist
mocker.patch('freqtrade.exchange.Exchange.get_markets', markets)
mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True))
freqtrade = get_patched_freqtradebot(mocker, whitelist_conf)
assert freqtrade.pairlists.name == pairlist
assert pairlist in freqtrade.pairlists.short_desc()
assert isinstance(freqtrade.pairlists.whitelist, list)
assert isinstance(freqtrade.pairlists.blacklist, list)
whitelist = ['ETH/BTC', 'TKN/BTC']
new_whitelist = freqtrade.pairlists._validate_whitelist(whitelist)
assert set(whitelist) == set(new_whitelist)
whitelist = ['ETH/BTC', 'TKN/BTC', 'TRX/ETH']
new_whitelist = freqtrade.pairlists._validate_whitelist(whitelist)
# TRX/ETH was removed
assert set(['ETH/BTC', 'TKN/BTC']) == set(new_whitelist)
whitelist = ['ETH/BTC', 'TKN/BTC', 'BLK/BTC']
new_whitelist = freqtrade.pairlists._validate_whitelist(whitelist)
# BLK/BTC is in blacklist ...
assert set(['ETH/BTC', 'TKN/BTC']) == set(new_whitelist)
View File
+224
View File
@@ -0,0 +1,224 @@
# pragma pylint: disable=missing-docstring, too-many-arguments, too-many-ancestors,
# pragma pylint: disable=protected-access, C0103
import time
from unittest.mock import MagicMock
import pytest
from requests.exceptions import RequestException
from freqtrade.rpc.fiat_convert import CryptoFiat, CryptoToFiatConverter
from freqtrade.tests.conftest import log_has, patch_coinmarketcap
def test_pair_convertion_object():
pair_convertion = CryptoFiat(
crypto_symbol='btc',
fiat_symbol='usd',
price=12345.0
)
# Check the cache duration is 6 hours
assert pair_convertion.CACHE_DURATION == 6 * 60 * 60
# Check a regular usage
assert pair_convertion.crypto_symbol == 'BTC'
assert pair_convertion.fiat_symbol == 'USD'
assert pair_convertion.price == 12345.0
assert pair_convertion.is_expired() is False
# Update the expiration time (- 2 hours) and check the behavior
pair_convertion._expiration = time.time() - 2 * 60 * 60
assert pair_convertion.is_expired() is True
# Check set price behaviour
time_reference = time.time() + pair_convertion.CACHE_DURATION
pair_convertion.set_price(price=30000.123)
assert pair_convertion.is_expired() is False
assert pair_convertion._expiration >= time_reference
assert pair_convertion.price == 30000.123
def test_fiat_convert_is_supported(mocker):
patch_coinmarketcap(mocker)
fiat_convert = CryptoToFiatConverter()
assert fiat_convert._is_supported_fiat(fiat='USD') is True
assert fiat_convert._is_supported_fiat(fiat='usd') is True
assert fiat_convert._is_supported_fiat(fiat='abc') is False
assert fiat_convert._is_supported_fiat(fiat='ABC') is False
def test_fiat_convert_add_pair(mocker):
patch_coinmarketcap(mocker)
fiat_convert = CryptoToFiatConverter()
pair_len = len(fiat_convert._pairs)
assert pair_len == 0
fiat_convert._add_pair(crypto_symbol='btc', fiat_symbol='usd', price=12345.0)
pair_len = len(fiat_convert._pairs)
assert pair_len == 1
assert fiat_convert._pairs[0].crypto_symbol == 'BTC'
assert fiat_convert._pairs[0].fiat_symbol == 'USD'
assert fiat_convert._pairs[0].price == 12345.0
fiat_convert._add_pair(crypto_symbol='btc', fiat_symbol='Eur', price=13000.2)
pair_len = len(fiat_convert._pairs)
assert pair_len == 2
assert fiat_convert._pairs[1].crypto_symbol == 'BTC'
assert fiat_convert._pairs[1].fiat_symbol == 'EUR'
assert fiat_convert._pairs[1].price == 13000.2
def test_fiat_convert_find_price(mocker):
patch_coinmarketcap(mocker)
fiat_convert = CryptoToFiatConverter()
with pytest.raises(ValueError, match=r'The fiat ABC is not supported.'):
fiat_convert._find_price(crypto_symbol='BTC', fiat_symbol='ABC')
assert fiat_convert.get_price(crypto_symbol='XRP', fiat_symbol='USD') == 0.0
mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
return_value=12345.0)
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 12345.0
assert fiat_convert.get_price(crypto_symbol='btc', fiat_symbol='usd') == 12345.0
mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
return_value=13000.2)
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='EUR') == 13000.2
def test_fiat_convert_unsupported_crypto(mocker, caplog):
mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._cryptomap', return_value=[])
patch_coinmarketcap(mocker)
fiat_convert = CryptoToFiatConverter()
assert fiat_convert._find_price(crypto_symbol='CRYPTO_123', fiat_symbol='EUR') == 0.0
assert log_has('unsupported crypto-symbol CRYPTO_123 - returning 0.0', caplog.record_tuples)
def test_fiat_convert_get_price(mocker):
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price',
return_value=28000.0)
fiat_convert = CryptoToFiatConverter()
with pytest.raises(ValueError, match=r'The fiat US DOLLAR is not supported.'):
fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='US Dollar')
# Check the value return by the method
pair_len = len(fiat_convert._pairs)
assert pair_len == 0
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 28000.0
assert fiat_convert._pairs[0].crypto_symbol == 'BTC'
assert fiat_convert._pairs[0].fiat_symbol == 'USD'
assert fiat_convert._pairs[0].price == 28000.0
assert fiat_convert._pairs[0]._expiration is not 0
assert len(fiat_convert._pairs) == 1
# Verify the cached is used
fiat_convert._pairs[0].price = 9867.543
expiration = fiat_convert._pairs[0]._expiration
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 9867.543
assert fiat_convert._pairs[0]._expiration == expiration
# Verify the cache expiration
expiration = time.time() - 2 * 60 * 60
fiat_convert._pairs[0]._expiration = expiration
assert fiat_convert.get_price(crypto_symbol='BTC', fiat_symbol='USD') == 28000.0
assert fiat_convert._pairs[0]._expiration is not expiration
def test_fiat_convert_same_currencies(mocker):
patch_coinmarketcap(mocker)
fiat_convert = CryptoToFiatConverter()
assert fiat_convert.get_price(crypto_symbol='USD', fiat_symbol='USD') == 1.0
def test_fiat_convert_two_FIAT(mocker):
patch_coinmarketcap(mocker)
fiat_convert = CryptoToFiatConverter()
assert fiat_convert.get_price(crypto_symbol='USD', fiat_symbol='EUR') == 0.0
def test_loadcryptomap(mocker):
patch_coinmarketcap(mocker)
fiat_convert = CryptoToFiatConverter()
assert len(fiat_convert._cryptomap) == 2
assert fiat_convert._cryptomap["BTC"] == "1"
def test_fiat_init_network_exception(mocker):
# Because CryptoToFiatConverter is a Singleton we reset the listings
listmock = MagicMock(side_effect=RequestException)
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.Market',
listings=listmock,
)
# with pytest.raises(RequestEsxception):
fiat_convert = CryptoToFiatConverter()
fiat_convert._cryptomap = {}
fiat_convert._load_cryptomap()
length_cryptomap = len(fiat_convert._cryptomap)
assert length_cryptomap == 0
def test_fiat_convert_without_network(mocker):
# Because CryptoToFiatConverter is a Singleton we reset the value of _coinmarketcap
patch_coinmarketcap(mocker)
fiat_convert = CryptoToFiatConverter()
cmc_temp = CryptoToFiatConverter._coinmarketcap
CryptoToFiatConverter._coinmarketcap = None
assert fiat_convert._coinmarketcap is None
assert fiat_convert._find_price(crypto_symbol='BTC', fiat_symbol='USD') == 0.0
CryptoToFiatConverter._coinmarketcap = cmc_temp
def test_fiat_invalid_response(mocker, caplog):
# Because CryptoToFiatConverter is a Singleton we reset the listings
listmock = MagicMock(return_value="{'novalidjson':DEADBEEFf}")
mocker.patch.multiple(
'freqtrade.rpc.fiat_convert.Market',
listings=listmock,
)
# with pytest.raises(RequestEsxception):
fiat_convert = CryptoToFiatConverter()
fiat_convert._cryptomap = {}
fiat_convert._load_cryptomap()
length_cryptomap = len(fiat_convert._cryptomap)
assert length_cryptomap == 0
assert log_has('Could not load FIAT Cryptocurrency map for the following problem: TypeError',
caplog.record_tuples)
def test_convert_amount(mocker):
patch_coinmarketcap(mocker)
mocker.patch('freqtrade.rpc.fiat_convert.CryptoToFiatConverter.get_price', return_value=12345.0)
fiat_convert = CryptoToFiatConverter()
result = fiat_convert.convert_amount(
crypto_amount=1.23,
crypto_symbol="BTC",
fiat_symbol="USD"
)
assert result == 15184.35
result = fiat_convert.convert_amount(
crypto_amount=1.23,
crypto_symbol="BTC",
fiat_symbol="BTC"
)
assert result == 1.23

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