Compare commits

...

3523 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
527f082e59
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
gcarq
b115963a70 Merge branch 'release/0.14.2' 2017-11-16 00:40:44 +01:00
gcarq
2e953a937d version bump 2017-11-16 00:40:36 +01:00
gcarq
4e05691cab check if balance list is empty (fixes #105) 2017-11-16 00:01:47 +01:00
gcarq
b5f58724a0 get_ticker_history: check if result is set (fixes #103) 2017-11-15 23:16:54 +01:00
gcarq
b83309b55d reduce calls_per_second to 1 2017-11-15 23:16:39 +01:00
gcarq
e8101a6da5 default BaseVolume to 0.0 if null 2017-11-14 17:48:19 +01:00
gcarq
dd9cb008fb refresh whitelist based on wallet health (fixes #60)
Refreshs the whitelist in each iteration based on the wallet health,
disabled wallets will be removed from the whitelist automatically.
2017-11-13 21:34:47 +01:00
gcarq
81f7172c4a sanitize get_ticker_history (fixes #100) 2017-11-13 19:54:09 +01:00
Michael Egger
bab59fbacd Merge pull request #99 from gcarq/more_triggers2
Expanding hyperopt
2017-11-13 12:11:15 +01:00
Janne Sinivirta
0f0b10b6cc adjust search spaces 2017-11-13 07:28:56 +02:00
Janne Sinivirta
8e68c5358e clean up prints during hyperopt 2017-11-12 09:44:31 +02:00
Janne Sinivirta
660f01b514 add hilbert transform leadsine trigger 2017-11-12 09:13:54 +02:00
Janne Sinivirta
13537e3ce4 add short ema guard to hyperopt 2017-11-12 08:45:32 +02:00
Janne Sinivirta
2963a90008 add stochastics trigger 2017-11-12 08:38:52 +02:00
Janne Sinivirta
15b20b83fa optimize hyperopt objective function 2017-11-12 08:30:58 +02:00
gcarq
1c3c316e45 reduce calls_per_second 2017-11-11 21:29:35 +01:00
gcarq
517879382b Add argument for dynamic-whitelist handling
If --dynamic-whitelist is passed the whitelist in the config file
is ignored. It gets automatically refreshed every 30 minutes and
currently selects the 20 topmost BaseVolume markets
2017-11-11 19:20:53 +01:00
gcarq
bcd3340a80 implement get_market_summaries 2017-11-11 19:20:16 +01:00
gcarq
12ae1e111e use get_candles from python-bittrex 2017-11-11 17:14:55 +01:00
gcarq
d3b3370f23 Add configurable throttle mechanism 2017-11-11 16:47:19 +01:00
gcarq
8f817a3634 use TTLCache for get_ticker_history 2017-11-11 15:29:31 +01:00
Janne Sinivirta
cf79b15651 use discrete values for filters 2017-11-11 11:50:10 +02:00
Janne Sinivirta
a4284351e3 fix green_candle 2017-11-11 11:22:12 +02:00
Janne Sinivirta
906caf329b remove two unused or poorly performing indicators 2017-11-11 11:22:12 +02:00
Janne Sinivirta
3db13fae13 add green_candle guard 2017-11-11 11:22:12 +02:00
Janne Sinivirta
274972f7af make faststoch trigger use crossed_above helper 2017-11-11 11:22:11 +02:00
Janne Sinivirta
83fd27e031 add sar reversal as trigger 2017-11-11 11:22:11 +02:00
gcarq
3126dcfcea drop sleep_time and use python-bittrex request delay 2017-11-10 23:39:49 +01:00
Michael Egger
72aec6c320 Merge pull request #96 from gcarq/feature/add-argparse
add argparse and implement basic arguments
2017-11-10 18:04:03 +01:00
gcarq
b709ccbf53 enhance logging messages 2017-11-10 17:56:03 +01:00
gcarq
7e99b13742 add missing commands to README 2017-11-10 17:27:19 +01:00
gcarq
8b464033ff add missing commands to README 2017-11-10 17:26:52 +01:00
gcarq
93c525a8fa Merge branch 'master' into develop 2017-11-10 17:18:21 +01:00
gcarq
54b15c1556 update README 2017-11-10 17:17:51 +01:00
gcarq
029f32af63 Merge tag '0.14.1' into develop
0.14.1
2017-11-09 23:53:14 +01:00
gcarq
de13df6ede Merge branch 'release/0.14.1' 2017-11-09 23:53:10 +01:00
gcarq
0de211674d version bump 2017-11-09 23:52:34 +01:00
gcarq
f7a27c156c add /version command handler 2017-11-09 23:51:32 +01:00
gcarq
98f11fc7bb fix sqlite threading issue 2017-11-09 23:45:22 +01:00
gcarq
013e13e546 use tabulate for /count 2017-11-09 23:45:03 +01:00
gcarq
6ff26c561a move plot_dataframe to scripts/ folder 2017-11-09 22:29:23 +01:00
gcarq
c81358c291 remove MagicBot 2017-11-09 22:11:02 +01:00
gcarq
ed34d9f22f add tests for /forcesell all 2017-11-09 22:08:28 +01:00
gcarq
ee05561ef3 refactor forcesellall to /forcesell all 2017-11-09 22:07:51 +01:00
Eoin
69ae99406a add telegram handler for forcesellall 2017-11-09 21:52:08 +01:00
gcarq
0cfbb56b6c enhance and test pair validation 2017-11-09 21:47:47 +01:00
gcarq
8960373f1c Merge tag '0.14.0' into develop
0.14.0
2017-11-09 20:56:12 +01:00
gcarq
349a91bd92 Merge branch 'release/0.14.0' 2017-11-09 20:56:07 +01:00
gcarq
991b43b7e5 version bump 2017-11-09 20:55:45 +01:00
gcarq
a0fa6abcdc use in-memory db for dry_run 2017-11-09 20:26:52 +01:00
gcarq
86501b43c0 adjust message formatting 2017-11-09 20:25:17 +01:00
gcarq
80592970e9 add more tests 2017-11-09 20:02:41 +01:00
gcarq
567ed4ecda remove version pinning from setup.py 2017-11-09 00:33:22 +01:00
gcarq
fafbb0abfe update python-bittrex to 0.2.0 2017-11-09 00:31:53 +01:00
gcarq
0f1a36b8e9 force to python3 2017-11-08 23:39:29 +01:00
gcarq
31c03cdce1 fix linter issue 2017-11-08 22:44:32 +01:00
gcarq
e01c85bb3a add argparse and implement basic arguments 2017-11-08 22:43:47 +01:00
gcarq
a1b91ad1ea remove unneeded wrapper function 2017-11-08 21:17:51 +01:00
gcarq
6ce6018bb7 add more tests 2017-11-07 22:27:44 +01:00
gcarq
18eec0f4d4 catch BaseException in command_handler 2017-11-07 22:27:16 +01:00
gcarq
32327c45c2 set close_date on sell_order update 2017-11-07 22:26:44 +01:00
gcarq
ba485fe2b2 return state changes 2017-11-07 22:26:08 +01:00
gcarq
f8084b117e apply pylint recommendations 2017-11-07 20:13:36 +01:00
gcarq
abdddd5193 define common fixtures 2017-11-07 20:12:56 +01:00
gcarq
8eeb02e592 make ticker interval configurable 2017-11-07 18:59:47 +01:00
gcarq
8555271102 remove unneeded header from get_ticker_history 2017-11-07 18:49:16 +01:00
gcarq
d921bae75e set executable bit 2017-11-07 18:42:40 +01:00
gcarq
a1388ef296 add tick_interval to get_ticker_history as an optional parameter 2017-11-07 18:41:48 +01:00
gcarq
ddc7c94a1d Merge branch 'develop' of https://github.com/gcarq/freqtrade into develop 2017-11-07 18:40:56 +01:00
Michael Egger
e36444df27 Merge pull request #95 from gcarq/improve_backtests
Share pytest fixtures. Cache testfile loading.
2017-11-07 18:40:00 +01:00
Janne Sinivirta
0395c92260 move testdata file loading to pytest fixture 2017-11-07 19:24:51 +02:00
gcarq
f03395b90d force python3 via shebang 2017-11-07 17:54:44 +01:00
gcarq
20d5628786 catch broader RequestException instead ConnectionError 2017-11-07 17:45:13 +01:00
gcarq
57e089efd3 fix NoneType issue in status command handle 2017-11-07 17:39:57 +01:00
Janne Sinivirta
fbbde9de25 put shared fixtures to conftest.py 2017-11-07 17:29:00 +02:00
Samuel Husso
3d42b9fd75 Merge pull request #94 from gcarq/autopep
autoformat with autopep8
2017-11-06 19:41:57 +02:00
Janne Sinivirta
adfae9e75c autoformat with autopep8 2017-11-06 19:17:23 +02:00
gcarq
117dfbb563 fix wording 2017-11-06 18:15:33 +01:00
Michael Egger
e66dc8b027 Merge pull request #93 from gcarq/feature/interpreter-version-check
add interpreter version check
2017-11-06 17:23:53 +01:00
Michael Egger
ae0b49f532 Merge pull request #92 from gcarq/feature/rework-dry_run-mode
rework dry_run
2017-11-06 16:54:55 +01:00
gcarq
a37ea13fd1 catch RuntimeError earlier
This makes it possible to to restart the bot, if there are temporary
server issues.
2017-11-06 01:03:37 +01:00
gcarq
cc29126d61 make download_backtest_data.py platform independent 2017-11-06 00:16:24 +01:00
gcarq
810f2f9243 drop minimum_date from get_ticker_history 2017-11-06 00:06:59 +01:00
gcarq
60e651cb4c only return data['result'] from get_ticker_history 2017-11-05 23:47:59 +01:00
gcarq
472ce8566d enhance bittrex exception messages 2017-11-05 22:47:55 +01:00
gcarq
27ac15f298 add tabulate to setup.py 2017-11-05 20:54:41 +01:00
gcarq
d12dba16db simplify status command 2017-11-05 18:35:32 +01:00
Michael Egger
0f1d114c03 Merge pull request #86 from flightcom/feature/advanced-status-command
telegram command: advanced status
2017-11-05 18:13:25 +01:00
gcarq
3e7700e9ac add interpreter version check 2017-11-05 17:44:58 +01:00
Sébastien Moreau
60615c232c Merge branch 'develop' into feature/advanced-status-command 2017-11-05 10:34:17 -05:00
Sébastien Moreau
3884cfb809 Merge branch 'develop' into feature/advanced-status-command 2017-11-05 10:32:53 -05:00
Sebastien Moreau
caa6e22e53 Adds unit tests 2017-11-05 10:26:03 -05:00
gcarq
19f6ff330c adapt float precision asserts 2017-11-05 16:21:13 +01:00
gcarq
8fdd127f72 fix float precision rendering 2017-11-05 16:13:55 +01:00
gcarq
0a5eba64e2 do not remove order from dry_run order list 2017-11-05 16:13:20 +01:00
gcarq
b82c4444b2 apply correct typehint 2017-11-05 16:12:58 +01:00
gcarq
95a17b8f98 dry_run: remove mock value notice 2017-11-05 15:35:15 +01:00
gcarq
325f72fd91 dry_run: keep list of open orders 2017-11-05 15:21:16 +01:00
Janne Sinivirta
a237225683 Merge pull request #91 from gcarq/multiple_builds_travis
Parallel build in Travis
2017-11-05 15:21:20 +02:00
Janne Sinivirta
29b173f4e7 only run four evals of hyperopt, just to check it works 2017-11-05 09:28:42 +02:00
Janne Sinivirta
50a979161c run parallel test envs 2017-11-05 09:27:49 +02:00
gcarq
264d71e29e fix some pylint warnings 2017-11-04 18:55:41 +01:00
gcarq
a873688a44 backtesting: init Trade with Bittrex fee 2017-11-04 18:43:23 +01:00
Michael Egger
7cc8533b8e Merge pull request #89 from gcarq/feature/take-fees-into-account
take fees into account & sell amount equal to amount purchased
2017-11-03 21:47:46 +01:00
gcarq
04342acff1 fix typo 2017-11-03 21:37:20 +01:00
gcarq
c37df0e70d inform about mocked values with dry_run 2017-11-03 21:36:55 +01:00
gcarq
460dfa1031 fix percentage formating in execute_sell 2017-11-02 19:00:25 +01:00
gcarq
08a1d3ca1d pylint changes 2017-11-02 19:00:25 +01:00
gcarq
1daeed4a52 fix assert 2017-11-02 19:00:25 +01:00
gcarq
99724e2458 use Decimal for profit calculation 2017-11-02 19:00:25 +01:00
gcarq
cd18629433 add fee to sqlalchemy model and respecting it in calc_profit 2017-11-02 19:00:25 +01:00
gcarq
41510fdb32 add dry_run for get_balance 2017-11-02 19:00:25 +01:00
gcarq
9cb249610a adapt dry_run return values 2017-11-02 19:00:25 +01:00
gcarq
543857ddb2 set initial open_rate and amount in create_trade
This is mostly needed by dry_run
2017-11-02 19:00:25 +01:00
gcarq
1e5b0e8726 adapt tests 2017-11-02 19:00:25 +01:00
gcarq
0d0d822904 bump dburl to tradesv3 2017-11-02 19:00:25 +01:00
gcarq
9ff4a7b205 refactor _process to update trade state 2017-11-02 19:00:25 +01:00
gcarq
0e96197a94 don't spend the whole coin balance when selling 2017-11-02 19:00:25 +01:00
gcarq
9b9d0250f7 replace get_open_oders() with get_order() and add property for fee 2017-11-02 18:58:55 +01:00
gcarq
4a35676794 rename and exchange instance and mark it as private 2017-11-02 18:58:55 +01:00
gcarq
465c91b9a9 telegram.cleanup: fix NoneType issue when telegram is deactivated 2017-11-02 18:56:57 +01:00
Sebastien Moreau
60249af04c Removes long format + pylint fixes 2017-11-02 13:25:19 -04:00
gcarq
c3653dc417 Merge branch 'master' of https://github.com/gcarq/freqtrade into develop 2017-11-01 18:37:27 +01:00
gcarq
3d61095ba4 modify header font size 2017-11-01 18:36:22 +01:00
gcarq
7a0be94cde adapt README 2017-11-01 18:32:27 +01:00
gcarq
fad6427078 coverage: omit vendor folder 2017-11-01 01:43:15 +01:00
gcarq
4dfde7f9a2 Merge tag '0.13.0' into develop
0.13.0
2017-11-01 01:15:35 +01:00
gcarq
e2eceaa904 Merge branch 'release/0.13.0' 2017-11-01 01:15:31 +01:00
gcarq
f34af0ad67 version bump 2017-11-01 01:15:06 +01:00
gcarq
e07904d436 PEP8 linting 2017-10-31 00:36:35 +01:00
gcarq
26468bef83 balance: filter currencies with 0.0 balances 2017-10-31 00:29:22 +01:00
Michael Egger
ea1b1e11ea Merge pull request #88 from gcarq/reduce_memory_use
Reduce memory use in backtesting
2017-10-31 00:28:38 +01:00
Janne Sinivirta
e68e6c0a1a reuse mock in hyperopt also 2017-10-30 22:31:28 +02:00
Janne Sinivirta
7190226c84 reuse same mock for get_ticker_history, just change return_value 2017-10-30 22:06:09 +02:00
gcarq
6f2915e25e move qtpylib to vendor folder
This is necessary to distribute qtpylib with
freqtrade when you install it globally.
2017-10-30 20:41:36 +01:00
gcarq
6f7ac0720b add qtpylib to manifest 2017-10-30 20:24:58 +01:00
gcarq
b76554a487 add __init__ file for qtpylib 2017-10-30 20:23:19 +01:00
Janne Sinivirta
8da55c3742 move patching of arrow.utcnow to remove 500 unnecessary mock objects 2017-10-30 19:56:53 +02:00
Michael Egger
05111edd04 Merge pull request #87 from gcarq/more_triggers
More triggers and guards to hyperopt
2017-10-30 14:43:18 +01:00
Sebastien Moreau
361bdd20d3 Updates README 2017-10-29 20:55:14 -04:00
Sebastien Moreau
8bdace68f6 Adds options for /status command 2017-10-29 20:51:38 -04:00
Sebastien Moreau
0e1eb20781 Adds /count command
Adds /count command

Adds /count command
2017-10-29 18:47:42 -04:00
Michael Egger
4c2dea83c5 Merge pull request #84 from gcarq/telegram/show-balance
Telegram command: /show balance
2017-10-29 22:05:10 +01:00
Janne Sinivirta
d066817d0b removed below_sma and over_sma to wait for better implementation 2017-10-29 21:33:57 +02:00
Janne Sinivirta
a632121368 add macd cross signal trigger to hyperopt 2017-10-29 21:33:57 +02:00
Janne Sinivirta
473d09b5cd add ema50 and ema100. add long uptrend ema guard to hyperopt 2017-10-29 21:33:57 +02:00
Janne Sinivirta
893738d6f0 add MACD to analyze 2017-10-29 21:33:57 +02:00
Janne Sinivirta
22cfef7d36 add ema5 cross ema10 trigger to hyperopt 2017-10-29 21:33:57 +02:00
Janne Sinivirta
e1bbe1d9a9 adjust indicator ranges in hyperopt 2017-10-29 21:33:57 +02:00
Janne Sinivirta
ec981b415a add RSI to hyperopt 2017-10-29 21:33:57 +02:00
Janne Sinivirta
57a17697a0 add RSI, MOM, EMA5 and EMA10 to analyze 2017-10-29 21:33:57 +02:00
Samuel Husso
f4fe09ffbf added get_balances as a abstract method to the interface baseclass 2017-10-29 17:57:57 +02:00
Michael Egger
871b5e17ee Merge pull request #85 from gcarq/datetime_fixes
Performance improvements for backtesting
2017-10-29 15:56:20 +01:00
Janne Sinivirta
9b00fc3474 use .ix instead of .loc for small perf boost 2017-10-29 16:28:55 +02:00
Janne Sinivirta
3b1dc36d8a switch to using itertuples instead of iterrows as it's a lot faster 2017-10-29 16:28:55 +02:00
Janne Sinivirta
4edf8f2079 copy only needed columns before iterating over them 2017-10-29 16:28:55 +02:00
Janne Sinivirta
54987fd9ca do date parsing while loading json, not later 2017-10-29 16:28:55 +02:00
Janne Sinivirta
da9c3e7d7d remove leftover dates from removing date filtering 2017-10-29 16:28:55 +02:00
Michael Egger
a948142ef5 Merge pull request #83 from gcarq/better-hyperopt-objective
Better hyperopt objective
2017-10-29 14:13:44 +01:00
Samuel Husso
4f6c3f94e0 added tests to /balance, minor cleanup 2017-10-29 10:10:00 +02:00
Janne Sinivirta
25d6d6bbe5 remove unused imports from test_hyperopt 2017-10-28 15:32:29 +03:00
Janne Sinivirta
649781d823 store result strings, display best result in summary. switch to a lot better objective algo 2017-10-28 15:26:22 +03:00
Janne Sinivirta
08ca7a8166 change print to format so result can be used in hyperopt Trials 2017-10-28 15:26:22 +03:00
Samuel Husso
dd78c62c3d added new command to return balance across all currencies 2017-10-28 08:59:43 +03:00
Samuel Husso
29de1645fe Merge pull request #82 from gcarq/feature/handle-process-signals
handle SIGINT, SIGTERM and SIGABRT process signals
2017-10-28 08:49:42 +03:00
gcarq
4139b0b0c7 add signal handler for SIGINT, SIGTERM and SIGABRT 2017-10-27 15:52:14 +02:00
Samuel Husso
0c33e917d5 Merge pull request #79 from gcarq/qtpylib
Include new indicators from qtpylib
2017-10-27 12:11:04 +03:00
Janne Sinivirta
e401a016f5 change analyze tests to use full json dump from bittrex 2017-10-26 16:50:31 +03:00
Janne Sinivirta
e0fde8665c Merge pull request #80 from gcarq/fix-testdate-dl-path
download testdata to correct folder when running from project root
2017-10-26 10:37:38 +03:00
Samuel Husso
752520c823 When running from project root download the files to the testdata folder instead of cwd 2017-10-26 10:24:22 +03:00
Janne Sinivirta
6ba2492360 add Awesome Oscillator and try it in hyperopt 2017-10-25 18:37:20 +03:00
Janne Sinivirta
d5d798f6fa pull in new indicators from QTPYLib 2017-10-25 18:37:20 +03:00
Janne Sinivirta
9c9cf76a0d Merge pull request #78 from gcarq/refactor-backtest
Refactor backtest functionality
2017-10-25 18:19:44 +03:00
Samuel Husso
041e201713 remove duplicated backtesting from hyperopt 2017-10-25 08:17:17 +03:00
gcarq
e09505b22d Merge tag '0.12.0' into develop
0.12.0
2017-10-24 18:14:41 +02:00
gcarq
6b15cb9b10 Merge branch 'release/0.12.0' 2017-10-24 18:14:37 +02:00
gcarq
ff4fcdc760 version bump 2017-10-24 18:14:31 +02:00
Samuel Husso
f43ba44b15 refactor backtesting to its own method as we use it also in hyperopt 2017-10-24 07:58:42 +03:00
Michael Egger
79c3e0583d Merge pull request #76 from gcarq/hyperopt
Use hyperopt to find optimal parameters for buy strategy
2017-10-23 09:40:13 +02:00
Janne Sinivirta
f6ef8383bb remove filtering from analyze that is super slow and not really needed 2017-10-22 21:50:07 +03:00
Janne Sinivirta
6f5307fda7 use more aggressive stop loss for hyperopt 2017-10-22 17:15:57 +03:00
Janne Sinivirta
37004e331a remove unused import and commented out code 2017-10-22 17:14:55 +03:00
Janne Sinivirta
57acf85b42 try a different objective function 2017-10-22 17:11:01 +03:00
Michael Egger
96790d50e5 Merge pull request #77 from gcarq/help-command
Help command to Telegram bot
2017-10-21 13:51:08 +02:00
Janne Sinivirta
d32ff3410c add help command to telegram bot 2017-10-21 11:08:08 +03:00
Janne Sinivirta
35838f5e64 upgrade to latest telegram lib 2017-10-21 11:07:29 +03:00
Janne Sinivirta
913488910c bump minimum evaluations to 40 rounds 2017-10-21 10:29:05 +03:00
Janne Sinivirta
17b984a7cd adjust objective function to emphasize trade lenghts more 2017-10-21 10:28:43 +03:00
Janne Sinivirta
f79b44eefe adjust ROI map for shorter trades 2017-10-21 10:28:02 +03:00
Janne Sinivirta
146c254c0f start adding other triggers than just the lower BBands 2017-10-21 10:26:38 +03:00
Janne Sinivirta
ce2966dd7f add uptrend_sma to hyperopt 2017-10-20 18:29:38 +03:00
Janne Sinivirta
0fbca8b8ef add CCI to hyperopt 2017-10-20 13:14:28 +03:00
Janne Sinivirta
3f7a583de6 add SAR to hyperopt. add over/under sma options to hyperopt 2017-10-20 12:56:44 +03:00
Janne Sinivirta
1196983d5f change objective to emphasize shorter trades and include average profit 2017-10-20 10:39:36 +03:00
Janne Sinivirta
bbb2c7cf62 more parametrizing. adjust ranges for previous parameters 2017-10-20 10:39:04 +03:00
Janne Sinivirta
ff100bdac4 the optimizer expects values in the order of smaller is better 2017-10-19 18:29:57 +03:00
Janne Sinivirta
4feb038d0a add hyperopt dependencies 2017-10-19 17:46:41 +03:00
Janne Sinivirta
1792e0fb9b use hyperopt to find optimal parameter values for indicators 2017-10-19 17:12:49 +03:00
Janne Sinivirta
d4f8b3ebbc remove setup.cfg as it's not used but it messes with running a single test 2017-10-19 17:12:08 +03:00
Michael Egger
aeef9bac33 Merge pull request #70 from dertione/patch-2
Download automatically altcoin datas
2017-10-17 13:36:33 +02:00
Michael Egger
eff361a104 Merge pull request #73 from gcarq/small_tweaks_to_strategy
Small tweaks to strategy
2017-10-15 18:08:18 +02:00
dertione
389f9b45bb update pylint 10/10 2017-10-15 17:24:49 +02:00
Janne Sinivirta
c9741cb291 adjust roi settings for faster trades 2017-10-15 17:32:07 +03:00
Janne Sinivirta
bf6f563df2 small tweaks to buy strategy and it's visualization 2017-10-15 17:32:07 +03:00
Michael Egger
58f34d4f4b Merge pull request #71 from steerio/develop
More efficient and flexible Docker builds
2017-10-15 15:46:39 +02:00
Janne Sinivirta
2c4d0144ba Add note about binding sqlite with dry_run enabled 2017-10-15 14:40:02 +03:00
dertione
afd1a0bf9b update for pylint 2017-10-14 14:40:26 +02:00
dertione
37f6c213f6 fork test 2017-10-13 15:50:50 +02:00
Roland Venesz
76736902c6 Merge branch 'master' into develop 2017-10-13 15:48:25 +02:00
Roland Venesz
d266171ed8 Docker improvements (faster and more secure builds) 2017-10-13 15:47:13 +02:00
Michael Egger
e7df373544 Merge pull request #67 from gcarq/upgrade-deps
Upgrade dependencies
2017-10-12 09:49:45 +02:00
Michael Egger
aa4b64d0bb Merge pull request #65 from xsmile/patch-4
set exchange in analyze.__main__ to fix plotting
2017-10-12 09:42:20 +02:00
Michael Egger
4559ddd74f Merge pull request #64 from xsmile/patch-1
Bittrex provider
2017-10-12 09:37:15 +02:00
xsmile
eecc45f8ba set exchange in analyze.__main__ to fix plotting
requires #64
2017-10-11 20:04:31 +02:00
xsmile
d76476040a Bittrex provider
remove redundant 'name' property and pair validation call
2017-10-11 19:51:37 +02:00
Janne Sinivirta
0c8c149b86 Fix the command for running backtesting in README.md 2017-10-11 13:09:57 +03:00
Janne Sinivirta
60a7f8614c upgrade dependencies 2017-10-10 19:04:05 +03:00
gcarq
c31b67bf7a Merge tag '0.11.0' into develop
0.11.0
2017-10-10 17:55:10 +02:00
177 changed files with 25962 additions and 1785 deletions

View File

@@ -1,2 +1,6 @@
[run]
omit = freqtrade/tests/*
omit =
scripts/*
freqtrade/tests/*
freqtrade/vendor/*
freqtrade/__main__.py

15
.dockerignore Normal file
View File

@@ -0,0 +1,15 @@
.git
.gitignore
Dockerfile
.dockerignore
config.json*
*.sqlite
.coveragerc
.eggs
.github
.pylintrc
.travis.yml
CONTRIBUTING.md
MANIFEST.in
README.md
freqtrade.service

32
.github/ISSUE_TEMPLATE.md vendored Normal file
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
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
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.*

19
.gitignore vendored
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/

View File

@@ -1,2 +1,10 @@
[MASTER]
extension-pkg-whitelist=numpy,talib,talib.abstract
[BASIC]
good-names=logger
ignore=vendor
[TYPECHECK]
ignored-modules=numpy,talib,talib.abstract

32
.pyup.yml Normal file
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

View File

@@ -1,9 +1,15 @@
sudo: false
sudo: true
os:
- linux
dist: trusty
language: python
python:
- 3.6
services:
- docker
env:
global:
- IMAGE_NAME=freqtradeorg/freqtrade
addons:
apt:
packages:
@@ -11,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
CONTRIBUTING.md Normal file
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.

View File

@@ -1,20 +1,26 @@
FROM python:3.6.2
FROM python:3.7.0-slim-stretch
RUN apt-get update
RUN apt-get -y install build-essential
# Install TA-lib
RUN wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
RUN tar zxvf ta-lib-0.4.0-src.tar.gz
RUN cd ta-lib && ./configure && make && make install
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
COPY . /freqtrade/
WORKDIR /freqtrade
# Install dependencies and execute
RUN pip install -r requirements.txt
RUN pip install -e .
CMD ["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 numpy --no-cache-dir \
&& pip install -r requirements.txt --no-cache-dir
# Install and execute
COPY . /freqtrade/
RUN pip install -e . --no-cache-dir
ENTRYPOINT ["freqtrade"]

9
Dockerfile.develop Normal file
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
Dockerfile.technical Normal file
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

View File

@@ -1,7 +1,5 @@
include LICENSE
include README.md
include config.json.example
include freqtrade/exchange/*.py
include freqtrade/rpc/*.py
include freqtrade/tests/*.py
recursive-include freqtrade *.py
include freqtrade/tests/testdata/*.json

279
README.md
View File

@@ -1,103 +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: Lists all open trades
* /profit: Lists cumulative profit from all finished trades
* /forcesell <trade_id>: Instantly sells the given trade (Ignoring `minimum_roi`).
* /performance: Show performance of each finished trade grouped by pair
#### 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": {
"2880": 0.005, # Sell after 48 hours if there is at least 0.5% profit
"1440": 0.01, # Sell after 24 hours if there is at least 1% profit
"720": 0.02, # Sell after 12 hours if there is at least 2% profit
"360": 0.02, # Sell after 6 hours if there is at least 2% profit
"0": 0.025 # Sell immediately if there is at least 2.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
`master` branch contains the latest stable release.
```bash
usage: main.py [-h] [-v] [--version] [-c PATH] [-d PATH] [-s NAME]
[--strategy-path PATH] [--dynamic-whitelist [INT]]
[--dry-run-db]
{backtesting,hyperopt} ...
`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).
Simple High Frequency Trading Bot for crypto currencies
```
$ 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
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 (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
#### Execute tests
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)
```
$ pytest
```
This will by default skip the slow running backtest set. To run backtest set:
- `/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
```
$ BACKTEST=true pytest
```
#### Docker
```
$ cd freqtrade
$ docker build -t freqtrade .
$ docker run --rm -it freqtrade
```
## Development branches
#### Contributing
The project is currently setup in two main branches:
Feel like our bot is missing a feature? We welcome your pull requests! Few pointers for contributions:
- `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.
## A note on Binance
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.
## Support
### Help / Slack
For any questions not covered by the documentation or for further
information about the bot, we encourage you to join our slack channel.
- [Click here to join Slack channel](https://join.slack.com/t/highfrequencybot/shared_invite/enQtMjQ5NTM0OTYzMzY3LWMxYzE3M2MxNDdjMGM3ZTYwNzFjMGIwZGRjNTc3ZGU3MGE3NzdmZGMwNmU3NDM5ZTNmM2Y3NjRiNzk4NmM4OGE).
### [Bugs / Issues](https://github.com/freqtrade/freqtrade/issues?q=is%3Aissue)
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.
### [Feature Requests](https://github.com/freqtrade/freqtrade/labels/enhancement)
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.

View File

@@ -1,4 +1,7 @@
#!/usr/bin/env python
#!/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
build_helpers/install_ta-lib.sh Executable file
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
build_helpers/publish_docker.sh Executable file
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.

View File

@@ -2,37 +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": {
"60": 0.0,
"40": 0.01,
"20": 0.02,
"0": 0.03
"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"
}
"initial_state": "running",
"forcebuy_enable": false,
"internals": {
"process_throttle_secs": 5
}
}

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
config_full.json.example Normal file
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
docs/backtesting.md Normal file
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
docs/bot-optimization.md Normal file
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
docs/bot-usage.md Normal file
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
docs/configuration.md Normal file
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
docs/developer.md Normal file
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
docs/edge.md Normal file
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
docs/faq.md Normal file
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
docs/hyperopt.md Normal file
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
docs/index.md Normal file
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
docs/installation.md Normal file
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
docs/plotting.md Normal file
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
docs/pre-requisite.md Normal file
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
docs/sandbox-testing.md Normal file
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
docs/sql_cheatsheet.md Normal file
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
docs/stoploss.md Normal file
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
docs/telegram-usage.md Normal file
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
docs/webhook-config.md Normal file
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
freqtrade.service Normal file
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

View File

@@ -1,3 +1,25 @@
__version__ = '0.11.0'
""" 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
freqtrade/__main__.py Normal file
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:])

View File

@@ -1,150 +0,0 @@
import logging
import time
from datetime import timedelta
import arrow
import talib.abstract as ta
from pandas import DataFrame
from freqtrade.exchange import get_ticker_history
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
def parse_ticker_dataframe(ticker: list, minimum_date: arrow.Arrow) -> DataFrame:
"""
Analyses the trend for the given pair
:param pair: pair as str in format BTC_ETH or BTC-ETH
:return: DataFrame
"""
df = DataFrame(ticker) \
.drop('BV', 1) \
.rename(columns={'C':'close', 'V':'volume', 'O':'open', 'H':'high', 'L':'low', 'T':'date'}) \
.sort_values('date')
return df[df['date'].map(arrow.get) > minimum_date]
def populate_indicators(dataframe: DataFrame) -> DataFrame:
"""
Adds several different TA indicators to the given DataFrame
"""
dataframe['sar'] = ta.SAR(dataframe, 0.02, 0.22)
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['cci'] = ta.CCI(dataframe, timeperiod=5)
dataframe['sma'] = ta.SMA(dataframe, timeperiod=100)
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=4)
dataframe['mfi'] = ta.MFI(dataframe)
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.loc[
(dataframe['close'] < dataframe['sma']) &
(dataframe['cci'] < -100) &
(dataframe['tema'] <= dataframe['blower']) &
(dataframe['mfi'] < 30) &
(dataframe['fastd'] < 20) &
(dataframe['adx'] > 20),
'buy'] = 1
dataframe.loc[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
"""
minimum_date = arrow.utcnow().shift(hours=-24)
data = get_ticker_history(pair, minimum_date)
dataframe = parse_ticker_dataframe(data['result'], minimum_date)
if dataframe.empty:
logger.warning('Empty dataframe for pair %s', pair)
return dataframe
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
def plot_dataframe(dataframe: DataFrame, pair: str) -> None:
"""
Plots the given dataframe
:param dataframe: DataFrame
:param pair: pair as str
:return: None
"""
import matplotlib
matplotlib.use("Qt5Agg")
import matplotlib.pyplot as plt
# Two subplots sharing x axis
fig, (ax1, ax2) = plt.subplots(2, sharex=True)
fig.suptitle(pair, fontsize=14, fontweight='bold')
ax1.plot(dataframe.index.values, dataframe['sar'], 'g_', label='pSAR')
ax1.plot(dataframe.index.values, dataframe['close'], label='close')
# ax1.plot(dataframe.index.values, dataframe['sell'], 'ro', label='sell')
ax1.plot(dataframe.index.values, dataframe['sma'], '--', label='SMA')
ax1.plot(dataframe.index.values, dataframe['buy_price'], 'bo', label='buy')
ax1.legend()
# ax2.plot(dataframe.index.values, dataframe['adx'], label='ADX')
ax2.plot(dataframe.index.values, dataframe['mfi'], label='MFI')
# ax2.plot(dataframe.index.values, [25] * len(dataframe.index.values))
ax2.legend()
# Fine-tune figure; make subplots close to each other and hide x ticks for
# all but bottom plot.
fig.subplots_adjust(hspace=0)
plt.setp([a.get_xticklabels() for a in fig.axes[:-1]], visible=False)
plt.show()
if __name__ == '__main__':
# Install PYQT5==5.9 manually if you want to test this helper function
while True:
test_pair = 'BTC_ETH'
# for pair in ['BTC_ANT', 'BTC_ETH', 'BTC_GNT', 'BTC_ETC']:
# get_buy_signal(pair)
plot_dataframe(analyze_ticker(test_pair), test_pair)
time.sleep(60)

423
freqtrade/arguments.py Normal file
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
freqtrade/configuration.py Normal file
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
freqtrade/constants.py Normal file
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'
]
}

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'
]

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
freqtrade/data/history.py Normal file
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
freqtrade/edge/__init__.py Normal file
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)
)

View File

@@ -1,115 +1,713 @@
import enum
# pragma pylint: disable=W0603
""" Cryptocurrency Exchanges support """
import logging
from typing import List
import inspect
from random import randint
from typing import List, Dict, Tuple, Any, Optional
from datetime import datetime
from math import floor, ceil
import arrow
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
EXCHANGE: Exchange = None
_CONF: dict = {}
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, EXCHANGE
_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))
EXCHANGE = 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 = EXCHANGE.get_markets()
for pair in pairs:
if pair not in markets:
raise RuntimeError('Pair {} is not available at {}'.format(pair, EXCHANGE.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']:
return 'dry_run'
class Exchange(object):
return EXCHANGE.buy(pair, rate, amount)
_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)
def sell(pair: str, rate: float, amount: float) -> str:
if _CONF['dry_run']:
return 'dry_run'
self._cached_ticker: Dict[str, Any] = {}
return EXCHANGE.sell(pair, rate, amount)
# Holds last candle refreshed time of each pair
self._pairs_last_refresh_time: Dict[str, int] = {}
# Holds candles
self._klines: Dict[str, DataFrame] = {}
def get_balance(currency: str) -> float:
if _CONF['dry_run']:
return 999.9
# Holds all open sell orders for dry_run
self._dry_run_open_orders: Dict[str, Any] = {}
return EXCHANGE.get_balance(currency)
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'))
def get_ticker(pair: str) -> dict:
return EXCHANGE.get_ticker(pair)
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 get_ticker_history(pair: str, minimum_date: arrow.Arrow):
return EXCHANGE.get_ticker_history(pair, minimum_date)
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']
def cancel_order(order_id: str) -> None:
if _CONF['dry_run']:
return
if name not in ccxt_module.exchanges:
raise OperationalException(f'Exchange {name} is not supported')
return EXCHANGE.cancel_order(order_id)
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)
}
if ccxt_kwargs:
logger.info('Applying additional ccxt config: %s', ccxt_kwargs)
ex_config.update(ccxt_kwargs)
try:
api = getattr(ccxt_module, name.lower())(ex_config)
except (KeyError, AttributeError):
raise OperationalException(f'Exchange {name} is not supported')
def get_open_orders(pair: str) -> List[dict]:
if _CONF['dry_run']:
return []
self.set_sandbox(api, exchange_config, name)
return EXCHANGE.get_open_orders(pair)
return api
@property
def name(self) -> str:
"""exchange Name (from ccxt)"""
return self._api.name
def get_pair_detail_url(pair: str) -> str:
return EXCHANGE.get_pair_detail_url(pair)
@property
def id(self) -> str:
"""exchange ccxt id"""
return self._api.id
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
def get_markets() -> List[str]:
return EXCHANGE.get_markets()
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())
except ccxt.BaseError as e:
logger.warning('Could not load async markets. Reason: %s', e)
return
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
"""
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.')
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.')
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
)
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 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
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}
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})
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}
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})
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.
"""
# 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')
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)

View File

@@ -1,120 +0,0 @@
import logging
from typing import List, Optional
import arrow
import requests
from bittrex.bittrex import Bittrex as _Bittrex
from freqtrade.exchange.interface import Exchange
logger = logging.getLogger(__name__)
_API: _Bittrex = None
_EXCHANGE_CONF: dict = {}
class Bittrex(Exchange):
"""
Bittrex API wrapper.
"""
# Base URL and API endpoints
BASE_URL: str = 'https://www.bittrex.com'
TICKER_METHOD: str = BASE_URL + '/Api/v2.0/pub/market/GetTicks'
PAIR_DETAIL_METHOD: str = BASE_URL + '/Market/Index'
# Ticker inveral
TICKER_INTERVAL: str = 'fiveMin'
# Sleep time to avoid rate limits, used in the main loop
SLEEP_TIME: float = 25
@property
def name(self) -> str:
return self.__class__.__name__
@property
def sleep_time(self) -> float:
return self.SLEEP_TIME
def __init__(self, config: dict) -> None:
global _API, _EXCHANGE_CONF
_EXCHANGE_CONF.update(config)
_API = _Bittrex(api_key=_EXCHANGE_CONF['key'], api_secret=_EXCHANGE_CONF['secret'])
# Check if all pairs are available
markets = self.get_markets()
exchange_name = self.name
for pair in _EXCHANGE_CONF['pair_whitelist']:
if pair not in markets:
raise RuntimeError('Pair {} is not available at {}'.format(pair, exchange_name))
def buy(self, pair: str, rate: float, amount: float) -> str:
data = _API.buy_limit(pair.replace('_', '-'), amount, rate)
if not data['success']:
raise RuntimeError('{}: {}'.format(self.name.upper(), data['message']))
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('{}: {}'.format(self.name.upper(), data['message']))
return data['result']['uuid']
def get_balance(self, currency: str) -> float:
data = _API.get_balance(currency)
if not data['success']:
raise RuntimeError('{}: {}'.format(self.name.upper(), data['message']))
return float(data['result']['Balance'] or 0.0)
def get_ticker(self, pair: str) -> dict:
data = _API.get_ticker(pair.replace('_', '-'))
if not data['success']:
raise RuntimeError('{}: {}'.format(self.name.upper(), data['message']))
return {
'bid': float(data['result']['Bid']),
'ask': float(data['result']['Ask']),
'last': float(data['result']['Last']),
}
def get_ticker_history(self, pair: str, minimum_date: Optional[arrow.Arrow] = None):
url = self.TICKER_METHOD
headers = {
# TODO: Set as global setting
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'
}
params = {
'marketName': pair.replace('_', '-'),
'tickInterval': self.TICKER_INTERVAL,
# TODO: Timestamp has no effect on API response
'_': minimum_date.timestamp * 1000
}
data = requests.get(url, params=params, headers=headers).json()
if not data['success']:
raise RuntimeError('{}: {}'.format(self.name.upper(), data['message']))
return data
def cancel_order(self, order_id: str) -> None:
data = _API.cancel(order_id)
if not data['success']:
raise RuntimeError('{}: {}'.format(self.name.upper(), data['message']))
def get_open_orders(self, pair: str) -> List[dict]:
data = _API.get_open_orders(pair.replace('_', '-'))
if not data['success']:
raise RuntimeError('{}: {}'.format(self.name.upper(), data['message']))
return [{
'id': entry['OrderUuid'],
'type': entry['OrderType'],
'opened': entry['Opened'],
'rate': entry['PricePerUnit'],
'amount': entry['Quantity'],
'remaining': entry['QuantityRemaining'],
} for entry in data['result']]
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('{}: {}'.format(self.name.upper(), data['message']))
return [m['MarketName'].replace('-', '_') for m in data['result']]

View File

@@ -1,127 +0,0 @@
from abc import ABC, abstractmethod
from typing import List, Optional
import arrow
class Exchange(ABC):
@property
def name(self) -> str:
"""
Name of the exchange.
:return: str representation of the class name
"""
return self.__class__.__name__
@property
@abstractmethod
def sleep_time(self) -> float:
"""
Sleep time in seconds for the main loop to avoid API rate limits.
:return: 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_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, minimum_date: Optional[arrow.Arrow] = None) -> dict:
"""
Gets ticker history for given pair.
:param pair: Pair as str, format: BTC_ETC
:param minimum_date: Minimum date (optional)
:return: dict, format: {
'success': bool,
'message': str,
'result': [
{
'O': float, (Open)
'H': float, (High)
'L': float, (Low)
'C': float, (Close)
'V': float, (Volume)
'T': datetime, (Time)
'BV': float, (Base Volume)
},
...
]
}
"""
@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_open_orders(self, pair: str) -> List[dict]:
"""
Gets all open orders for given pair.
:param pair: Pair as str, format: BTC_ETC
:return: List of dicts, format: [
{
'id': str,
'type': str,
'opened': datetime,
'rate': float,
'amount': float,
'remaining': int,
},
...
]
"""
@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
"""

832
freqtrade/freqtradebot.py Normal file
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()

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)

View File

@@ -1,273 +1,89 @@
#!/usr/bin/env python
import copy
import json
#!/usr/bin/env python3
"""
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 typing import Dict, Optional
import sys
from argparse import Namespace
from typing import List
from jsonschema import validate
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
from freqtrade import __version__, exchange, persistence
from freqtrade.analyze import get_buy_signal
from freqtrade.misc import CONF_SCHEMA, State, get_state, update_state
from freqtrade.persistence import Trade
from freqtrade.rpc import telegram
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
_CONF = {}
logger = logging.getLogger('freqtrade')
def _process() -> None:
def main(sysargv: List[str]) -> None:
"""
Queries the persistence layer for open trades and handles them,
otherwise a new trade is created.
This function will initiate the bot and start the trading loop.
:return: None
"""
try:
# 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)
else:
logging.info('Got no buy signal...')
except ValueError:
logger.exception('Unable to create trade')
for trade in trades:
# Check if there is already an open order for this trade
orders = exchange.get_open_orders(trade.pair)
orders = [o for o in orders if o['id'] == trade.open_order_id]
if orders:
logger.info('There is an open order for: %s', orders[0])
else:
# Update state
trade.open_order_id = None
# Check if this trade can be closed
if not close_trade_if_fulfilled(trade):
# Check if we can sell our current pair
handle_trade(trade)
Trade.session.flush()
except (ConnectionError, json.JSONDecodeError) as error:
msg = 'Got {} in _process()'.format(error.__class__.__name__)
logger.exception(msg)
def close_trade_if_fulfilled(trade: Trade) -> bool:
"""
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
"""
# 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('No open orders found and trade is fulfilled. Marking %s as closed ...', trade)
return True
return False
def execute_sell(trade: Trade, current_rate: float) -> None:
"""
Executes a sell for the given trade and current rate
:param trade: Trade instance
:param current_rate: current rate
:return: None
"""
# Get available balance
currency = trade.pair.split('_')[1]
balance = exchange.get_balance(currency)
profit = trade.exec_sell_order(current_rate, balance)
message = '*{}:* Selling [{}]({}) at rate `{:f} (profit: {}%)`'.format(
trade.exchange,
trade.pair.replace('_', '/'),
exchange.get_pair_detail_url(trade.pair),
trade.close_rate,
round(profit, 2)
arguments = Arguments(
sysargv,
'Simple High Frequency Trading Bot for crypto currencies'
)
logger.info(message)
telegram.send_msg(message)
args = arguments.get_parsed_arg()
# 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 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 = (current_rate - trade.open_rate) / trade.open_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()):
duration, threshold = float(duration), float(threshold)
# Check if time matches and current rate is above threshold
time_diff = (current_time - trade.open_date).total_seconds() / 60
if time_diff > 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) -> None:
"""
Sells the current pair if the threshold is reached and updates the trade record.
:return: None
"""
freqtrade = None
return_code = 1
try:
if not trade.is_open:
raise ValueError('attempt to handle closed trade: {}'.format(trade))
# Load and validate configuration
config = Configuration(args).get_config()
logger.debug('Handling open trade %s ...', trade)
# Init the bot
freqtrade = FreqtradeBot(config)
current_rate = exchange.get_ticker(trade.pair)['bid']
if should_sell(trade, current_rate, datetime.utcnow()):
execute_sell(trade, current_rate)
return
state = None
while 1:
state = freqtrade.worker(old_state=state)
if state == State.RELOAD_CONF:
freqtrade = reconfigure(freqtrade, args)
except ValueError:
logger.exception('Unable to handle open order')
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('Creating 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
open_rate = get_target_bid(exchange.get_ticker(pair))
amount = stake_amount / open_rate
order_id = exchange.buy(pair, open_rate, amount)
# Create trade entity and return
message = '*{}:* Buying [{}]({}) at rate `{:f}`'.format(
exchange.EXCHANGE.name.upper(),
pair.replace('_', '/'),
exchange.get_pair_detail_url(pair),
open_rate
)
logger.info(message)
telegram.send_msg(message)
return Trade(pair=pair,
stake_amount=stake_amount,
open_rate=open_rate,
open_date=datetime.utcnow(),
amount=amount,
exchange=exchange.EXCHANGE.name.upper(),
open_order_id=order_id,
is_open=True)
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)
def app(config: dict) -> None:
"""
Main loop which handles the application state
:param config: config as dict
:return: None
"""
logger.info('Starting freqtrade %s', __version__)
init(config)
try:
old_state = get_state()
logger.info('Initial State: %s', old_state)
telegram.send_msg('*Status:* `{}`'.format(old_state.name.lower()))
while True:
new_state = get_state()
# Log state transition
if new_state != old_state:
telegram.send_msg('*Status:* `{}`'.format(new_state.name.lower()))
logging.info('Changing state to: %s', new_state.name)
if new_state == State.STOPPED:
time.sleep(1)
elif new_state == State.RUNNING:
_process()
# We need to sleep here because otherwise we would run into bittrex rate limit
time.sleep(exchange.EXCHANGE.sleep_time)
old_state = new_state
except RuntimeError:
telegram.send_msg('*Status:* Got RuntimeError: ```\n{}\n```'.format(traceback.format_exc()))
logger.exception('RuntimeError. Trader stopped!')
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:
telegram.send_msg('*Status:* `Trader has stopped`')
if freqtrade:
freqtrade.rpc.send_msg({
'type': RPCMessageType.STATUS_NOTIFICATION,
'status': 'process died'
})
freqtrade.cleanup()
sys.exit(return_code)
def main():
def reconfigure(freqtrade: FreqtradeBot, args: Namespace) -> FreqtradeBot:
"""
Loads and validates the config and starts the main loop
:return: None
Cleans up current instance, reloads the configuration and returns the new instance
"""
global _CONF
with open('config.json') as file:
_CONF = json.load(file)
validate(_CONF, CONF_SCHEMA)
app(_CONF)
# Clean up current modules
freqtrade.cleanup()
# 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:])

View File

@@ -1,103 +1,91 @@
import enum
"""
Various tool function for Freqtrade and scripts
"""
from wrapt import synchronized
import gzip
import json
import logging
import re
from datetime import datetime
from typing import Dict
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)
# 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']},
},
'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'
]
}
def format_ms_time(date: int) -> str:
"""
convert MS date to readable format.
: epoch-string in ms
"""
return datetime.fromtimestamp(date/1000.0).strftime('%Y-%m-%dT%H:%M:%S')

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

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()

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'),
]

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()

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()

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
"""

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]

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'])

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

View File

@@ -1,87 +1,367 @@
from datetime import datetime
from typing import Optional
"""
This module contains the class to persist trades into SQLite
"""
from sqlalchemy import Boolean, Column, DateTime, Float, Integer, String, create_engine
import logging
from datetime import datetime
from decimal import Decimal
from typing import Any, Dict, Optional
import arrow
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.types import Enum
from sqlalchemy import func
from sqlalchemy.pool import StaticPool
from freqtrade import exchange
from freqtrade import OperationalException
_CONF = {}
logger = logging.getLogger(__name__)
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, db_url: Optional[str] = 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 db_url: database connector string for sqlalchemy (Optional)
:return: None
"""
_CONF.update(config)
if not db_url:
if _CONF.get('dry_run', False):
db_url = 'sqlite:///tradesv2.dry_run.sqlite'
else:
db_url = 'sqlite:///tradesv2.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})')
engine = create_engine(db_url, echo=False)
session = scoped_session(sessionmaker(bind=engine, autoflush=True, autocommit=True))
Trade.session = session()
Trade.query = session.query_property()
Base.metadata.create_all(engine)
_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()
class Trade(Base):
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:
"""
Flushes all pending operations to disk.
:return: 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)
open_rate = Column(Float, nullable=False)
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, name='btc_amount', nullable=False)
amount = Column(Float, nullable=False)
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):
if self.is_open:
open_since = '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:
open_since = round((datetime.utcnow() - self.open_date).total_seconds() / 60, 2)
return 'Trade(id={}, pair={}, amount={}, open_rate={}, open_since={})'.format(
self.id,
self.pair,
self.amount,
self.open_rate,
open_since
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:
"""
Updates this entity with amount and actual open/close rates.
:param order: order retrieved by exchange.get_order()
:return: None
"""
order_type = order['type']
# Ignore open and cancelled orders
if order['status'] == 'open' or order['price'] is None:
return
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 = 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(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 exec_sell_order(self, rate: float, amount: float) -> float:
def calc_open_trade_price(
self,
fee: Optional[float] = None) -> float:
"""
Executes a sell for the given trade and updated the entity.
:param rate: rate to sell for
:param amount: amount to sell
:return: current profit as percentage
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
"""
profit = 100 * ((rate - self.open_rate) / self.open_rate)
# Execute sell and update trade record
order_id = exchange.sell(str(self.pair), rate, amount)
self.close_rate = rate
self.close_profit = profit
self.close_date = datetime.utcnow()
self.open_order_id = order_id
buy_trade = (Decimal(self.amount) * Decimal(self.open_rate))
fees = buy_trade * Decimal(fee or self.fee_open)
return float(buy_trade + fees)
# Flush changes
Trade.session.flush()
return profit
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
"""
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

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

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)
)

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

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)
)

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)
)

View File

@@ -1 +1,2 @@
from . import telegram
from .rpc import RPC, RPCMessageType, RPCException # noqa
from .rpc_manager import RPCManager # noqa

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
freqtrade/rpc/rpc.py Normal file
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

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()}'
})

View File

@@ -1,326 +1,541 @@
import logging
from datetime import timedelta
from typing import Callable, Any
# 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
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 = 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 _CONF['telegram']['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('start', _start),
CommandHandler('stop', _stop),
CommandHandler('forcesell', _forcesell),
CommandHandler('performance', _performance),
]
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 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]
if not isinstance(bot, Bot) or not isinstance(update, Update):
raise ValueError('Received invalid Arguments: {}'.format(*args))
# Reject unauthorized messages
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
)
return wrapper
logger.info(
'Executing handler: %s for chat_id: %s',
command_handler.__name__,
chat_id
)
try:
return command_handler(self, *args, **kwargs)
except BaseException:
logger.exception('Exception occurred within Telegram module')
chat_id = int(_CONF['telegram']['chat_id'])
if int(update.message.chat_id) == chat_id:
logger.info('Executing handler: %s for chat_id: %s', command_handler.__name__, chat_id)
return command_handler(*args, **kwargs)
else:
logger.info('Rejected unauthorized message from: %s', update.message.chat_id)
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
"""
# 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:
for trade in trades:
# calculate profit and send message to user
current_rate = exchange.get_ticker(trade.pair)['bid']
current_profit = 100 * ((current_rate - trade.open_rate) / trade.open_rate)
orders = exchange.get_open_orders(trade.pair)
orders = [o for o in orders if o['id'] == trade.open_order_id]
order = orders[0] if orders else None
class Telegram(RPC):
""" This class handles all telegram communication """
fmt_close_profit = '{:.2f}%'.format(
round(trade.close_profit, 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}`
*Close Rate:* `{close_rate}`
*Current Rate:* `{current_rate}`
*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, 2),
open_order='{} ({})'.format(order['remaining'], order['type']) if order else None,
)
send_msg(message, bot=bot)
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)
self._updater: Updater = None
self._config = freqtrade.config
self._init()
if self._config.get('fiat_display_currency', None):
self._fiat_converter = CryptoToFiatConverter()
@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()
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)
profit_amounts = []
profits = []
durations = []
for trade in trades:
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 = 100 * ((current_rate - trade.open_rate) / trade.open_rate)
profit_amounts.append((profit / 100) * 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:.2f} ({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), 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, 2),
)
send_msg(markdown_msg, bot=bot)
@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
try:
trade_id = int(update.message.text
.replace('/forcesell', '')
.strip())
# Query for trade
trade = Trade.query.filter(and_(
Trade.id == trade_id,
Trade.is_open.is_(True)
)).first()
if not trade:
send_msg('There is no open trade with ID: `{}`'.format(trade_id))
return
# Get current rate
current_rate = exchange.get_ticker(trade.pair)['bid']
# Get available balance
currency = trade.pair.split('_')[1]
balance = exchange.get_balance(currency)
# Execute sell
profit = trade.exec_sell_order(current_rate, balance)
message = '*{}:* Selling [{}]({}) at rate `{:f} (profit: {}%)`'.format(
trade.exchange,
trade.pair.replace('_', '/'),
exchange.get_pair_detail_url(trade.pair),
trade.close_rate,
round(profit, 2)
# 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,
)
logger.info(
'rpc.telegram is listening for following commands: %s',
[h.command for h in handles]
)
logger.info(message)
send_msg(message)
except ValueError:
send_msg('Invalid argument. Usage: `/forcesell <trade_id>`')
logger.warning('/forcesell: Invalid argument received')
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 """
@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
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
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()
message = ("*{exchange}:* Buying [{pair}]({market_url})\n"
"with limit `{limit:.8f}\n"
"({stake_amount:.6f} {stake_currency}").format(**msg)
stats = '\n'.join('{index}. <code>{pair}\t{profit:.2f}%</code>'.format(
index=i + 1,
pair=pair,
profit=round(rate, 2)
) for i, (pair, rate) in enumerate(pair_rates))
if msg.get('fiat_currency', None):
message += ",{stake_amount_fiat:.3f} {fiat_currency}".format(**msg)
message += ")`"
message = '<b>Performance:</b>\n{}\n'.format(stats)
logger.debug(message)
send_msg(message, parse_mode=ParseMode.HTML)
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)
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 _CONF['telegram'].get('enabled', False):
try:
bot = bot or _updater.bot
try:
bot.send_message(_CONF['telegram']['chat_id'], msg, parse_mode=parse_mode)
except NetworkError as error:
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(
'Got Telegram NetworkError: %s! Trying one more time.',
error.message
'Telegram NetworkError: %s! Trying one more time.',
network_err.message
)
bot.send_message(_CONF['telegram']['chat_id'], msg, parse_mode=parse_mode)
except Exception:
logger.exception('Exception occurred within Telegram API')
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
freqtrade/rpc/webhook.py Normal file
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
freqtrade/state.py Normal file
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

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)

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

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)

843
freqtrade/tests/conftest.py Normal file
View File

@@ -0,0 +1,843 @@
# pragma pylint: disable=missing-docstring
import json
import logging
from datetime import datetime
from functools import reduce
from typing import Dict, Optional
from unittest.mock import MagicMock, PropertyMock
import arrow
import pytest
from telegram import Chat, Message, Update
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)
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.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
},
"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": 1
},
"exchange": {
"name": "bittrex",
"enabled": True,
"key": "key",
"secret": "secret",
"pair_whitelist": [
"ETH/BTC",
"LTC/BTC",
"XRP/BTC",
"NEO/BTC"
]
},
"telegram": {
"enabled": True,
"token": "token",
"chat_id": "0"
},
"initial_state": "running",
"db_url": "sqlite://",
"loglevel": logging.DEBUG,
}
return configuration
@pytest.fixture
def update():
_update = Update(0)
_update.message = Message(0, 0, datetime.utcnow(), Chat(0, 0))
return _update
@pytest.fixture
def fee():
return MagicMock(return_value=0.0025)
@pytest.fixture
def ticker():
return MagicMock(return_value={
'bid': 0.00001098,
'ask': 0.00001099,
'last': 0.00001098,
})
@pytest.fixture
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',
'side': 'buy',
'pair': 'mocked',
'datetime': arrow.utcnow().isoformat(),
'price': 0.00001099,
'amount': 90.99181073,
'remaining': 0.0,
'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'
}
@pytest.fixture
def limit_sell_order():
return {
'id': 'mocked_limit_sell',
'type': 'limit',
'side': 'sell',
'pair': 'mocked',
'datetime': arrow.utcnow().isoformat(),
'price': 0.00001173,
'amount': 90.99181073,
'remaining': 0.0,
'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

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)

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

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

View File

File diff suppressed because it is too large Load Diff

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

View File

@@ -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)

View File

@@ -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)

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

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

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

View File

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

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